From b22add292c194697c1581baf1a323996f966d04b Mon Sep 17 00:00:00 2001 From: James Long Date: Mon, 13 Apr 2026 16:51:59 -0400 Subject: [PATCH] refactor(core): publish sync events to global event stream (#22347) --- packages/app/src/context/global-sdk.tsx | 7 +- packages/opencode/src/bus/bus-event.ts | 29 +- .../opencode/src/cli/cmd/tui/context/event.ts | 4 + packages/opencode/src/project/instance.ts | 2 - .../opencode/src/server/instance/event.ts | 8 +- .../opencode/src/server/instance/global.ts | 48 +- packages/opencode/src/sync/index.ts | 59 +- packages/sdk/js/src/v2/gen/sdk.gen.ts | 20 - packages/sdk/js/src/v2/gen/types.gen.ts | 229 ++++--- packages/sdk/openapi.json | 600 ++++++++++++------ 10 files changed, 597 insertions(+), 409 deletions(-) diff --git a/packages/app/src/context/global-sdk.tsx b/packages/app/src/context/global-sdk.tsx index 1205a8fa82..172b5c9664 100644 --- a/packages/app/src/context/global-sdk.tsx +++ b/packages/app/src/context/global-sdk.tsx @@ -155,7 +155,12 @@ export const { use: useGlobalSDK, provider: GlobalSDKProvider } = createSimpleCo resetHeartbeat() streamErrorLogged = false const directory = event.directory ?? "global" - const payload = event.payload + if (event.payload.type === "sync") { + continue + } + + const payload = event.payload as Event + const k = key(directory, payload) if (k) { const i = coalesced.get(k) diff --git a/packages/opencode/src/bus/bus-event.ts b/packages/opencode/src/bus/bus-event.ts index d97922290e..aad5f398e0 100644 --- a/packages/opencode/src/bus/bus-event.ts +++ b/packages/opencode/src/bus/bus-event.ts @@ -16,25 +16,18 @@ export namespace BusEvent { } export function payloads() { - return z - .discriminatedUnion( - "type", - registry - .entries() - .map(([type, def]) => { - return z - .object({ - type: z.literal(type), - properties: def.properties, - }) - .meta({ - ref: "Event" + "." + def.type, - }) + return registry + .entries() + .map(([type, def]) => { + return z + .object({ + type: z.literal(type), + properties: def.properties, + }) + .meta({ + ref: "Event" + "." + def.type, }) - .toArray() as any, - ) - .meta({ - ref: "Event", }) + .toArray() } } diff --git a/packages/opencode/src/cli/cmd/tui/context/event.ts b/packages/opencode/src/cli/cmd/tui/context/event.ts index da073f6e92..156f9c9476 100644 --- a/packages/opencode/src/cli/cmd/tui/context/event.ts +++ b/packages/opencode/src/cli/cmd/tui/context/event.ts @@ -8,6 +8,10 @@ export function useEvent() { function subscribe(handler: (event: Event) => void) { return sdk.event.on("event", (event) => { + if (event.payload.type === "sync") { + return + } + // Special hack for truly global events if (event.directory === "global") { handler(event.payload) diff --git a/packages/opencode/src/project/instance.ts b/packages/opencode/src/project/instance.ts index 8d2d51db68..12de88a27a 100644 --- a/packages/opencode/src/project/instance.ts +++ b/packages/opencode/src/project/instance.ts @@ -21,8 +21,6 @@ const disposal = { all: undefined as Promise | undefined, } -function emitDisposed(directory: string) {} - function boot(input: { directory: string; init?: () => Promise; worktree?: string; project?: Project.Info }) { return iife(async () => { const ctx = diff --git a/packages/opencode/src/server/instance/event.ts b/packages/opencode/src/server/instance/event.ts index 989b857710..5d631d954e 100644 --- a/packages/opencode/src/server/instance/event.ts +++ b/packages/opencode/src/server/instance/event.ts @@ -1,8 +1,10 @@ +import z from "zod" import { Hono } from "hono" import { describeRoute, resolver } from "hono-openapi" import { streamSSE } from "hono/streaming" import { Log } from "@/util/log" import { BusEvent } from "@/bus/bus-event" +import { SyncEvent } from "@/sync" import { Bus } from "@/bus" import { AsyncQueue } from "../../util/queue" @@ -20,7 +22,11 @@ export const EventRoutes = () => description: "Event stream", content: { "text/event-stream": { - schema: resolver(BusEvent.payloads()), + schema: resolver( + z.union(BusEvent.payloads()).meta({ + ref: "Event", + }), + ), }, }, }, diff --git a/packages/opencode/src/server/instance/global.ts b/packages/opencode/src/server/instance/global.ts index daecb6bd36..d462a07f74 100644 --- a/packages/opencode/src/server/instance/global.ts +++ b/packages/opencode/src/server/instance/global.ts @@ -109,7 +109,7 @@ export const GlobalRoutes = lazy(() => directory: z.string(), project: z.string().optional(), workspace: z.string().optional(), - payload: BusEvent.payloads(), + payload: z.union([...BusEvent.payloads(), ...SyncEvent.payloads()]), }) .meta({ ref: "GlobalEvent", @@ -135,52 +135,6 @@ export const GlobalRoutes = lazy(() => }) }, ) - .get( - "/sync-event", - describeRoute({ - summary: "Subscribe to global sync events", - description: "Get global sync events", - operationId: "global.sync-event.subscribe", - responses: { - 200: { - description: "Event stream", - content: { - "text/event-stream": { - schema: resolver( - z - .object({ - payload: SyncEvent.payloads(), - }) - .meta({ - ref: "SyncEvent", - }), - ), - }, - }, - }, - }, - }), - async (c) => { - log.info("global sync event connected") - c.header("Cache-Control", "no-cache, no-transform") - c.header("X-Accel-Buffering", "no") - c.header("X-Content-Type-Options", "nosniff") - return streamEvents(c, (q) => { - return SyncEvent.subscribeAll(({ def, event }) => { - // TODO: don't pass def, just pass the type (and it should - // be versioned) - q.push( - JSON.stringify({ - payload: { - ...event, - type: SyncEvent.versionedType(def.type, def.version), - }, - }), - ) - }) - }) - }, - ) .get( "/config", describeRoute({ diff --git a/packages/opencode/src/sync/index.ts b/packages/opencode/src/sync/index.ts index a409391915..d7cb7f774f 100644 --- a/packages/opencode/src/sync/index.ts +++ b/packages/opencode/src/sync/index.ts @@ -2,9 +2,12 @@ import z from "zod" import type { ZodObject } from "zod" import { EventEmitter } from "events" import { Database, eq } from "@/storage/db" +import { GlobalBus } from "@/bus/global" import { Bus as ProjectBus } from "@/bus" import { BusEvent } from "@/bus/bus-event" +import { Instance } from "@/project/instance" import { EventSequenceTable, EventTable } from "./event.sql" +import { WorkspaceContext } from "@/control-plane/workspace-context" import { EventID } from "./schema" import { Flag } from "@/flag/flag" @@ -37,8 +40,6 @@ export namespace SyncEvent { let frozen = false let convertEvent: (type: string, event: Event["data"]) => Promise> | Record - const Bus = new EventEmitter<{ event: [{ def: Definition; event: Event }] }>() - export function reset() { frozen = false projectors = undefined @@ -140,11 +141,6 @@ export namespace SyncEvent { } Database.effect(() => { - Bus.emit("event", { - def, - event, - }) - if (options?.publish) { const result = convertEvent(def.type, event.data) if (result instanceof Promise) { @@ -154,6 +150,17 @@ export namespace SyncEvent { } else { ProjectBus.publish({ type: def.type, properties: def.schema }, result) } + + GlobalBus.emit("event", { + directory: Instance.directory, + project: Instance.project.id, + workspace: WorkspaceContext.workspaceID, + payload: { + type: "sync", + name: versionedType(def.type, def.version), + ...event, + }, + }) } }) }) @@ -235,31 +242,23 @@ export namespace SyncEvent { }) } - export function subscribeAll(handler: (event: { def: Definition; event: Event }) => void) { - Bus.on("event", handler) - return () => Bus.off("event", handler) - } - export function payloads() { - return z - .union( - registry - .entries() - .map(([type, def]) => { - return z - .object({ - type: z.literal(type), - aggregate: z.literal(def.aggregate), - data: def.schema, - }) - .meta({ - ref: "SyncEvent" + "." + def.type, - }) + return registry + .entries() + .map(([type, def]) => { + return z + .object({ + type: z.literal("sync"), + name: z.literal(type), + id: z.string(), + seq: z.number(), + aggregateID: z.literal(def.aggregate), + data: def.schema, + }) + .meta({ + ref: "SyncEvent" + "." + def.type, }) - .toArray() as any, - ) - .meta({ - ref: "SyncEvent", }) + .toArray() } } diff --git a/packages/sdk/js/src/v2/gen/sdk.gen.ts b/packages/sdk/js/src/v2/gen/sdk.gen.ts index f297cb18e2..b5fc976bba 100644 --- a/packages/sdk/js/src/v2/gen/sdk.gen.ts +++ b/packages/sdk/js/src/v2/gen/sdk.gen.ts @@ -51,7 +51,6 @@ import type { GlobalDisposeResponses, GlobalEventResponses, GlobalHealthResponses, - GlobalSyncEventSubscribeResponses, GlobalUpgradeErrors, GlobalUpgradeResponses, InstanceDisposeResponses, @@ -237,20 +236,6 @@ class HeyApiRegistry { } } -export class SyncEvent extends HeyApiClient { - /** - * Subscribe to global sync events - * - * Get global sync events - */ - public subscribe(options?: Options) { - return (options?.client ?? this.client).sse.get({ - url: "/global/sync-event", - ...options, - }) - } -} - export class Config extends HeyApiClient { /** * Get global configuration @@ -350,11 +335,6 @@ export class Global extends HeyApiClient { }) } - private _syncEvent?: SyncEvent - get syncEvent(): SyncEvent { - return (this._syncEvent ??= new SyncEvent({ client: this.client })) - } - private _config?: Config get config(): Config { return (this._config ??= new Config({ client: this.client })) diff --git a/packages/sdk/js/src/v2/gen/types.gen.ts b/packages/sdk/js/src/v2/gen/types.gen.ts index 83ec3d751a..e2a9a88ad3 100644 --- a/packages/sdk/js/src/v2/gen/types.gen.ts +++ b/packages/sdk/js/src/v2/gen/types.gen.ts @@ -971,64 +971,12 @@ export type EventSessionDeleted = { } } -export type Event = - | EventProjectUpdated - | EventServerInstanceDisposed - | EventInstallationUpdated - | EventInstallationUpdateAvailable - | EventServerConnected - | EventGlobalDisposed - | EventFileEdited - | EventFileWatcherUpdated - | EventLspClientDiagnostics - | EventLspUpdated - | EventMessagePartDelta - | EventPermissionAsked - | EventPermissionReplied - | EventSessionDiff - | EventSessionError - | EventQuestionAsked - | EventQuestionReplied - | EventQuestionRejected - | EventTodoUpdated - | EventSessionStatus - | EventSessionIdle - | EventSessionCompacted - | EventTuiPromptAppend - | EventTuiCommandExecute - | EventTuiToastShow - | EventTuiSessionSelect - | EventMcpToolsChanged - | EventMcpBrowserOpenFailed - | EventCommandExecuted - | EventVcsBranchUpdated - | EventWorktreeReady - | EventWorktreeFailed - | EventPtyCreated - | EventPtyUpdated - | EventPtyExited - | EventPtyDeleted - | EventWorkspaceReady - | EventWorkspaceFailed - | EventWorkspaceStatus - | EventMessageUpdated - | EventMessageRemoved - | EventMessagePartUpdated - | EventMessagePartRemoved - | EventSessionCreated - | EventSessionUpdated - | EventSessionDeleted - -export type GlobalEvent = { - directory: string - project?: string - workspace?: string - payload: Event -} - export type SyncEventMessageUpdated = { - type: "message.updated.1" - aggregate: "sessionID" + type: "sync" + name: "message.updated.1" + id: string + seq: number + aggregateID: "sessionID" data: { sessionID: string info: Message @@ -1036,8 +984,11 @@ export type SyncEventMessageUpdated = { } export type SyncEventMessageRemoved = { - type: "message.removed.1" - aggregate: "sessionID" + type: "sync" + name: "message.removed.1" + id: string + seq: number + aggregateID: "sessionID" data: { sessionID: string messageID: string @@ -1045,8 +996,11 @@ export type SyncEventMessageRemoved = { } export type SyncEventMessagePartUpdated = { - type: "message.part.updated.1" - aggregate: "sessionID" + type: "sync" + name: "message.part.updated.1" + id: string + seq: number + aggregateID: "sessionID" data: { sessionID: string part: Part @@ -1055,8 +1009,11 @@ export type SyncEventMessagePartUpdated = { } export type SyncEventMessagePartRemoved = { - type: "message.part.removed.1" - aggregate: "sessionID" + type: "sync" + name: "message.part.removed.1" + id: string + seq: number + aggregateID: "sessionID" data: { sessionID: string messageID: string @@ -1065,8 +1022,11 @@ export type SyncEventMessagePartRemoved = { } export type SyncEventSessionCreated = { - type: "session.created.1" - aggregate: "sessionID" + type: "sync" + name: "session.created.1" + id: string + seq: number + aggregateID: "sessionID" data: { sessionID: string info: Session @@ -1074,8 +1034,11 @@ export type SyncEventSessionCreated = { } export type SyncEventSessionUpdated = { - type: "session.updated.1" - aggregate: "sessionID" + type: "sync" + name: "session.updated.1" + id: string + seq: number + aggregateID: "sessionID" data: { sessionID: string info: { @@ -1114,16 +1077,75 @@ export type SyncEventSessionUpdated = { } export type SyncEventSessionDeleted = { - type: "session.deleted.1" - aggregate: "sessionID" + type: "sync" + name: "session.deleted.1" + id: string + seq: number + aggregateID: "sessionID" data: { sessionID: string info: Session } } -export type SyncEvent = { - payload: SyncEvent +export type GlobalEvent = { + directory: string + project?: string + workspace?: string + payload: + | EventProjectUpdated + | EventServerInstanceDisposed + | EventInstallationUpdated + | EventInstallationUpdateAvailable + | EventServerConnected + | EventGlobalDisposed + | EventFileEdited + | EventFileWatcherUpdated + | EventLspClientDiagnostics + | EventLspUpdated + | EventMessagePartDelta + | EventPermissionAsked + | EventPermissionReplied + | EventSessionDiff + | EventSessionError + | EventQuestionAsked + | EventQuestionReplied + | EventQuestionRejected + | EventTodoUpdated + | EventSessionStatus + | EventSessionIdle + | EventSessionCompacted + | EventTuiPromptAppend + | EventTuiCommandExecute + | EventTuiToastShow + | EventTuiSessionSelect + | EventMcpToolsChanged + | EventMcpBrowserOpenFailed + | EventCommandExecuted + | EventVcsBranchUpdated + | EventWorktreeReady + | EventWorktreeFailed + | EventPtyCreated + | EventPtyUpdated + | EventPtyExited + | EventPtyDeleted + | EventWorkspaceReady + | EventWorkspaceFailed + | EventWorkspaceStatus + | EventMessageUpdated + | EventMessageRemoved + | EventMessagePartUpdated + | EventMessagePartRemoved + | EventSessionCreated + | EventSessionUpdated + | EventSessionDeleted + | SyncEventMessageUpdated + | SyncEventMessageRemoved + | SyncEventMessagePartUpdated + | SyncEventMessagePartRemoved + | SyncEventSessionCreated + | SyncEventSessionUpdated + | SyncEventSessionDeleted } /** @@ -1982,6 +2004,54 @@ export type File = { status: "added" | "deleted" | "modified" } +export type Event = + | EventProjectUpdated + | EventServerInstanceDisposed + | EventInstallationUpdated + | EventInstallationUpdateAvailable + | EventServerConnected + | EventGlobalDisposed + | EventFileEdited + | EventFileWatcherUpdated + | EventLspClientDiagnostics + | EventLspUpdated + | EventMessagePartDelta + | EventPermissionAsked + | EventPermissionReplied + | EventSessionDiff + | EventSessionError + | EventQuestionAsked + | EventQuestionReplied + | EventQuestionRejected + | EventTodoUpdated + | EventSessionStatus + | EventSessionIdle + | EventSessionCompacted + | EventTuiPromptAppend + | EventTuiCommandExecute + | EventTuiToastShow + | EventTuiSessionSelect + | EventMcpToolsChanged + | EventMcpBrowserOpenFailed + | EventCommandExecuted + | EventVcsBranchUpdated + | EventWorktreeReady + | EventWorktreeFailed + | EventPtyCreated + | EventPtyUpdated + | EventPtyExited + | EventPtyDeleted + | EventWorkspaceReady + | EventWorkspaceFailed + | EventWorkspaceStatus + | EventMessageUpdated + | EventMessageRemoved + | EventMessagePartUpdated + | EventMessagePartRemoved + | EventSessionCreated + | EventSessionUpdated + | EventSessionDeleted + export type McpStatusConnected = { status: "connected" } @@ -2113,23 +2183,6 @@ export type GlobalEventResponses = { export type GlobalEventResponse = GlobalEventResponses[keyof GlobalEventResponses] -export type GlobalSyncEventSubscribeData = { - body?: never - path?: never - query?: never - url: "/global/sync-event" -} - -export type GlobalSyncEventSubscribeResponses = { - /** - * Event stream - */ - 200: SyncEvent -} - -export type GlobalSyncEventSubscribeResponse = - GlobalSyncEventSubscribeResponses[keyof GlobalSyncEventSubscribeResponses] - export type GlobalConfigGetData = { body?: never path?: never diff --git a/packages/sdk/openapi.json b/packages/sdk/openapi.json index 85443726f3..54f11eccf3 100644 --- a/packages/sdk/openapi.json +++ b/packages/sdk/openapi.json @@ -66,31 +66,6 @@ ] } }, - "/global/sync-event": { - "get": { - "operationId": "global.sync-event.subscribe", - "summary": "Subscribe to global sync events", - "description": "Get global sync events", - "responses": { - "200": { - "description": "Event stream", - "content": { - "text/event-stream": { - "schema": { - "$ref": "#/components/schemas/SyncEvent" - } - } - } - } - }, - "x-codeSamples": [ - { - "lang": "js", - "source": "import { createOpencodeClient } from \"@opencode-ai/sdk\n\nconst client = createOpencodeClient()\nawait client.global.sync-event.subscribe({\n ...\n})" - } - ] - } - }, "/global/config": { "get": { "operationId": "global.config.get", @@ -9925,174 +9900,24 @@ }, "required": ["type", "properties"] }, - "Event": { - "anyOf": [ - { - "$ref": "#/components/schemas/Event.project.updated" - }, - { - "$ref": "#/components/schemas/Event.server.instance.disposed" - }, - { - "$ref": "#/components/schemas/Event.installation.updated" - }, - { - "$ref": "#/components/schemas/Event.installation.update-available" - }, - { - "$ref": "#/components/schemas/Event.server.connected" - }, - { - "$ref": "#/components/schemas/Event.global.disposed" - }, - { - "$ref": "#/components/schemas/Event.file.edited" - }, - { - "$ref": "#/components/schemas/Event.file.watcher.updated" - }, - { - "$ref": "#/components/schemas/Event.lsp.client.diagnostics" - }, - { - "$ref": "#/components/schemas/Event.lsp.updated" - }, - { - "$ref": "#/components/schemas/Event.message.part.delta" - }, - { - "$ref": "#/components/schemas/Event.permission.asked" - }, - { - "$ref": "#/components/schemas/Event.permission.replied" - }, - { - "$ref": "#/components/schemas/Event.session.diff" - }, - { - "$ref": "#/components/schemas/Event.session.error" - }, - { - "$ref": "#/components/schemas/Event.question.asked" - }, - { - "$ref": "#/components/schemas/Event.question.replied" - }, - { - "$ref": "#/components/schemas/Event.question.rejected" - }, - { - "$ref": "#/components/schemas/Event.todo.updated" - }, - { - "$ref": "#/components/schemas/Event.session.status" - }, - { - "$ref": "#/components/schemas/Event.session.idle" - }, - { - "$ref": "#/components/schemas/Event.session.compacted" - }, - { - "$ref": "#/components/schemas/Event.tui.prompt.append" - }, - { - "$ref": "#/components/schemas/Event.tui.command.execute" - }, - { - "$ref": "#/components/schemas/Event.tui.toast.show" - }, - { - "$ref": "#/components/schemas/Event.tui.session.select" - }, - { - "$ref": "#/components/schemas/Event.mcp.tools.changed" - }, - { - "$ref": "#/components/schemas/Event.mcp.browser.open.failed" - }, - { - "$ref": "#/components/schemas/Event.command.executed" - }, - { - "$ref": "#/components/schemas/Event.vcs.branch.updated" - }, - { - "$ref": "#/components/schemas/Event.worktree.ready" - }, - { - "$ref": "#/components/schemas/Event.worktree.failed" - }, - { - "$ref": "#/components/schemas/Event.pty.created" - }, - { - "$ref": "#/components/schemas/Event.pty.updated" - }, - { - "$ref": "#/components/schemas/Event.pty.exited" - }, - { - "$ref": "#/components/schemas/Event.pty.deleted" - }, - { - "$ref": "#/components/schemas/Event.workspace.ready" - }, - { - "$ref": "#/components/schemas/Event.workspace.failed" - }, - { - "$ref": "#/components/schemas/Event.workspace.status" - }, - { - "$ref": "#/components/schemas/Event.message.updated" - }, - { - "$ref": "#/components/schemas/Event.message.removed" - }, - { - "$ref": "#/components/schemas/Event.message.part.updated" - }, - { - "$ref": "#/components/schemas/Event.message.part.removed" - }, - { - "$ref": "#/components/schemas/Event.session.created" - }, - { - "$ref": "#/components/schemas/Event.session.updated" - }, - { - "$ref": "#/components/schemas/Event.session.deleted" - } - ] - }, - "GlobalEvent": { - "type": "object", - "properties": { - "directory": { - "type": "string" - }, - "project": { - "type": "string" - }, - "workspace": { - "type": "string" - }, - "payload": { - "$ref": "#/components/schemas/Event" - } - }, - "required": ["directory", "payload"] - }, "SyncEvent.message.updated": { "type": "object", "properties": { "type": { + "type": "string", + "const": "sync" + }, + "name": { "type": "string", "const": "message.updated.1" }, - "aggregate": { + "id": { + "type": "string" + }, + "seq": { + "type": "number" + }, + "aggregateID": { "type": "string", "const": "sessionID" }, @@ -10110,16 +9935,26 @@ "required": ["sessionID", "info"] } }, - "required": ["type", "aggregate", "data"] + "required": ["type", "name", "id", "seq", "aggregateID", "data"] }, "SyncEvent.message.removed": { "type": "object", "properties": { "type": { + "type": "string", + "const": "sync" + }, + "name": { "type": "string", "const": "message.removed.1" }, - "aggregate": { + "id": { + "type": "string" + }, + "seq": { + "type": "number" + }, + "aggregateID": { "type": "string", "const": "sessionID" }, @@ -10138,16 +9973,26 @@ "required": ["sessionID", "messageID"] } }, - "required": ["type", "aggregate", "data"] + "required": ["type", "name", "id", "seq", "aggregateID", "data"] }, "SyncEvent.message.part.updated": { "type": "object", "properties": { "type": { + "type": "string", + "const": "sync" + }, + "name": { "type": "string", "const": "message.part.updated.1" }, - "aggregate": { + "id": { + "type": "string" + }, + "seq": { + "type": "number" + }, + "aggregateID": { "type": "string", "const": "sessionID" }, @@ -10168,16 +10013,26 @@ "required": ["sessionID", "part", "time"] } }, - "required": ["type", "aggregate", "data"] + "required": ["type", "name", "id", "seq", "aggregateID", "data"] }, "SyncEvent.message.part.removed": { "type": "object", "properties": { "type": { + "type": "string", + "const": "sync" + }, + "name": { "type": "string", "const": "message.part.removed.1" }, - "aggregate": { + "id": { + "type": "string" + }, + "seq": { + "type": "number" + }, + "aggregateID": { "type": "string", "const": "sessionID" }, @@ -10200,16 +10055,26 @@ "required": ["sessionID", "messageID", "partID"] } }, - "required": ["type", "aggregate", "data"] + "required": ["type", "name", "id", "seq", "aggregateID", "data"] }, "SyncEvent.session.created": { "type": "object", "properties": { "type": { + "type": "string", + "const": "sync" + }, + "name": { "type": "string", "const": "session.created.1" }, - "aggregate": { + "id": { + "type": "string" + }, + "seq": { + "type": "number" + }, + "aggregateID": { "type": "string", "const": "sessionID" }, @@ -10227,16 +10092,26 @@ "required": ["sessionID", "info"] } }, - "required": ["type", "aggregate", "data"] + "required": ["type", "name", "id", "seq", "aggregateID", "data"] }, "SyncEvent.session.updated": { "type": "object", "properties": { "type": { + "type": "string", + "const": "sync" + }, + "name": { "type": "string", "const": "session.updated.1" }, - "aggregate": { + "id": { + "type": "string" + }, + "seq": { + "type": "number" + }, + "aggregateID": { "type": "string", "const": "sessionID" }, @@ -10479,16 +10354,26 @@ "required": ["sessionID", "info"] } }, - "required": ["type", "aggregate", "data"] + "required": ["type", "name", "id", "seq", "aggregateID", "data"] }, "SyncEvent.session.deleted": { "type": "object", "properties": { "type": { + "type": "string", + "const": "sync" + }, + "name": { "type": "string", "const": "session.deleted.1" }, - "aggregate": { + "id": { + "type": "string" + }, + "seq": { + "type": "number" + }, + "aggregateID": { "type": "string", "const": "sessionID" }, @@ -10506,16 +10391,185 @@ "required": ["sessionID", "info"] } }, - "required": ["type", "aggregate", "data"] + "required": ["type", "name", "id", "seq", "aggregateID", "data"] }, - "SyncEvent": { + "GlobalEvent": { "type": "object", "properties": { + "directory": { + "type": "string" + }, + "project": { + "type": "string" + }, + "workspace": { + "type": "string" + }, "payload": { - "$ref": "#/components/schemas/SyncEvent" + "anyOf": [ + { + "$ref": "#/components/schemas/Event.project.updated" + }, + { + "$ref": "#/components/schemas/Event.server.instance.disposed" + }, + { + "$ref": "#/components/schemas/Event.installation.updated" + }, + { + "$ref": "#/components/schemas/Event.installation.update-available" + }, + { + "$ref": "#/components/schemas/Event.server.connected" + }, + { + "$ref": "#/components/schemas/Event.global.disposed" + }, + { + "$ref": "#/components/schemas/Event.file.edited" + }, + { + "$ref": "#/components/schemas/Event.file.watcher.updated" + }, + { + "$ref": "#/components/schemas/Event.lsp.client.diagnostics" + }, + { + "$ref": "#/components/schemas/Event.lsp.updated" + }, + { + "$ref": "#/components/schemas/Event.message.part.delta" + }, + { + "$ref": "#/components/schemas/Event.permission.asked" + }, + { + "$ref": "#/components/schemas/Event.permission.replied" + }, + { + "$ref": "#/components/schemas/Event.session.diff" + }, + { + "$ref": "#/components/schemas/Event.session.error" + }, + { + "$ref": "#/components/schemas/Event.question.asked" + }, + { + "$ref": "#/components/schemas/Event.question.replied" + }, + { + "$ref": "#/components/schemas/Event.question.rejected" + }, + { + "$ref": "#/components/schemas/Event.todo.updated" + }, + { + "$ref": "#/components/schemas/Event.session.status" + }, + { + "$ref": "#/components/schemas/Event.session.idle" + }, + { + "$ref": "#/components/schemas/Event.session.compacted" + }, + { + "$ref": "#/components/schemas/Event.tui.prompt.append" + }, + { + "$ref": "#/components/schemas/Event.tui.command.execute" + }, + { + "$ref": "#/components/schemas/Event.tui.toast.show" + }, + { + "$ref": "#/components/schemas/Event.tui.session.select" + }, + { + "$ref": "#/components/schemas/Event.mcp.tools.changed" + }, + { + "$ref": "#/components/schemas/Event.mcp.browser.open.failed" + }, + { + "$ref": "#/components/schemas/Event.command.executed" + }, + { + "$ref": "#/components/schemas/Event.vcs.branch.updated" + }, + { + "$ref": "#/components/schemas/Event.worktree.ready" + }, + { + "$ref": "#/components/schemas/Event.worktree.failed" + }, + { + "$ref": "#/components/schemas/Event.pty.created" + }, + { + "$ref": "#/components/schemas/Event.pty.updated" + }, + { + "$ref": "#/components/schemas/Event.pty.exited" + }, + { + "$ref": "#/components/schemas/Event.pty.deleted" + }, + { + "$ref": "#/components/schemas/Event.workspace.ready" + }, + { + "$ref": "#/components/schemas/Event.workspace.failed" + }, + { + "$ref": "#/components/schemas/Event.workspace.status" + }, + { + "$ref": "#/components/schemas/Event.message.updated" + }, + { + "$ref": "#/components/schemas/Event.message.removed" + }, + { + "$ref": "#/components/schemas/Event.message.part.updated" + }, + { + "$ref": "#/components/schemas/Event.message.part.removed" + }, + { + "$ref": "#/components/schemas/Event.session.created" + }, + { + "$ref": "#/components/schemas/Event.session.updated" + }, + { + "$ref": "#/components/schemas/Event.session.deleted" + }, + { + "$ref": "#/components/schemas/SyncEvent.message.updated" + }, + { + "$ref": "#/components/schemas/SyncEvent.message.removed" + }, + { + "$ref": "#/components/schemas/SyncEvent.message.part.updated" + }, + { + "$ref": "#/components/schemas/SyncEvent.message.part.removed" + }, + { + "$ref": "#/components/schemas/SyncEvent.session.created" + }, + { + "$ref": "#/components/schemas/SyncEvent.session.updated" + }, + { + "$ref": "#/components/schemas/SyncEvent.session.deleted" + } + ] } }, - "required": ["payload"] + "required": ["directory", "payload"] }, "LogLevel": { "description": "Log level", @@ -12608,6 +12662,148 @@ }, "required": ["path", "added", "removed", "status"] }, + "Event": { + "anyOf": [ + { + "$ref": "#/components/schemas/Event.project.updated" + }, + { + "$ref": "#/components/schemas/Event.server.instance.disposed" + }, + { + "$ref": "#/components/schemas/Event.installation.updated" + }, + { + "$ref": "#/components/schemas/Event.installation.update-available" + }, + { + "$ref": "#/components/schemas/Event.server.connected" + }, + { + "$ref": "#/components/schemas/Event.global.disposed" + }, + { + "$ref": "#/components/schemas/Event.file.edited" + }, + { + "$ref": "#/components/schemas/Event.file.watcher.updated" + }, + { + "$ref": "#/components/schemas/Event.lsp.client.diagnostics" + }, + { + "$ref": "#/components/schemas/Event.lsp.updated" + }, + { + "$ref": "#/components/schemas/Event.message.part.delta" + }, + { + "$ref": "#/components/schemas/Event.permission.asked" + }, + { + "$ref": "#/components/schemas/Event.permission.replied" + }, + { + "$ref": "#/components/schemas/Event.session.diff" + }, + { + "$ref": "#/components/schemas/Event.session.error" + }, + { + "$ref": "#/components/schemas/Event.question.asked" + }, + { + "$ref": "#/components/schemas/Event.question.replied" + }, + { + "$ref": "#/components/schemas/Event.question.rejected" + }, + { + "$ref": "#/components/schemas/Event.todo.updated" + }, + { + "$ref": "#/components/schemas/Event.session.status" + }, + { + "$ref": "#/components/schemas/Event.session.idle" + }, + { + "$ref": "#/components/schemas/Event.session.compacted" + }, + { + "$ref": "#/components/schemas/Event.tui.prompt.append" + }, + { + "$ref": "#/components/schemas/Event.tui.command.execute" + }, + { + "$ref": "#/components/schemas/Event.tui.toast.show" + }, + { + "$ref": "#/components/schemas/Event.tui.session.select" + }, + { + "$ref": "#/components/schemas/Event.mcp.tools.changed" + }, + { + "$ref": "#/components/schemas/Event.mcp.browser.open.failed" + }, + { + "$ref": "#/components/schemas/Event.command.executed" + }, + { + "$ref": "#/components/schemas/Event.vcs.branch.updated" + }, + { + "$ref": "#/components/schemas/Event.worktree.ready" + }, + { + "$ref": "#/components/schemas/Event.worktree.failed" + }, + { + "$ref": "#/components/schemas/Event.pty.created" + }, + { + "$ref": "#/components/schemas/Event.pty.updated" + }, + { + "$ref": "#/components/schemas/Event.pty.exited" + }, + { + "$ref": "#/components/schemas/Event.pty.deleted" + }, + { + "$ref": "#/components/schemas/Event.workspace.ready" + }, + { + "$ref": "#/components/schemas/Event.workspace.failed" + }, + { + "$ref": "#/components/schemas/Event.workspace.status" + }, + { + "$ref": "#/components/schemas/Event.message.updated" + }, + { + "$ref": "#/components/schemas/Event.message.removed" + }, + { + "$ref": "#/components/schemas/Event.message.part.updated" + }, + { + "$ref": "#/components/schemas/Event.message.part.removed" + }, + { + "$ref": "#/components/schemas/Event.session.created" + }, + { + "$ref": "#/components/schemas/Event.session.updated" + }, + { + "$ref": "#/components/schemas/Event.session.deleted" + } + ] + }, "MCPStatusConnected": { "type": "object", "properties": {