feat(simulation): add endpoint handshake protocol (#37157)
This commit is contained in:
parent
f92d84746b
commit
5e2e0d6965
8 changed files with 350 additions and 2 deletions
|
|
@ -19,6 +19,30 @@ test("scopes the frontend control server and reports malformed JSON", async () =
|
|||
Queue.offerUnsafe(messages, JSON.parse(String(event.data)))
|
||||
})
|
||||
|
||||
socket.send(
|
||||
JSON.stringify({
|
||||
jsonrpc: "2.0",
|
||||
id: 0,
|
||||
method: "simulation.handshake",
|
||||
params: {
|
||||
client: { name: "test", version: "test" },
|
||||
expectedRole: "ui",
|
||||
offeredVersions: [1],
|
||||
requiredCapabilities: ["ui.state"],
|
||||
optionalCapabilities: [],
|
||||
},
|
||||
}),
|
||||
)
|
||||
expect(yield* Queue.take(messages)).toMatchObject({
|
||||
id: 0,
|
||||
result: {
|
||||
protocolVersion: 1,
|
||||
role: "ui",
|
||||
server: { name: "opencode", version: expect.any(String) },
|
||||
capabilities: expect.arrayContaining(["ui.state", "ui.capture"]),
|
||||
},
|
||||
})
|
||||
|
||||
socket.send(JSON.stringify({ jsonrpc: "2.0", id: 1, method: "ui.state" }))
|
||||
expect(yield* Queue.take(messages)).toMatchObject({
|
||||
id: 1,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import { expect, test } from "bun:test"
|
||||
import { Frontend } from "../src/protocol"
|
||||
import { describe, expect, test } from "bun:test"
|
||||
import { Effect } from "effect"
|
||||
import { Backend, Frontend, Handshake } from "../src/protocol"
|
||||
|
||||
test("decodes ui.matches text params", () => {
|
||||
expect(
|
||||
|
|
@ -19,3 +20,104 @@ test("decodes ui.matches text params", () => {
|
|||
}),
|
||||
).toThrow()
|
||||
})
|
||||
|
||||
const params: Handshake.Params = {
|
||||
client: { name: "opencode-drive", version: "test" },
|
||||
expectedRole: "ui",
|
||||
offeredVersions: [1],
|
||||
requiredCapabilities: ["ui.state"],
|
||||
optionalCapabilities: ["ui.capture", "future.capability"],
|
||||
}
|
||||
|
||||
const ui: Handshake.DispatchAction = {
|
||||
role: "ui",
|
||||
server: { name: "opencode", version: "test" },
|
||||
capabilities: Frontend.Capabilities,
|
||||
}
|
||||
|
||||
describe("simulation.handshake", () => {
|
||||
test("decodes through both endpoint request protocols", () => {
|
||||
const request = {
|
||||
jsonrpc: "2.0" as const,
|
||||
id: 1,
|
||||
method: "simulation.handshake" as const,
|
||||
params,
|
||||
}
|
||||
expect(Frontend.decodeRequest(request)).toEqual(request)
|
||||
expect(Backend.decodeRequest({ ...request, params: { ...params, expectedRole: "backend" } })).toMatchObject({
|
||||
method: "simulation.handshake",
|
||||
params: { expectedRole: "backend" },
|
||||
})
|
||||
})
|
||||
|
||||
test("rejects invalid version and capability declarations", () => {
|
||||
const request = {
|
||||
jsonrpc: "2.0" as const,
|
||||
id: 1,
|
||||
method: "simulation.handshake" as const,
|
||||
params,
|
||||
}
|
||||
expect(() =>
|
||||
Frontend.decodeRequest({
|
||||
...request,
|
||||
params: { ...params, offeredVersions: [] },
|
||||
}),
|
||||
).toThrow()
|
||||
expect(() =>
|
||||
Frontend.decodeRequest({
|
||||
...request,
|
||||
params: { ...params, requiredCapabilities: ["ui.state", "ui.state"] },
|
||||
}),
|
||||
).toThrow()
|
||||
expect(() =>
|
||||
Frontend.decodeRequest({
|
||||
...request,
|
||||
params: { ...params, optionalCapabilities: [""] },
|
||||
}),
|
||||
).toThrow()
|
||||
})
|
||||
|
||||
test("selects the protocol and advertises only installed capabilities", async () => {
|
||||
await expect(Effect.runPromise(Handshake.dispatch(ui, params))).resolves.toEqual({
|
||||
protocolVersion: 1,
|
||||
role: "ui",
|
||||
server: { name: "opencode", version: "test" },
|
||||
capabilities: [...Frontend.Capabilities],
|
||||
})
|
||||
})
|
||||
|
||||
test("rejects a role mismatch", async () => {
|
||||
await expect(
|
||||
Effect.runPromise(Handshake.dispatch(ui, { ...params, expectedRole: "backend" })),
|
||||
).rejects.toMatchObject({ _tag: "SimulationHandshake.RoleMismatchError", expected: "backend", actual: "ui" })
|
||||
})
|
||||
|
||||
test("rejects unsupported protocol versions", async () => {
|
||||
await expect(Effect.runPromise(Handshake.dispatch(ui, { ...params, offeredVersions: [2] }))).rejects.toMatchObject({
|
||||
_tag: "SimulationHandshake.UnsupportedProtocolError",
|
||||
offered: [2],
|
||||
supported: [1],
|
||||
})
|
||||
})
|
||||
|
||||
test("rejects a missing required capability but ignores missing optional capabilities", async () => {
|
||||
await expect(
|
||||
Effect.runPromise(
|
||||
Handshake.dispatch(ui, {
|
||||
...params,
|
||||
requiredCapabilities: ["ui.state", "ui.future"],
|
||||
}),
|
||||
),
|
||||
).rejects.toMatchObject({ _tag: "SimulationHandshake.MissingCapabilityError", missing: ["ui.future"] })
|
||||
|
||||
await expect(
|
||||
Effect.runPromise(
|
||||
Handshake.dispatch(ui, {
|
||||
...params,
|
||||
requiredCapabilities: [],
|
||||
optionalCapabilities: ["ui.future"],
|
||||
}),
|
||||
),
|
||||
).resolves.toMatchObject({ capabilities: Frontend.Capabilities })
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -7,6 +7,30 @@ import { availableEndpoint, connect } from "./fixture/websocket"
|
|||
test("streams a Drive-controlled provider response and removes the finished invocation", async () => {
|
||||
await runProvider((provider, socket, messages) =>
|
||||
Effect.gen(function* () {
|
||||
socket.send(
|
||||
JSON.stringify({
|
||||
jsonrpc: "2.0",
|
||||
id: 0,
|
||||
method: "simulation.handshake",
|
||||
params: {
|
||||
client: { name: "test", version: "test" },
|
||||
expectedRole: "backend",
|
||||
offeredVersions: [1],
|
||||
requiredCapabilities: ["llm.attach", "llm.request"],
|
||||
optionalCapabilities: [],
|
||||
},
|
||||
}),
|
||||
)
|
||||
expect(yield* Queue.take(messages)).toMatchObject({
|
||||
id: 0,
|
||||
result: {
|
||||
protocolVersion: 1,
|
||||
role: "backend",
|
||||
server: { name: "opencode", version: expect.any(String) },
|
||||
capabilities: expect.arrayContaining(["llm.attach", "llm.request"]),
|
||||
},
|
||||
})
|
||||
|
||||
socket.send("{")
|
||||
expect(yield* Queue.take(messages)).toMatchObject({ id: null, error: { code: -32000 } })
|
||||
yield* attach(socket, messages)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue