refactor(api): remove watermark sync surface

This commit is contained in:
Dax Raad 2026-07-06 12:59:26 -04:00
commit ff499c3603
25 changed files with 141 additions and 392 deletions

View file

@ -66,7 +66,7 @@ export type Endpoint4_1Input = {
export type Endpoint4_1Output = EffectValue<ReturnType<RawClient["server.session"]["session.create"]>>["data"]
export type SessionCreateOperation<E = never> = (input?: Endpoint4_1Input) => Effect.Effect<Endpoint4_1Output, E>
export type Endpoint4_2Output = EffectValue<ReturnType<RawClient["server.session"]["session.active"]>>
export type Endpoint4_2Output = EffectValue<ReturnType<RawClient["server.session"]["session.active"]>>["data"]
export type SessionActiveOperation<E = never> = () => Effect.Effect<Endpoint4_2Output, E>
type Endpoint4_3Request = Parameters<RawClient["server.session"]["session.get"]>[0]
@ -658,12 +658,8 @@ export interface SkillApi<E = never> {
export type Endpoint18_0Output = StreamValue<EffectValue<ReturnType<RawClient["server.event"]["event.subscribe"]>>>
export type EventSubscribeOperation<E = never> = () => Stream.Stream<Endpoint18_0Output, E>
export type Endpoint18_1Output = StreamValue<EffectValue<ReturnType<RawClient["server.event"]["event.changes"]>>>
export type EventChangesOperation<E = never> = () => Stream.Stream<Endpoint18_1Output, E>
export interface EventApi<E = never> {
readonly subscribe: EventSubscribeOperation<E>
readonly changes: EventChangesOperation<E>
}
type Endpoint19_0Request = Parameters<RawClient["server.pty"]["pty.list"]>[0]

View file

@ -82,7 +82,10 @@ const Endpoint4_1 = (raw: RawClient["server.session"]) => (input?: Endpoint4_1In
)
const Endpoint4_2 = (raw: RawClient["server.session"]) => () =>
raw["session.active"]({}).pipe(Effect.mapError(mapClientError))
raw["session.active"]({}).pipe(
Effect.mapError(mapClientError),
Effect.map((value) => value.data),
)
type Endpoint4_3Request = Parameters<RawClient["server.session"]["session.get"]>[0]
type Endpoint4_3Input = { readonly sessionID: Endpoint4_3Request["params"]["sessionID"] }
@ -796,15 +799,7 @@ const Endpoint18_0 = (raw: RawClient["server.event"]) => () =>
),
)
const Endpoint18_1 = (raw: RawClient["server.event"]) => () =>
Stream.unwrap(
raw["event.changes"]({}).pipe(
Effect.mapError(mapClientError),
Effect.map((stream) => stream.pipe(Stream.mapError(mapClientError))),
),
)
const adaptGroup18 = (raw: RawClient["server.event"]) => ({ subscribe: Endpoint18_0(raw), changes: Endpoint18_1(raw) })
const adaptGroup18 = (raw: RawClient["server.event"]) => ({ subscribe: Endpoint18_0(raw) })
type Endpoint19_0Request = Parameters<RawClient["server.pty"]["pty.list"]>[0]
type Endpoint19_0Input = { readonly location?: Endpoint19_0Request["query"]["location"] }

View file

