feat(simulation): share control protocol schemas (#35230)

This commit is contained in:
James Long 2026-07-03 15:46:04 -04:00 committed by GitHub
commit 37b26e495b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 170 additions and 148 deletions

View file

@ -0,0 +1,145 @@
import { Effect, Schema } from "effect"
const JsonRpcID = Schema.Union([Schema.String, Schema.Number, Schema.Null])
type Json = Schema.Schema.Type<typeof Schema.Json>
export namespace JsonRpc {
export const Request = Schema.Struct({
jsonrpc: Schema.Literal("2.0"),
id: Schema.optional(JsonRpcID),
method: Schema.String,
params: Schema.optional(Schema.Json),
})
export interface Request extends Schema.Schema.Type<typeof Request> {}
export const ErrorObject = Schema.Struct({
code: Schema.Number,
message: Schema.String,
data: Schema.optional(Schema.Json),
})
export const Response = Schema.Struct({
jsonrpc: Schema.Literal("2.0"),
id: JsonRpcID,
result: Schema.optional(Schema.Json),
error: Schema.optional(ErrorObject),
})
export interface Response extends Schema.Schema.Type<typeof Response> {}
export const decodeRequest = Schema.decodeUnknownSync(Request)
export function success(id: Request["id"], result: unknown): Response | undefined {
if (id === undefined) return undefined
return { jsonrpc: "2.0", id, result: result as Json }
}
export function failure(id: Request["id"], error: unknown): Response {
return {
jsonrpc: "2.0",
id: id ?? null,
error: {
code: -32000,
message: error instanceof Error ? error.message : String(error),
},
}
}
}
export namespace Frontend {
export const KeyModifiers = Schema.Struct({
ctrl: Schema.optional(Schema.Boolean),
shift: Schema.optional(Schema.Boolean),
meta: Schema.optional(Schema.Boolean),
super: Schema.optional(Schema.Boolean),
hyper: Schema.optional(Schema.Boolean),
})
export interface KeyModifiers extends Schema.Schema.Type<typeof KeyModifiers> {}
export const Action = Schema.Union([
Schema.Struct({ type: Schema.Literal("typeText"), text: Schema.String }),
Schema.Struct({ type: Schema.Literal("pressKey"), key: Schema.String, modifiers: Schema.optional(KeyModifiers) }),
Schema.Struct({ type: Schema.Literal("pressEnter") }),
Schema.Struct({ type: Schema.Literal("pressArrow"), direction: Schema.Literals(["up", "down", "left", "right"]) }),
Schema.Struct({ type: Schema.Literal("focus"), target: Schema.Number }),
Schema.Struct({ type: Schema.Literal("click"), target: Schema.Number, x: Schema.Number, y: Schema.Number }),
])
export type Action = Schema.Schema.Type<typeof Action>
export const Element = Schema.Struct({
id: Schema.String,
num: Schema.Number,
x: Schema.Number,
y: Schema.Number,
width: Schema.Number,
height: Schema.Number,
focusable: Schema.Boolean,
focused: Schema.Boolean,
clickable: Schema.Boolean,
editor: Schema.Boolean,
})
export interface Element extends Schema.Schema.Type<typeof Element> {}
export const State = Schema.Struct({
screen: Schema.String,
focused: Schema.Struct({
renderable: Schema.optional(Schema.Number),
editor: Schema.Boolean,
}),
elements: Schema.Array(Element),
actions: Schema.Array(Action),
})
export interface State extends Schema.Schema.Type<typeof State> {}
export const ActionParams = Schema.Struct({ action: Action })
export interface ActionParams extends Schema.Schema.Type<typeof ActionParams> {}
export const decodeActionParams = Schema.decodeUnknownSync(ActionParams)
export const TraceRecord = Schema.Struct({
id: Schema.Number,
time: Schema.String,
type: Schema.String,
data: Schema.optional(Schema.Json),
})
export interface TraceRecord extends Schema.Schema.Type<typeof TraceRecord> {}
export const TraceList = Schema.Struct({ records: Schema.Array(TraceRecord) })
export interface TraceList extends Schema.Schema.Type<typeof TraceList> {}
}
export namespace Backend {
export const Item = Schema.Union([
Schema.Struct({ type: Schema.Literal("textDelta"), text: Schema.String }),
Schema.Struct({ type: Schema.Literal("reasoningDelta"), text: Schema.String }),
Schema.Struct({ type: Schema.Literal("toolCall"), id: Schema.String, name: Schema.String, input: Schema.Json }),
Schema.Struct({ type: Schema.Literal("raw"), chunk: Schema.Json }),
])
export type Item = Schema.Schema.Type<typeof Item>
export const FinishReason = Schema.Literals(["stop", "tool-calls", "length", "content-filter"])
export type FinishReason = Schema.Schema.Type<typeof FinishReason>
export const ChunkParams = Schema.Struct({ id: Schema.String, items: Schema.Array(Item) })
export interface ChunkParams extends Schema.Schema.Type<typeof ChunkParams> {}
export const FinishParams = Schema.Struct({
id: Schema.String,
reason: FinishReason.pipe(Schema.withDecodingDefault(Effect.succeed("stop" as const))),
})
export interface FinishParams extends Schema.Schema.Type<typeof FinishParams> {}
export const OpenedExchange = Schema.Struct({ id: Schema.String, url: Schema.String, body: Schema.Json })
export interface OpenedExchange extends Schema.Schema.Type<typeof OpenedExchange> {}
export const NetworkLogEntry = Schema.Struct({
time: Schema.Number,
method: Schema.String,
url: Schema.String,
matched: Schema.Boolean,
})
export interface NetworkLogEntry extends Schema.Schema.Type<typeof NetworkLogEntry> {}
export const decodeChunkParams = Schema.decodeUnknownPromise(ChunkParams)
export const decodeFinishParams = Schema.decodeUnknownPromise(FinishParams)
}
export * as SimulationProtocol from "./index"