feat(core): sync routes, refactor proxy, session restore, and more syncing (#22518)

This commit is contained in:
James Long 2026-04-15 10:18:48 -04:00 committed by GitHub
commit af20191d1c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 1132 additions and 63 deletions

View file

@ -35,6 +35,8 @@ import type {
ExperimentalWorkspaceListResponses,
ExperimentalWorkspaceRemoveErrors,
ExperimentalWorkspaceRemoveResponses,
ExperimentalWorkspaceSessionRestoreErrors,
ExperimentalWorkspaceSessionRestoreResponses,
ExperimentalWorkspaceStatusResponses,
FileListResponses,
FilePartInput,
@ -157,6 +159,10 @@ import type {
SessionUpdateErrors,
SessionUpdateResponses,
SubtaskPartInput,
SyncHistoryListErrors,
SyncHistoryListResponses,
SyncReplayErrors,
SyncReplayResponses,
TextPartInput,
ToolIdsErrors,
ToolIdsResponses,
@ -1243,6 +1249,49 @@ export class Workspace extends HeyApiClient {
})
}
/**
* Restore session into workspace
*
* Replay a session's sync events into the target workspace in batches.
*/
public sessionRestore<ThrowOnError extends boolean = false>(
parameters: {
id: string
directory?: string
workspace?: string
sessionID?: string
},
options?: Options<never, ThrowOnError>,
) {
const params = buildClientParams(
[parameters],
[
{
args: [
{ in: "path", key: "id" },
{ in: "query", key: "directory" },
{ in: "query", key: "workspace" },
{ in: "body", key: "sessionID" },
],
},
],
)
return (options?.client ?? this.client).post<
ExperimentalWorkspaceSessionRestoreResponses,
ExperimentalWorkspaceSessionRestoreErrors,
ThrowOnError
>({
url: "/experimental/workspace/{id}/session-restore",
...options,
...params,
headers: {
"Content-Type": "application/json",
...options?.headers,
...params.headers,
},
})
}
private _adaptor?: Adaptor
get adaptor(): Adaptor {
return (this._adaptor ??= new Adaptor({ client: this.client }))
@ -2961,6 +3010,109 @@ export class Provider extends HeyApiClient {
}
}
export class History extends HeyApiClient {
/**
* List sync events
*
* List sync events for all aggregates. Keys are aggregate IDs the client already knows about, values are the last known sequence ID. Events with seq > value are returned for those aggregates. Aggregates not listed in the input get their full history.
*/
public list<ThrowOnError extends boolean = false>(
parameters?: {
directory?: string
workspace?: string
body?: {
[key: string]: number
}
},
options?: Options<never, ThrowOnError>,
) {
const params = buildClientParams(
[parameters],
[
{
args: [
{ in: "query", key: "directory" },
{ in: "query", key: "workspace" },
{ key: "body", map: "body" },
],
},
],
)
return (options?.client ?? this.client).get<SyncHistoryListResponses, SyncHistoryListErrors, ThrowOnError>({
url: "/sync/history",
...options,
...params,
headers: {
"Content-Type": "application/json",
...options?.headers,
...params.headers,
},
})
}
}
export class Sync extends HeyApiClient {
/**
* Replay sync events
*
* Validate and replay a complete sync event history.
*/
public replay<ThrowOnError extends boolean = false>(
parameters?: {
query_directory?: string
workspace?: string
body_directory?: string
events?: Array<{
id: string
aggregateID: string
seq: number
type: string
data: {
[key: string]: unknown
}
}>
},
options?: Options<never, ThrowOnError>,
) {
const params = buildClientParams(
[parameters],
[
{
args: [
{
in: "query",
key: "query_directory",
map: "directory",
},
{ in: "query", key: "workspace" },
{
in: "body",
key: "body_directory",
map: "directory",
},
{ in: "body", key: "events" },
],
},
],
)
return (options?.client ?? this.client).post<SyncReplayResponses, SyncReplayErrors, ThrowOnError>({
url: "/sync/replay",
...options,
...params,
headers: {
"Content-Type": "application/json",
...options?.headers,
...params.headers,
},
})
}
private _history?: History
get history(): History {
return (this._history ??= new History({ client: this.client }))
}
}
export class Find extends HeyApiClient {
/**
* Find text
@ -4217,6 +4369,11 @@ export class OpencodeClient extends HeyApiClient {
return (this._provider ??= new Provider({ client: this.client }))
}
private _sync?: Sync
get sync(): Sync {
return (this._sync ??= new Sync({ client: this.client }))
}
private _find?: Find
get find(): Find {
return (this._find ??= new Find({ client: this.client }))

View file

@ -520,6 +520,16 @@ export type EventWorkspaceFailed = {
}
}
export type EventWorkspaceRestore = {
type: "workspace.restore"
properties: {
workspaceID: string
sessionID: string
total: number
step: number
}
}
export type EventWorkspaceStatus = {
type: "workspace.status"
properties: {
@ -1137,6 +1147,7 @@ export type GlobalEvent = {
| EventPtyDeleted
| EventWorkspaceReady
| EventWorkspaceFailed
| EventWorkspaceRestore
| EventWorkspaceStatus
| EventMessageUpdated
| EventMessageRemoved
@ -2049,6 +2060,7 @@ export type Event =
| EventPtyDeleted
| EventWorkspaceReady
| EventWorkspaceFailed
| EventWorkspaceRestore
| EventWorkspaceStatus
| EventMessageUpdated
| EventMessageRemoved
@ -3006,6 +3018,42 @@ export type ExperimentalWorkspaceRemoveResponses = {
export type ExperimentalWorkspaceRemoveResponse =
ExperimentalWorkspaceRemoveResponses[keyof ExperimentalWorkspaceRemoveResponses]
export type ExperimentalWorkspaceSessionRestoreData = {
body?: {
sessionID: string
}
path: {
id: string
}
query?: {
directory?: string
workspace?: string
}
url: "/experimental/workspace/{id}/session-restore"
}
export type ExperimentalWorkspaceSessionRestoreErrors = {
/**
* Bad request
*/
400: BadRequestError
}
export type ExperimentalWorkspaceSessionRestoreError =
ExperimentalWorkspaceSessionRestoreErrors[keyof ExperimentalWorkspaceSessionRestoreErrors]
export type ExperimentalWorkspaceSessionRestoreResponses = {
/**
* Session replay started
*/
200: {
total: number
}
}
export type ExperimentalWorkspaceSessionRestoreResponse =
ExperimentalWorkspaceSessionRestoreResponses[keyof ExperimentalWorkspaceSessionRestoreResponses]
export type WorktreeRemoveData = {
body?: WorktreeRemoveInput
path?: never
@ -4456,6 +4504,85 @@ export type ProviderOauthCallbackResponses = {
export type ProviderOauthCallbackResponse = ProviderOauthCallbackResponses[keyof ProviderOauthCallbackResponses]
export type SyncReplayData = {
body?: {
directory: string
events: Array<{
id: string
aggregateID: string
seq: number
type: string
data: {
[key: string]: unknown
}
}>
}
path?: never
query?: {
directory?: string
workspace?: string
}
url: "/sync/replay"
}
export type SyncReplayErrors = {
/**
* Bad request
*/
400: BadRequestError
}
export type SyncReplayError = SyncReplayErrors[keyof SyncReplayErrors]
export type SyncReplayResponses = {
/**
* Replayed sync events
*/
200: {
sessionID: string
}
}
export type SyncReplayResponse = SyncReplayResponses[keyof SyncReplayResponses]
export type SyncHistoryListData = {
body?: {
[key: string]: number
}
path?: never
query?: {
directory?: string
workspace?: string
}
url: "/sync/history"
}
export type SyncHistoryListErrors = {
/**
* Bad request
*/
400: BadRequestError
}
export type SyncHistoryListError = SyncHistoryListErrors[keyof SyncHistoryListErrors]
export type SyncHistoryListResponses = {
/**
* Sync events
*/
200: Array<{
id: string
aggregate_id: string
seq: number
type: string
data: {
[key: string]: unknown
}
}>
}
export type SyncHistoryListResponse = SyncHistoryListResponses[keyof SyncHistoryListResponses]
export type FindTextData = {
body?: never
path?: never