@ -133,7 +133,6 @@ import type {
SkillListInput,
SkillListOutput,
EventSubscribeOutput,
EventChangesOutput,
PtyListInput,
PtyListOutput,
PtyCreateInput,
@ -402,7 +401,7 @@ export function make(options: ClientOptions) {
requestOptions,
).then((value) => value.data),
active: (requestOptions?: RequestOptions) =>
request<SessionActiveOutput>(
request<{ readonly data: SessionActiveOutput }>(
{
method: "GET",
path: `/api/session/active`,
@ -411,7 +410,7 @@ export function make(options: ClientOptions) {
empty: false,
},
requestOptions,
),
).then((value) => value.data),
get: (input: SessionGetInput, requestOptions?: RequestOptions) =>
request<{ readonly data: SessionGetOutput }>(
{
@ -646,7 +645,7 @@ export function make(options: ClientOptions) {
sse<SessionLogOutput>(
{
method: "GET",
path: `/api/session/${encodeURIComponent(input.sessionID)}/log`,
path: `/api/experimental/session/${encodeURIComponent(input.sessionID)}/log`,
query: { after: input["after"], follow: input["follow"] },
successStatus: 200,
declaredStatuses: [404, 400, 401],
@ -1183,11 +1182,6 @@ export function make(options: ClientOptions) {
{ method: "GET", path: `/api/event`, successStatus: 200, declaredStatuses: [401, 400], empty: false },
requestOptions,
),
changes: (requestOptions?: RequestOptions): AsyncIterable<EventChangesOutput> =>
sse<EventChangesOutput>(
{ method: "GET", path: `/api/event/changes`, successStatus: 200, declaredStatuses: [401, 400], empty: false },
requestOptions,
),
},
pty: {
list: (input?: PtyListInput, requestOptions?: RequestOptions) =>

View file

@ -354,7 +354,6 @@ export type SessionListOutput = {
}>
}
}>
readonly watermarks: { readonly [x: string]: number }
readonly cursor: { readonly previous?: string | null; readonly next?: string | null }
}
@ -419,10 +418,7 @@ export type SessionCreateOutput = {
}
}["data"]
export type SessionActiveOutput = {
readonly data: { readonly [x: string]: { readonly type: "running" } }
readonly watermarks: { readonly [x: string]: number }
}
export type SessionActiveOutput = { readonly data: { readonly [x: string]: { readonly type: "running" } } }["data"]
export type SessionGetInput = { readonly sessionID: { readonly sessionID: string }["sessionID"] }
@ -1987,7 +1983,6 @@ export type MessageListOutput = {
readonly time: { readonly created: number }
}
>
readonly watermark?: number
readonly cursor: { readonly previous?: string | null; readonly next?: string | null }
}
@ -5546,10 +5541,6 @@ export type EventSubscribeOutput =
readonly data: {}
}
export type EventChangesOutput =
| { readonly type: "log.hint"; readonly aggregateID: string; readonly seq: number }
| { readonly type: "log.sweep_required" }
export type PtyListInput = {
readonly location?: {
readonly location?: { readonly directory?: string | undefined; readonly workspace?: string | undefined } | undefined

View file

@ -99,7 +99,7 @@ test("session methods retain decoded Effect inputs and outputs", async () => {
return Effect.succeed(
HttpClientResponse.fromWeb(
request,
Response.json({ data: { ses_test: { type: "running" } }, watermarks: { ses_test: 3 } }),
Response.json({ data: { ses_test: { type: "running" } } }),
),
)
}
@ -112,7 +112,7 @@ test("session methods retain decoded Effect inputs and outputs", async () => {
return Effect.succeed(
HttpClientResponse.fromWeb(
request,
Response.json({ data: [session.data], watermarks: { ses_test: 3 }, cursor: { next: "next" } }),
Response.json({ data: [session.data], cursor: { next: "next" } }),
),
)
})
@ -148,8 +148,7 @@ test("session methods retain decoded Effect inputs and outputs", async () => {
}).pipe(Effect.provideService(HttpClient.HttpClient, httpClient), Effect.runPromise)
expect(DateTime.toEpochMillis(result.page.data[0].time.created)).toBe(1_717_171_717_000)
expect(result.active).toEqual({ data: { ses_test: { type: "running" } }, watermarks: { ses_test: 3 } })
expect(result.page.watermarks).toEqual({ ses_test: 3 })
expect(result.active).toEqual({ ses_test: { type: "running" } })
expect(Object.getPrototypeOf(result.page.data[0])).toBe(Object.prototype)
expect(Object.getPrototypeOf(result.created)).toBe(Object.prototype)
expect(result.created.id).toBe("ses_test")

View file

@ -47,7 +47,7 @@ test("exposes every standard HTTP API group", () => {
expect(Object.keys(client.vcs)).toEqual(["status", "diff"])
expect(Object.keys(client.pty)).toEqual(["list", "create", "get", "update", "remove"])
expect(Object.keys(client.shell)).toEqual(["list", "create", "get", "output", "remove"])
expect(Object.keys(client.project)).toEqual(["current", "directories"])
expect(Object.keys(client.project)).toEqual(["list", "current", "directories"])
})
test("file.read returns binary content from the public HTTP contract", async () => {
@ -198,7 +198,7 @@ test("session methods use the public HTTP contract", async () => {
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" } }, watermarks: { ses_test: 3 } })
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" } })
@ -227,7 +227,7 @@ test("session methods use the public HTTP contract", async () => {
const message = await client.session.message({ sessionID: "ses_test", messageID: "msg_model" })
expect(page.cursor.next).toBe("next")
expect(active).toEqual({ data: { ses_test: { type: "running" } }, watermarks: { ses_test: 3 } })
expect(active).toEqual({ ses_test: { type: "running" } })
expect(created.id).toBe("ses_test")
expect(admitted.id).toBe("msg_test")
expect(context).toEqual([])
@ -243,7 +243,7 @@ test("session methods use the public HTTP contract", async () => {
["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/log?after=0"],
["GET", "http://localhost:3000/api/experimental/session/ses_test/log?after=0"],
["POST", "http://localhost:3000/api/session/ses_test/interrupt"],
["GET", "http://localhost:3000/api/session/ses_test/message/msg_model"],
])