feat(plugin): publish generation updates

This commit is contained in:
Dax Raad 2026-07-05 17:21:39 -04:00
parent 6ffecf9345
commit 33cb536879
7 changed files with 74 additions and 1 deletions

View file

@ -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

View file

@ -92,6 +92,7 @@ const layer = Layer.effect(
id: definition.id,
...(definition.version === undefined ? {} : { version: definition.version }),
}))
yield* events.publish(Event.Updated, {})
}),
)
})

View file

@ -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()

View file

@ -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) }

View file

@ -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)

View file

@ -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<unknown>
}
export type ProjectDirectoriesUpdated2 = {
id: string
created: number
@ -11192,6 +11237,7 @@ export type V2EventV2 =
| PermissionV2Asked2
| PermissionV2Replied2
| PluginAdded2
| PluginUpdated2
| ProjectDirectoriesUpdated2
| CommandUpdated2
| ConfigUpdated2

View file

@ -1040,6 +1040,12 @@ function App(props: { onSnapshot?: () => Promise<string[]>; 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({