From 33cb536879be44f15214130b5b65d5c3f170766a Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Sun, 5 Jul 2026 17:21:39 -0400 Subject: [PATCH] feat(plugin): publish generation updates --- .../client/src/promise/generated/types.ts | 8 ++++ packages/core/src/plugin.ts | 1 + packages/core/test/plugin.test.ts | 5 ++ packages/schema/src/plugin.ts | 6 ++- packages/schema/test/event-manifest.test.ts | 3 ++ packages/sdk/js/src/v2/gen/types.gen.ts | 46 +++++++++++++++++++ packages/tui/src/app.tsx | 6 +++ 7 files changed, 74 insertions(+), 1 deletion(-) diff --git a/packages/client/src/promise/generated/types.ts b/packages/client/src/promise/generated/types.ts index c4ed970247b..f58135023c0 100644 --- a/packages/client/src/promise/generated/types.ts +++ b/packages/client/src/promise/generated/types.ts @@ -4974,6 +4974,14 @@ export type EventSubscribeOutput = readonly location?: { readonly directory: string; readonly workspaceID?: string } readonly data: { readonly id: string } } + | { + readonly id: string + readonly created: number + readonly metadata?: { readonly [x: string]: unknown } + readonly type: "plugin.updated" + readonly location?: { readonly directory: string; readonly workspaceID?: string } + readonly data: {} + } | { readonly id: string readonly created: number diff --git a/packages/core/src/plugin.ts b/packages/core/src/plugin.ts index 1b57d9fc5e4..907920c10e1 100644 --- a/packages/core/src/plugin.ts +++ b/packages/core/src/plugin.ts @@ -92,6 +92,7 @@ const layer = Layer.effect( id: definition.id, ...(definition.version === undefined ? {} : { version: definition.version }), })) + yield* events.publish(Event.Updated, {}) }), ) }) diff --git a/packages/core/test/plugin.test.ts b/packages/core/test/plugin.test.ts index 6037f4538b1..e437feb0667 100644 --- a/packages/core/test/plugin.test.ts +++ b/packages/core/test/plugin.test.ts @@ -42,7 +42,11 @@ describe("PluginV2", () => { Effect.gen(function* () { const plugins = yield* PluginV2.Service const agents = yield* AgentV2.Service + const events = yield* EventV2.Service let description = "first" + const updated = yield* events + .subscribe(Plugin.Event.Updated) + .pipe(Stream.take(2), Stream.runCollect, Effect.forkScoped({ startImmediately: true })) const managed = () => define({ @@ -67,6 +71,7 @@ describe("PluginV2", () => { yield* plugins.activate([{ plugin: managed(), version: "next" }]) expect((yield* agents.get(AgentV2.ID.make("configured")))?.description).toBe("second") + expect(yield* Fiber.join(updated)).toHaveLength(2) yield* plugins.activate([]) expect(yield* agents.get(AgentV2.ID.make("configured"))).toBeUndefined() diff --git a/packages/schema/src/plugin.ts b/packages/schema/src/plugin.ts index 40379e30512..4a003f1199e 100644 --- a/packages/schema/src/plugin.ts +++ b/packages/schema/src/plugin.ts @@ -15,4 +15,8 @@ const Added = ephemeral({ type: "plugin.added", schema: { id: ID }, }) -export const Event = { Added, Definitions: inventory(Added) } +const Updated = ephemeral({ + type: "plugin.updated", + schema: {}, +}) +export const Event = { Added, Updated, Definitions: inventory(Added, Updated) } diff --git a/packages/schema/test/event-manifest.test.ts b/packages/schema/test/event-manifest.test.ts index 0147c7469bd..da344ba5289 100644 --- a/packages/schema/test/event-manifest.test.ts +++ b/packages/schema/test/event-manifest.test.ts @@ -15,6 +15,7 @@ import { EventManifest } from "../src/event-manifest.js" import { FileSystemV1 } from "../src/filesystem-v1.js" import { IdeEvent } from "../src/ide-event.js" import { McpEvent } from "../src/mcp-event.js" +import { Plugin } from "../src/plugin.js" import { SessionEvent } from "../src/session-event.js" import { SessionTodo } from "../src/session-todo.js" import { SessionV1 } from "../src/session-v1.js" @@ -46,6 +47,7 @@ describe("public event manifest", () => { EventManifest.Definitions.map((definition) => definition.type), ) expect(EventManifest.Latest.get("agent.updated")).toBe(Agent.Event.Updated) + expect(EventManifest.Latest.get("plugin.updated")).toBe(Plugin.Event.Updated) expect(EventManifest.Server.get("mcp.status.changed")).toBe(McpEvent.StatusChanged) expect(EventManifest.Server.has("mcp.tools.changed")).toBe(false) expect(Agent.Event.Updated.durable).toBeUndefined() @@ -70,6 +72,7 @@ describe("public event manifest", () => { expect(Permission.Event.Definitions).toEqual([Permission.Event.Asked, Permission.Event.Replied]) expect(Form.Event.Definitions).toEqual([Form.Event.Created, Form.Event.Replied, Form.Event.Cancelled]) expect(Reference.Event.Definitions).toEqual([Reference.Event.Updated]) + expect(Plugin.Event.Definitions).toEqual([Plugin.Event.Added, Plugin.Event.Updated]) expect(McpEvent.Definitions).toEqual([McpEvent.ToolsChanged, McpEvent.StatusChanged]) expect(EventManifest.Latest.has("mcp.browser.open.failed")).toBe(false) expect(EventManifest.Latest.has("ide.installed")).toBe(false) diff --git a/packages/sdk/js/src/v2/gen/types.gen.ts b/packages/sdk/js/src/v2/gen/types.gen.ts index 6db6b405b84..5b9d40cad58 100644 --- a/packages/sdk/js/src/v2/gen/types.gen.ts +++ b/packages/sdk/js/src/v2/gen/types.gen.ts @@ -63,6 +63,7 @@ export type Event = | EventPermissionV2Asked | EventPermissionV2Replied | EventPluginAdded + | EventPluginUpdated | EventProjectDirectoriesUpdated | EventCommandUpdated | EventConfigUpdated @@ -1325,6 +1326,13 @@ export type GlobalEvent = { id: string } } + | { + id: string + type: "plugin.updated" + properties: { + [key: string]: unknown + } + } | { id: string type: "project.directories.updated" @@ -3071,6 +3079,7 @@ export type V2Event = | PermissionV2Asked | PermissionV2Replied | PluginAdded + | PluginUpdated | ProjectDirectoriesUpdated | CommandUpdated | ConfigUpdated @@ -5957,6 +5966,19 @@ export type PluginAdded = { } } +export type PluginUpdated = { + id: string + created: number + metadata?: { + [key: string]: unknown + } + type: "plugin.updated" + location?: LocationRef + data: { + [key: string]: unknown + } +} + export type ProjectDirectoriesUpdated = { id: string created: number @@ -7274,6 +7296,14 @@ export type EventPluginAdded = { } } +export type EventPluginUpdated = { + id: string + type: "plugin.updated" + properties: { + [key: string]: unknown + } +} + export type EventProjectDirectoriesUpdated = { id: string type: "project.directories.updated" @@ -10380,6 +10410,21 @@ export type PluginAdded2 = { } } +export type PluginUpdated2 = { + id: string + created: number + metadata?: { + [key: string]: unknown + } + type: "plugin.updated" + location?: LocationRef2 + data: + | { + [key: string]: unknown + } + | Array +} + export type ProjectDirectoriesUpdated2 = { id: string created: number @@ -11192,6 +11237,7 @@ export type V2EventV2 = | PermissionV2Asked2 | PermissionV2Replied2 | PluginAdded2 + | PluginUpdated2 | ProjectDirectoriesUpdated2 | CommandUpdated2 | ConfigUpdated2 diff --git a/packages/tui/src/app.tsx b/packages/tui/src/app.tsx index a3b2c319737..b769a436a00 100644 --- a/packages/tui/src/app.tsx +++ b/packages/tui/src/app.tsx @@ -1040,6 +1040,12 @@ function App(props: { onSnapshot?: () => Promise; pluginHost: TuiPlugi }) }) + event.on("plugin.updated", (_evt, { directory, workspace }) => { + if (directory !== project.instance.directory()) return + if (workspace !== project.workspace.current()) return + toast.show({ variant: "success", message: "Plugins reloaded" }) + }) + event.on("tui.session.select", (evt, { workspace }) => { if (workspace !== project.workspace.current()) return route.navigate({