feat(server): durable log reads, changes feed, and watermarked snapshots (#34962)

This commit is contained in:
Kit Langton 2026-07-02 16:42:30 -04:00 committed by GitHub
commit bc2e270f82
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
28 changed files with 1402 additions and 1605 deletions

View file

@ -4,6 +4,8 @@ export * from "./generated-effect/index"
export { Agent } from "@opencode-ai/schema/agent"
export { Command } from "@opencode-ai/schema/command"
export { Credential } from "@opencode-ai/schema/credential"
export { Event } from "@opencode-ai/schema/event"
export { EventLog } from "@opencode-ai/schema/event-log"
export { FileSystem } from "@opencode-ai/schema/filesystem"
export { Integration } from "@opencode-ai/schema/integration"
export { Location } from "@opencode-ai/schema/location"

View file

@ -80,10 +80,7 @@ const Endpoint4_1 = (raw: RawClient["server.session"]) => (input?: Endpoint4_1In
)
const Endpoint4_2 = (raw: RawClient["server.session"]) => () =>
raw["session.active"]({}).pipe(
Effect.mapError(mapClientError),
Effect.map((value) => value.data),
)
raw["session.active"]({}).pipe(Effect.mapError(mapClientError))
type Endpoint4_3Request = Parameters<RawClient["server.session"]["session.get"]>[0]
type Endpoint4_3Input = { readonly sessionID: Endpoint4_3Request["params"]["sessionID"] }
@ -282,47 +279,39 @@ const Endpoint4_20 = (raw: RawClient["server.session"]) => (input: Endpoint4_20I
Effect.mapError(mapClientError),
)
type Endpoint4_21Request = Parameters<RawClient["server.session"]["session.history"]>[0]
type Endpoint4_21Request = Parameters<RawClient["server.session"]["session.log"]>[0]
type Endpoint4_21Input = {
readonly sessionID: Endpoint4_21Request["params"]["sessionID"]
readonly limit?: Endpoint4_21Request["query"]["limit"]
readonly after?: Endpoint4_21Request["query"]["after"]
readonly follow?: Endpoint4_21Request["query"]["follow"]
}
const Endpoint4_21 = (raw: RawClient["server.session"]) => (input: Endpoint4_21Input) =>
raw["session.history"]({
params: { sessionID: input["sessionID"] },
query: { limit: input["limit"], after: input["after"] },
}).pipe(Effect.mapError(mapClientError))
type Endpoint4_22Request = Parameters<RawClient["server.session"]["session.events"]>[0]
type Endpoint4_22Input = {
readonly sessionID: Endpoint4_22Request["params"]["sessionID"]
readonly after?: Endpoint4_22Request["query"]["after"]
}
const Endpoint4_22 = (raw: RawClient["server.session"]) => (input: Endpoint4_22Input) =>
Stream.unwrap(
raw["session.events"]({ params: { sessionID: input["sessionID"] }, query: { after: input["after"] } }).pipe(
raw["session.log"]({
params: { sessionID: input["sessionID"] },
query: { after: input["after"], follow: input["follow"] },
}).pipe(
Effect.mapError(mapClientError),
Effect.map((stream) => stream.pipe(Stream.mapError(mapClientError))),
),
)
type Endpoint4_23Request = Parameters<RawClient["server.session"]["session.interrupt"]>[0]
type Endpoint4_23Input = { readonly sessionID: Endpoint4_23Request["params"]["sessionID"] }
const Endpoint4_23 = (raw: RawClient["server.session"]) => (input: Endpoint4_23Input) =>
type Endpoint4_22Request = Parameters<RawClient["server.session"]["session.interrupt"]>[0]
type Endpoint4_22Input = { readonly sessionID: Endpoint4_22Request["params"]["sessionID"] }
const Endpoint4_22 = (raw: RawClient["server.session"]) => (input: Endpoint4_22Input) =>
raw["session.interrupt"]({ params: { sessionID: input["sessionID"] } }).pipe(Effect.mapError(mapClientError))
type Endpoint4_24Request = Parameters<RawClient["server.session"]["session.background"]>[0]
type Endpoint4_24Input = { readonly sessionID: Endpoint4_24Request["params"]["sessionID"] }
const Endpoint4_24 = (raw: RawClient["server.session"]) => (input: Endpoint4_24Input) =>
type Endpoint4_23Request = Parameters<RawClient["server.session"]["session.background"]>[0]
type Endpoint4_23Input = { readonly sessionID: Endpoint4_23Request["params"]["sessionID"] }
const Endpoint4_23 = (raw: RawClient["server.session"]) => (input: Endpoint4_23Input) =>
raw["session.background"]({ params: { sessionID: input["sessionID"] } }).pipe(Effect.mapError(mapClientError))
type Endpoint4_25Request = Parameters<RawClient["server.session"]["session.message"]>[0]
type Endpoint4_25Input = {
readonly sessionID: Endpoint4_25Request["params"]["sessionID"]
readonly messageID: Endpoint4_25Request["params"]["messageID"]
type Endpoint4_24Request = Parameters<RawClient["server.session"]["session.message"]>[0]
type Endpoint4_24Input = {
readonly sessionID: Endpoint4_24Request["params"]["sessionID"]
readonly messageID: Endpoint4_24Request["params"]["messageID"]
}
const Endpoint4_25 = (raw: RawClient["server.session"]) => (input: Endpoint4_25Input) =>
const Endpoint4_24 = (raw: RawClient["server.session"]) => (input: Endpoint4_24Input) =>
raw["session.message"]({ params: { sessionID: input["sessionID"], messageID: input["messageID"] } }).pipe(
Effect.mapError(mapClientError),
Effect.map((value) => value.data),
@ -350,11 +339,10 @@ const adaptGroup4 = (raw: RawClient["server.session"]) => ({
listContextEntries: Endpoint4_18(raw),
putContextEntry: Endpoint4_19(raw),
removeContextEntry: Endpoint4_20(raw),
history: Endpoint4_21(raw),
events: Endpoint4_22(raw),
interrupt: Endpoint4_23(raw),
background: Endpoint4_24(raw),
message: Endpoint4_25(raw),
log: Endpoint4_21(raw),
interrupt: Endpoint4_22(raw),
background: Endpoint4_23(raw),
message: Endpoint4_24(raw),
})
type Endpoint5_0Request = Parameters<RawClient["server.message"]["session.messages"]>[0]
@ -696,7 +684,15 @@ const Endpoint17_0 = (raw: RawClient["server.event"]) => () =>
),
)
const adaptGroup17 = (raw: RawClient["server.event"]) => ({ subscribe: Endpoint17_0(raw) })
const Endpoint17_1 = (raw: RawClient["server.event"]) => () =>
Stream.unwrap(
raw["event.changes"]({}).pipe(
Effect.mapError(mapClientError),
Effect.map((stream) => stream.pipe(Stream.mapError(mapClientError))),
),
)
const adaptGroup17 = (raw: RawClient["server.event"]) => ({ subscribe: Endpoint17_0(raw), changes: Endpoint17_1(raw) })
type Endpoint18_0Request = Parameters<RawClient["server.pty"]["pty.list"]>[0]
type Endpoint18_0Input = { readonly location?: Endpoint18_0Request["query"]["location"] }

View file

@ -47,10 +47,8 @@ import type {
SessionPutContextEntryOutput,
SessionRemoveContextEntryInput,
SessionRemoveContextEntryOutput,
SessionHistoryInput,
SessionHistoryOutput,
SessionEventsInput,
SessionEventsOutput,
SessionLogInput,
SessionLogOutput,
SessionInterruptInput,
SessionInterruptOutput,
SessionBackgroundInput,
@ -118,6 +116,7 @@ import type {
SkillListInput,
SkillListOutput,
EventSubscribeOutput,
EventChangesOutput,
PtyListInput,
PtyListOutput,
PtyCreateInput,
@ -380,7 +379,7 @@ export function make(options: ClientOptions) {
requestOptions,
).then((value) => value.data),
active: (requestOptions?: RequestOptions) =>
request<{ readonly data: SessionActiveOutput }>(
request<SessionActiveOutput>(
{
method: "GET",
path: `/api/session/active`,
@ -389,7 +388,7 @@ export function make(options: ClientOptions) {
empty: false,
},
requestOptions,
).then((value) => value.data),
),
get: (input: SessionGetInput, requestOptions?: RequestOptions) =>
request<{ readonly data: SessionGetOutput }>(
{
@ -608,24 +607,12 @@ export function make(options: ClientOptions) {
},
requestOptions,
),
history: (input: SessionHistoryInput, requestOptions?: RequestOptions) =>
request<SessionHistoryOutput>(
log: (input: SessionLogInput, requestOptions?: RequestOptions): AsyncIterable<SessionLogOutput> =>
sse<SessionLogOutput>(
{
method: "GET",
path: `/api/session/${encodeURIComponent(input.sessionID)}/history`,
query: { limit: input["limit"], after: input["after"] },
successStatus: 200,
declaredStatuses: [404, 400, 401],
empty: false,
},
requestOptions,
),
events: (input: SessionEventsInput, requestOptions?: RequestOptions): AsyncIterable<SessionEventsOutput> =>
sse<SessionEventsOutput>(
{
method: "GET",
path: `/api/session/${encodeURIComponent(input.sessionID)}/event`,
query: { after: input["after"] },
path: `/api/session/${encodeURIComponent(input.sessionID)}/log`,
query: { after: input["after"], follow: input["follow"] },
successStatus: 200,
declaredStatuses: [404, 400, 401],
empty: false,
@ -1067,6 +1054,11 @@ 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) =>

File diff suppressed because it is too large Load diff