diff --git a/packages/core/src/filesystem/location-watcher.ts b/packages/core/src/filesystem/location-watcher.ts index 40a76c4de30..2ba0b90a8d6 100644 --- a/packages/core/src/filesystem/location-watcher.ts +++ b/packages/core/src/filesystem/location-watcher.ts @@ -93,7 +93,7 @@ const layer = Layer.effect( ) yield* policy.observe(reconcile) yield* Effect.gen(function* () { - yield* plugins.flush + yield* plugins.awaitActivation yield* reconcile(policy.current()) }).pipe( Effect.catchCauseIf( diff --git a/packages/core/src/plugin/supervisor-service.ts b/packages/core/src/plugin/supervisor-service.ts index 8cb09b5f02b..73eaf186ef0 100644 --- a/packages/core/src/plugin/supervisor-service.ts +++ b/packages/core/src/plugin/supervisor-service.ts @@ -8,10 +8,12 @@ import { Context, Effect } from "effect" */ export interface Interface { /** - * Wait for the plugin generation to settle. Use this rarely: blocking reads, - * UI startup, or other unrelated work on plugin boot should be avoided. + * Wait for configured plugin activation to settle, including missing-package installs. + * Completion does not imply every plugin succeeded. + * Interrupting this wait does not cancel activation. Use rarely: avoid blocking reads, + * UI startup, or unrelated work on plugin boot. */ - readonly flush: Effect.Effect + readonly awaitActivation: Effect.Effect } export class Service extends Context.Service()("@opencode/PluginSupervisor") {} diff --git a/packages/core/src/plugin/supervisor.ts b/packages/core/src/plugin/supervisor.ts index 7ec540c80aa..961127a154c 100644 --- a/packages/core/src/plugin/supervisor.ts +++ b/packages/core/src/plugin/supervisor.ts @@ -169,7 +169,7 @@ export const layer = Layer.effect( Stream.map(() => undefined), ), ).pipe( - // Make accepted work visible to flush before coalescing the burst. + // Make accepted work visible to awaitActivation before coalescing the burst. Stream.mapEffect(() => Effect.gen(function* () { observed++ @@ -191,7 +191,7 @@ export const layer = Layer.effect( Effect.forkScoped({ startImmediately: true }), ) yield* Effect.sleep("24 hours").pipe(Effect.andThen(activate()), Effect.forever, Effect.forkScoped) - return Service.of({ flush: ready.await }) + return Service.of({ awaitActivation: ready.await }) }), ) diff --git a/packages/core/src/session.ts b/packages/core/src/session.ts index 25298d3f415..626dfc29d4d 100644 --- a/packages/core/src/session.ts +++ b/packages/core/src/session.ts @@ -407,7 +407,7 @@ const layer = Layer.effect( const session = yield* result.get(input.sessionID) const commands = yield* Effect.gen(function* () { const plugins = yield* PluginSupervisor.Service - yield* plugins.flush + yield* plugins.awaitActivation return yield* Command.Service }).pipe(instances.provide(session)) const delivery = input.delivery ?? "steer" diff --git a/packages/core/src/session/context.ts b/packages/core/src/session/context.ts index 18e535f83da..4ec2b323ed0 100644 --- a/packages/core/src/session/context.ts +++ b/packages/core/src/session/context.ts @@ -125,7 +125,7 @@ const layer = Layer.effect( if (session.location.directory !== location.directory || session.location.workspaceID !== location.workspaceID) return yield* Effect.interrupt - yield* plugins.flush + yield* plugins.awaitActivation yield* mcpTools.flush const agent = yield* agents.select(session.agent) if (!agent.info) return yield* new AgentNotFoundError({ sessionID: session.id, agent: session.agent ?? agent.id }) diff --git a/packages/core/src/session/prompt.ts b/packages/core/src/session/prompt.ts index 71a8f0db700..9cd8df4d4f2 100644 --- a/packages/core/src/session/prompt.ts +++ b/packages/core/src/session/prompt.ts @@ -37,7 +37,7 @@ export const make = Effect.fn("SessionPrompt.make")(function* () { messageID: SessionMessage.ID input: Input }) { - yield* plugins.flush + yield* plugins.awaitActivation const event = yield* hooks.trigger("session", "prompt", { sessionID: request.sessionID, messageID: request.messageID, diff --git a/packages/core/src/session/revert.ts b/packages/core/src/session/revert.ts index b9f60d485ba..05cfd634794 100644 --- a/packages/core/src/session/revert.ts +++ b/packages/core/src/session/revert.ts @@ -38,7 +38,7 @@ export const make = Effect.fn("SessionRevert.make")(function* () { const snapshot = yield* Snapshot.Service const stage: Interface["stage"] = Effect.fn("SessionRevert.stage")(function* (input) { - yield* plugins.flush + yield* plugins.awaitActivation const original = input.session.revert?.snapshot ? Snapshot.ID.make(input.session.revert.snapshot) : yield* snapshot.capture() @@ -66,7 +66,7 @@ export const make = Effect.fn("SessionRevert.make")(function* () { }) const clear: Interface["clear"] = Effect.fn("SessionRevert.clear")(function* (session) { - yield* plugins.flush + yield* plugins.awaitActivation if (!session.revert) return const original = session.revert.snapshot ? Snapshot.ID.make(session.revert.snapshot) : undefined if (original) diff --git a/packages/core/src/session/runner/llm.ts b/packages/core/src/session/runner/llm.ts index c9622e5ee45..7bbc89e2171 100644 --- a/packages/core/src/session/runner/llm.ts +++ b/packages/core/src/session/runner/llm.ts @@ -57,7 +57,7 @@ const layer = Layer.effect( const control = pending.type === "compaction" || pending.type === "move" if (promotable === "steer" && pending.delivery === "queue" && !control) return DrainResult.Complete() } - yield* plugins.flush + yield* plugins.awaitActivation yield* settleStaleToolCalls(sessionID) const advanceToStep = Effect.fn("SessionRunner.advanceToStep")(() => diff --git a/packages/core/src/session/session.ts b/packages/core/src/session/session.ts index 7dd1db4df85..df0473acd7d 100644 --- a/packages/core/src/session/session.ts +++ b/packages/core/src/session/session.ts @@ -196,7 +196,7 @@ export const make = Effect.fn("Session.make")(function* () { // Resolve shell services here without pinning Session events to this Location after a move. const shell = yield* Effect.gen(function* () { const plugins = yield* PluginSupervisor.Service - yield* plugins.flush + yield* plugins.awaitActivation return yield* Shell.Service }).pipe(instances.provide(session)) const started = yield* shell diff --git a/packages/core/test/config/plugin.test.ts b/packages/core/test/config/plugin.test.ts index 53f45a14638..ca20987ccb2 100644 --- a/packages/core/test/config/plugin.test.ts +++ b/packages/core/test/config/plugin.test.ts @@ -490,7 +490,7 @@ describe("PluginSupervisor config", () => { ), ) - it.live("unblocks flush when plugin activation fails", () => + it.live("unblocks awaitActivation when plugin activation fails", () => Effect.gen(function* () { const sdk = yield* SdkPlugins.Service yield* sdk.register(define({ id: "duplicate-id", effect: () => Effect.void })) @@ -530,9 +530,9 @@ describe("PluginSupervisor config", () => { const plugins = yield* Plugin.Service expect((yield* plugins.list()).map((plugin) => String(plugin.id))).toContain("opencode.provider.openai") const supervisor = yield* PluginSupervisor.Service - expect(Option.isNone(yield* supervisor.flush.pipe(Effect.timeoutOption("20 millis")))).toBeTrue() + expect(Option.isNone(yield* supervisor.awaitActivation.pipe(Effect.timeoutOption("20 millis")))).toBeTrue() yield* Effect.promise(() => Bun.write(path.join(global.tmp, "cold-plugin", "release"), "")) - yield* supervisor.flush.pipe(Effect.timeout("2 seconds")) + yield* supervisor.awaitActivation.pipe(Effect.timeout("2 seconds")) expect((yield* plugins.list()).map((plugin) => String(plugin.id))).toContain("cold-plugin") }), ), @@ -542,7 +542,7 @@ describe("PluginSupervisor config", () => { const ready = Effect.fnUntraced(function* () { const supervisor = yield* PluginSupervisor.Service - yield* supervisor.flush + yield* supervisor.awaitActivation }) const waitForFile = (file: string) => diff --git a/packages/core/test/filesystem/watcher.test.ts b/packages/core/test/filesystem/watcher.test.ts index 9ad0401684a..e97ac7bb533 100644 --- a/packages/core/test/filesystem/watcher.test.ts +++ b/packages/core/test/filesystem/watcher.test.ts @@ -31,7 +31,7 @@ const it = testEffect(AppNodeBuilder.build(LayerNode.group([FSUtil.node, Bus.nod const configLayer = Config.testLayer() const pluginNode = makeLocationNode({ service: PluginSupervisor.Service, - layer: Layer.succeed(PluginSupervisor.Service, PluginSupervisor.Service.of({ flush: Effect.void })), + layer: Layer.succeed(PluginSupervisor.Service, PluginSupervisor.Service.of({ awaitActivation: Effect.void })), deps: [], }) @@ -313,7 +313,7 @@ describe("LocationWatcher subscriptions", () => { Effect.gen(function* () { const policy = yield* LocationWatcherPolicy.Service yield* policy.transform((draft) => draft.add([".git"])) - return PluginSupervisor.Service.of({ flush: Effect.void }) + return PluginSupervisor.Service.of({ awaitActivation: Effect.void }) }), ), deps: [LocationWatcherPolicy.node], diff --git a/packages/core/test/fixture/prompt-location.ts b/packages/core/test/fixture/prompt-location.ts index 9f4a66fe5c1..71a3c4706ac 100644 --- a/packages/core/test/fixture/prompt-location.ts +++ b/packages/core/test/fixture/prompt-location.ts @@ -31,7 +31,7 @@ export const promptLocationNode = makeGlobalNode({ }), Layer.succeed(FSUtil.Service, fs), Layer.succeed(PluginSupervisor.Service, { - flush: Effect.void, + awaitActivation: Effect.void, }), Layer.mock(Reference.Service, { refresh: () => Effect.void }), ), diff --git a/packages/core/test/formatter.test.ts b/packages/core/test/formatter.test.ts index 2f4adf3b13e..e8ba3e4fd5b 100644 --- a/packages/core/test/formatter.test.ts +++ b/packages/core/test/formatter.test.ts @@ -44,7 +44,7 @@ function withFormatter( Effect.andThen( Effect.gen(function* () { const plugins = yield* PluginSupervisor.Service - yield* plugins.flush + yield* plugins.awaitActivation return yield* body(yield* Formatter.Service, directory) }).pipe( Effect.scoped, diff --git a/packages/core/test/instance-plugins.test.ts b/packages/core/test/instance-plugins.test.ts index 786a033afe7..f0a3606f241 100644 --- a/packages/core/test/instance-plugins.test.ts +++ b/packages/core/test/instance-plugins.test.ts @@ -69,7 +69,7 @@ describe("InstancePlugins", () => { const agents = (ref: Location.Ref) => Effect.gen(function* () { const supervisor = yield* PluginSupervisor.Service - yield* supervisor.flush + yield* supervisor.awaitActivation const service = yield* Agent.Service return { bound: yield* service.get(Agent.ID.make("thread-a-agent")), diff --git a/packages/core/test/instance-vanilla.test.ts b/packages/core/test/instance-vanilla.test.ts index a18d16934a9..e07a5c9055e 100644 --- a/packages/core/test/instance-vanilla.test.ts +++ b/packages/core/test/instance-vanilla.test.ts @@ -77,7 +77,7 @@ describe("Instance vanilla", () => { const read = (ref: Location.Ref) => Effect.gen(function* () { const supervisor = yield* PluginSupervisor.Service - yield* supervisor.flush + yield* supervisor.awaitActivation const config = yield* Config.Service const discovery = yield* InstructionDiscovery.Service const tools = yield* Tool.Service @@ -145,7 +145,7 @@ describe("Instance vanilla", () => { const ref = Location.Ref.make({ directory: AbsolutePath.make(directory) }) yield* Effect.gen(function* () { const supervisor = yield* PluginSupervisor.Service - yield* supervisor.flush + yield* supervisor.awaitActivation const config = yield* Config.Service // Only the pathless host-injected document; nothing file-backed. const entries = yield* config.entries() diff --git a/packages/core/test/location-layer.test.ts b/packages/core/test/location-layer.test.ts index 5d7de96afd4..4c166f94fb9 100644 --- a/packages/core/test/location-layer.test.ts +++ b/packages/core/test/location-layer.test.ts @@ -199,7 +199,7 @@ describe("LocationServiceMap", () => { const ref = Location.Ref.make({ directory: AbsolutePath.make(dir.path) }) const read = Effect.gen(function* () { const supervisor = yield* PluginSupervisor.Service - yield* supervisor.flush + yield* supervisor.awaitActivation const agents = yield* Agent.Service return yield* agents.get(id) }) @@ -233,14 +233,14 @@ describe("LocationServiceMap", () => { const context = yield* locations.contextEffect(Location.Ref.make({ directory: AbsolutePath.make(dir.path) })) yield* Deferred.await(started) - const flushFiber = yield* PluginSupervisor.Service.use((supervisor) => supervisor.flush).pipe( + const activationFiber = yield* PluginSupervisor.Service.use((supervisor) => supervisor.awaitActivation).pipe( Effect.provide(context), Effect.forkChild, ) - expect(flushFiber.pollUnsafe()).toBeUndefined() + expect(activationFiber.pollUnsafe()).toBeUndefined() yield* Deferred.succeed(release, undefined) - yield* Fiber.join(flushFiber) - yield* PluginSupervisor.Service.use((supervisor) => supervisor.flush).pipe( + yield* Fiber.join(activationFiber) + yield* PluginSupervisor.Service.use((supervisor) => supervisor.awaitActivation).pipe( Effect.provide(context), Effect.timeout("1 second"), ) @@ -281,7 +281,7 @@ describe("LocationServiceMap", () => { const context = yield* locations.contextEffect(Location.Ref.make({ directory: AbsolutePath.make(dir.path) })) yield* Deferred.await(firstStarted) - const flushFiber = yield* PluginSupervisor.Service.use((supervisor) => supervisor.flush).pipe( + const activationFiber = yield* PluginSupervisor.Service.use((supervisor) => supervisor.awaitActivation).pipe( Effect.provide(context), Effect.forkChild({ startImmediately: true }), ) @@ -296,10 +296,10 @@ describe("LocationServiceMap", () => { yield* Deferred.succeed(releaseFirst, undefined) yield* Deferred.await(secondStarted) - expect(flushFiber.pollUnsafe()).toBeUndefined() + expect(activationFiber.pollUnsafe()).toBeUndefined() yield* Deferred.succeed(releaseSecond, undefined) - yield* Fiber.join(flushFiber) + yield* Fiber.join(activationFiber) }), ), ), @@ -352,22 +352,22 @@ describe("LocationServiceMap", () => { ) yield* Fiber.join(updated) - const flushFiber = yield* PluginSupervisor.Service.use((supervisor) => supervisor.flush).pipe( + const activationFiber = yield* PluginSupervisor.Service.use((supervisor) => supervisor.awaitActivation).pipe( Effect.provide(context), Effect.forkChild, ) yield* Deferred.succeed(releaseFirst, undefined) yield* Deferred.await(secondStarted) - expect(flushFiber.pollUnsafe()).toBeUndefined() + expect(activationFiber.pollUnsafe()).toBeUndefined() yield* Deferred.succeed(releaseSecond, undefined) - yield* Fiber.join(flushFiber) + yield* Fiber.join(activationFiber) expect(activations.count).toBe(2) }), ), ), ) - itWithSdk.live("keeps flush pending while startup updates continue", () => + itWithSdk.live("keeps awaitActivation pending while startup updates continue", () => Effect.acquireRelease( Effect.promise(() => tmpdir()), (dir) => Effect.promise(() => dir[Symbol.asyncDispose]()), @@ -376,7 +376,7 @@ describe("LocationServiceMap", () => { Effect.gen(function* () { const locations = yield* LocationServiceMap.Service const context = yield* locations.contextEffect(Location.Ref.make({ directory: AbsolutePath.make(dir.path) })) - const flushFiber = yield* PluginSupervisor.Service.use((supervisor) => supervisor.flush).pipe( + const activationFiber = yield* PluginSupervisor.Service.use((supervisor) => supervisor.awaitActivation).pipe( Effect.provide(context), Effect.forkChild({ startImmediately: true }), ) @@ -387,8 +387,8 @@ describe("LocationServiceMap", () => { () => bus.publish(SdkPlugins.Updated, {}).pipe(Effect.andThen(Effect.sleep("50 millis"))), { discard: true }, ) - expect(flushFiber.pollUnsafe()).toBeUndefined() - yield* Fiber.join(flushFiber) + expect(activationFiber.pollUnsafe()).toBeUndefined() + yield* Fiber.join(activationFiber) }), ), ), @@ -412,7 +412,7 @@ describe("LocationServiceMap", () => { const locations = yield* LocationServiceMap.Service const context = yield* locations.contextEffect(Location.Ref.make({ directory: AbsolutePath.make(dir.path) })) - yield* PluginSupervisor.Service.use((supervisor) => supervisor.flush).pipe(Effect.provide(context)) + yield* PluginSupervisor.Service.use((supervisor) => supervisor.awaitActivation).pipe(Effect.provide(context)) expect(activations.count).toBe(1) yield* Bus.Service.use((bus) => bus.publish(Config.Event.Updated, {})).pipe(Effect.provide(context)) @@ -424,7 +424,7 @@ describe("LocationServiceMap", () => { ), ) - itWithSdk.live("keeps flush open while later hot reload runs", () => + itWithSdk.live("keeps awaitActivation pending while later hot reload runs", () => Effect.acquireRelease( Effect.promise(() => tmpdir()), (dir) => Effect.promise(() => dir[Symbol.asyncDispose]()), @@ -433,7 +433,7 @@ describe("LocationServiceMap", () => { Effect.gen(function* () { const locations = yield* LocationServiceMap.Service const context = yield* locations.contextEffect(Location.Ref.make({ directory: AbsolutePath.make(dir.path) })) - yield* PluginSupervisor.Service.use((supervisor) => supervisor.flush).pipe(Effect.provide(context)) + yield* PluginSupervisor.Service.use((supervisor) => supervisor.awaitActivation).pipe(Effect.provide(context)) const started = yield* Deferred.make() const release = yield* Deferred.make() @@ -451,20 +451,20 @@ describe("LocationServiceMap", () => { ) yield* Deferred.await(started) - const flushFiber = yield* PluginSupervisor.Service.use((supervisor) => supervisor.flush).pipe( + const activationFiber = yield* PluginSupervisor.Service.use((supervisor) => supervisor.awaitActivation).pipe( Effect.provide(context), Effect.forkChild({ startImmediately: true }), ) - expect(flushFiber.pollUnsafe()).toBeUndefined() + expect(activationFiber.pollUnsafe()).toBeUndefined() yield* Deferred.succeed(release, undefined) - yield* Fiber.join(flushFiber) + yield* Fiber.join(activationFiber) yield* Deferred.await(completed) }), ), ), ) - itWithSdk.live("does not cancel activation when a flush waiter is interrupted", () => + itWithSdk.live("does not cancel activation when an awaitActivation waiter is interrupted", () => Effect.acquireRelease( Effect.promise(() => tmpdir()), (dir) => Effect.promise(() => dir[Symbol.asyncDispose]()), @@ -489,15 +489,15 @@ describe("LocationServiceMap", () => { const locations = yield* LocationServiceMap.Service const context = yield* locations.contextEffect(Location.Ref.make({ directory: AbsolutePath.make(dir.path) })) yield* Deferred.await(started) - const flushFiber = yield* PluginSupervisor.Service.use((supervisor) => supervisor.flush).pipe( + const activationFiber = yield* PluginSupervisor.Service.use((supervisor) => supervisor.awaitActivation).pipe( Effect.provide(context), Effect.forkChild({ startImmediately: true }), ) - yield* Fiber.interrupt(flushFiber) + yield* Fiber.interrupt(activationFiber) yield* Deferred.succeed(release, undefined) yield* Deferred.await(completed) - yield* PluginSupervisor.Service.use((supervisor) => supervisor.flush).pipe( + yield* PluginSupervisor.Service.use((supervisor) => supervisor.awaitActivation).pipe( Effect.provide(context), Effect.timeout("500 millis"), ) @@ -519,7 +519,7 @@ describe("LocationServiceMap", () => { const plugins = yield* Effect.gen(function* () { const plugins = yield* Plugin.Service const supervisor = yield* PluginSupervisor.Service - yield* supervisor.flush + yield* supervisor.awaitActivation return yield* plugins.list() }).pipe( Effect.scoped, @@ -546,7 +546,7 @@ describe("LocationServiceMap", () => { yield* Effect.gen(function* () { const registry = yield* Plugin.Service const supervisor = yield* PluginSupervisor.Service - yield* supervisor.flush + yield* supervisor.awaitActivation expect((yield* registry.list()).map((plugin) => String(plugin.id))).toEqual(["opencode.agent"]) yield* Effect.promise(() => fs.writeFile(file, JSON.stringify({ plugins: ["-*", "opencode.command"] }))) @@ -710,7 +710,7 @@ describe("LocationServiceMap", () => { const catalog = yield* Catalog.Service yield* catalog.transform((editor) => editor.provider.update(providerID, () => {})) const supervisor = yield* PluginSupervisor.Service - yield* supervisor.flush + yield* supervisor.awaitActivation const registry = yield* Tool.Service return { providers: yield* catalog.provider.all(), @@ -992,7 +992,7 @@ describe("LocationServiceMap", () => { yield* Effect.gen(function* () { const supervisor = yield* PluginSupervisor.Service const mcp = yield* Mcp.Service - yield* supervisor.flush + yield* supervisor.awaitActivation expect(observed.example).toBe(false) yield* mcp.add("dynamic", { type: "remote", diff --git a/packages/core/test/session-generate.test.ts b/packages/core/test/session-generate.test.ts index e6fc2889d93..fbde4fae8db 100644 --- a/packages/core/test/session-generate.test.ts +++ b/packages/core/test/session-generate.test.ts @@ -111,7 +111,7 @@ const discovery = Layer.mock(InstructionDiscovery.Service, { const skills = Layer.mock(SkillInstructions.Service, { load: () => Effect.succeed(Instructions.empty) }) const references = Layer.mock(ReferenceInstructions.Service, { load: () => Effect.succeed(Instructions.empty) }) const mcp = Layer.mock(McpInstructions.Service, { load: () => Effect.succeed(Instructions.empty) }) -const plugins = Layer.mock(PluginSupervisor.Service, { flush: Effect.void }) +const plugins = Layer.mock(PluginSupervisor.Service, { awaitActivation: Effect.void }) const tools = Layer.mock(Tool.Service, { snapshot: () => Effect.succeed({ diff --git a/packages/core/test/session-owned.test.ts b/packages/core/test/session-owned.test.ts index da18ebe03f1..0717aa01562 100644 --- a/packages/core/test/session-owned.test.ts +++ b/packages/core/test/session-owned.test.ts @@ -95,7 +95,7 @@ const setup = Effect.fnUntraced(function* (options?: { ) const hooks = yield* PluginHooks.Service.pipe(Effect.provide(LayerNode.compile(PluginHooks.node))) const locations: Location.Ref[] = [] - const flushes: Location.Ref[] = [] + const activationWaits: Location.Ref[] = [] const resumes: SessionSchema.ID[] = [] const wakes: Array<{ sessionID: SessionSchema.ID; pending: SessionMessage.ID[]; enqueued: number }> = [] const execution = SessionExecution.Service.of({ @@ -143,8 +143,8 @@ const setup = Effect.fnUntraced(function* (options?: { }), options?.snapshot?.(ref) ?? Layer.mock(Snapshot.Service, {}), Layer.succeed(PluginSupervisor.Service, { - flush: Effect.sync(() => { - flushes.push(ref) + awaitActivation: Effect.sync(() => { + activationWaits.push(ref) }), }), ), @@ -175,7 +175,7 @@ const setup = Effect.fnUntraced(function* (options?: { }), Effect.provideService(SessionExecution.Service, options?.execution ?? execution), ) - return { sessions, hooks, locations, flushes, resumes, wakes, db: database.db, bus, store } + return { sessions, hooks, locations, activationWaits, resumes, wakes, db: database.db, bus, store } }) describe("Session-owned handles", () => { @@ -252,7 +252,7 @@ describe("Session-owned handles", () => { const calls: string[] = [] yield* fixture.hooks.register("session", "prompt", (event) => Effect.sync(() => { - expect(fixture.flushes).toEqual([source]) + expect(fixture.activationWaits).toEqual([source]) calls.push(event.prompt.text) event.prompt.text += " prepared" }), @@ -277,7 +277,7 @@ describe("Session-owned handles", () => { ) expect(calls).toEqual(["Original"]) expect(fixture.locations).toEqual([source]) - expect(fixture.flushes).toEqual([source]) + expect(fixture.activationWaits).toEqual([source]) expect(fixture.wakes).toEqual([ { sessionID, pending: [synthetic.id, first.id], enqueued: 2 }, { sessionID, pending: [synthetic.id, first.id], enqueued: 2 }, @@ -363,7 +363,7 @@ describe("Session-owned handles", () => { expect(fixture.locations).toEqual([source]) yield* prompt expect(fixture.locations).toEqual([source, destination]) - expect(fixture.flushes).toEqual([source, destination]) + expect(fixture.activationWaits).toEqual([source, destination]) expect((yield* fixture.sessions.forSession(otherID).get()).location).toEqual(source) }), ) @@ -408,7 +408,7 @@ describe("Session-owned handles", () => { expect( events.filter((event) => event.type === SessionEvent.Skill.Activated.type).map((event) => event.location), ).toEqual([undefined, undefined, source]) - expect(fixture.flushes).toEqual([]) + expect(fixture.activationWaits).toEqual([]) expect(fixture.resumes).toEqual([]) expect(fixture.wakes).toEqual([]) }), @@ -442,7 +442,7 @@ describe("Session-owned handles", () => { expect(yield* handle.get()).toEqual(before) expect(yield* handle.inbox()).toEqual([]) expect(yield* fixture.store.context(sessionID)).toEqual([]) - expect(fixture.flushes).toEqual([]) + expect(fixture.activationWaits).toEqual([]) expect(fixture.resumes).toEqual([]) expect(fixture.wakes).toEqual([]) }), @@ -776,7 +776,7 @@ describe("Session-owned handles", () => { expect(captures).toEqual([source, destination]) expect(fixture.locations).toEqual([source, destination, destination]) - expect(fixture.flushes).toEqual([source, destination, destination]) + expect(fixture.activationWaits).toEqual([source, destination, destination]) expect((yield* handle.get()).revert).toBeUndefined() }), ) @@ -800,7 +800,7 @@ describe("SessionPrompt construction", () => { Layer.mergeAll( Layer.succeed(PluginHooks.Service, fixture.hooks), Layer.succeed(PluginSupervisor.Service, { - flush: Effect.sync(() => { + awaitActivation: Effect.sync(() => { calls.push("ready") }), }), @@ -842,8 +842,8 @@ describe("SessionRevert construction", () => { Effect.provide( Layer.merge( Layer.succeed(PluginSupervisor.Service, { - flush: Effect.sync(() => { - calls.push("flush") + awaitActivation: Effect.sync(() => { + calls.push("awaitActivation") }), }), Layer.mock(Snapshot.Service, { @@ -871,7 +871,7 @@ describe("SessionRevert construction", () => { yield* revert .stage({ session, messageID: boundary.id, files: false }) .pipe(Effect.satisfiesServicesType(), Effect.provide(unrelated)) - expect(calls).toEqual(["flush", "capture", "capture", "diff"]) + expect(calls).toEqual(["awaitActivation", "capture", "capture", "diff"]) const staged = yield* handle.get() expect(staged.revert?.snapshot).toBe(Snapshot.ID.make("captured-tree")) @@ -879,7 +879,15 @@ describe("SessionRevert construction", () => { const cleared = yield* handle.get() expect(cleared.revert).toBeUndefined() yield* revert.clear(cleared).pipe(Effect.satisfiesServicesType(), Effect.provide(unrelated)) - expect(calls).toEqual(["flush", "capture", "capture", "diff", "flush", "restore", "flush"]) + expect(calls).toEqual([ + "awaitActivation", + "capture", + "capture", + "diff", + "awaitActivation", + "restore", + "awaitActivation", + ]) }), ) }) diff --git a/packages/core/test/session-prompt-hooks.test.ts b/packages/core/test/session-prompt-hooks.test.ts index 27193f2a2cd..59e6950be17 100644 --- a/packages/core/test/session-prompt-hooks.test.ts +++ b/packages/core/test/session-prompt-hooks.test.ts @@ -60,7 +60,7 @@ const setup = Effect.gen(function* () { const services = locations.get(session.location) const hooks = yield* Effect.gen(function* () { const plugins = yield* PluginSupervisor.Service - yield* plugins.flush + yield* plugins.awaitActivation return yield* PluginHooks.Service }).pipe(Effect.provide(services)) return { sessions, session, hooks, services } diff --git a/packages/core/test/session-prompt.test.ts b/packages/core/test/session-prompt.test.ts index 21aff7005c6..36290e41ebf 100644 --- a/packages/core/test/session-prompt.test.ts +++ b/packages/core/test/session-prompt.test.ts @@ -115,7 +115,7 @@ const locations = (references: Layer.Layer) => Layer.succeed( PluginSupervisor.Service, PluginSupervisor.Service.of({ - flush: Effect.sync(() => (ready = true)), + awaitActivation: Effect.sync(() => (ready = true)), }), ), ), diff --git a/packages/core/test/session-revert.test.ts b/packages/core/test/session-revert.test.ts index 9080771aa9a..e8ba1c5029f 100644 --- a/packages/core/test/session-revert.test.ts +++ b/packages/core/test/session-revert.test.ts @@ -67,7 +67,7 @@ describe("Session.revert files", () => { yield* Effect.gen(function* () { const plugins = yield* PluginSupervisor.Service - yield* plugins.flush + yield* plugins.awaitActivation const snapshot = yield* Snapshot.Service const before = yield* snapshot.capture() if (!before) throw new Error("Initial snapshot missing") diff --git a/packages/core/test/session-runner-recorded.test.ts b/packages/core/test/session-runner-recorded.test.ts index dd8e05825c0..2bda7afcc11 100644 --- a/packages/core/test/session-runner-recorded.test.ts +++ b/packages/core/test/session-runner-recorded.test.ts @@ -85,7 +85,10 @@ const referenceInstructions = Layer.mock(ReferenceInstructions.Service, { }) const mcpInstructions = Layer.mock(McpInstructions.Service, { load: () => Effect.succeed(Instructions.empty) }) const config = Config.testLayer() -const pluginSupervisor = Layer.succeed(PluginSupervisor.Service, PluginSupervisor.Service.of({ flush: Effect.void })) +const pluginSupervisor = Layer.succeed( + PluginSupervisor.Service, + PluginSupervisor.Service.of({ awaitActivation: Effect.void }), +) const promptCatalog = Layer.mock(Catalog.Service, { provider: { get: () => Effect.undefined, diff --git a/packages/core/test/session-runner.test.ts b/packages/core/test/session-runner.test.ts index c5a2bf30ad1..8bd2304bc05 100644 --- a/packages/core/test/session-runner.test.ts +++ b/packages/core/test/session-runner.test.ts @@ -206,7 +206,7 @@ const makeRunnerState = () => { systemUnavailable: false, systemLoadHook: Effect.void, skillBaselines: new Map(), - pluginFlushHook: Effect.void, + pluginActivationHook: Effect.void, authorizations: new Array(), executions: new Array(), closedTransports: new Array(), @@ -390,7 +390,7 @@ const layer = Layer.unwrap( const pluginSupervisor = Layer.succeed( PluginSupervisor.Service, PluginSupervisor.Service.of({ - flush: Effect.suspend(() => state.pluginFlushHook), + awaitActivation: Effect.suspend(() => state.pluginActivationHook), }), ) const promptCatalog = Layer.mock(Catalog.Service, { @@ -1840,7 +1840,7 @@ describe("SessionRunnerLLM", () => { scenario("waits for initial plugin readiness before constructing the model request", function* (s) { const release = yield* Deferred.make() - s.pluginFlushHook = Deferred.await(release) + s.pluginActivationHook = Deferred.await(release) yield* s.session.prompt({ sessionID, text: "Wait for plugins", resume: false }) s.requests.length = 0 diff --git a/packages/core/test/session-skill.test.ts b/packages/core/test/session-skill.test.ts index 4ff1c2051ce..4f7b752a3b6 100644 --- a/packages/core/test/session-skill.test.ts +++ b/packages/core/test/session-skill.test.ts @@ -53,7 +53,7 @@ const locations = makeGlobalNode({ list: () => Effect.succeed([info]), }), Layer.succeed(PluginSupervisor.Service, { - flush: Effect.void, + awaitActivation: Effect.void, }), Layer.mock(Reference.Service, { refresh: () => Effect.void }), ), diff --git a/packages/core/test/session-title.test.ts b/packages/core/test/session-title.test.ts index 8f508d1bb4b..31cc90ea11c 100644 --- a/packages/core/test/session-title.test.ts +++ b/packages/core/test/session-title.test.ts @@ -130,7 +130,7 @@ const it = testEffect( Catalog.node.replace(catalog), SessionRunnerModel.node.replace(models), Location.node.replace(Location.boundNode({ directory: AbsolutePath.make("/project") })), - PluginSupervisor.node.replace(Layer.mock(PluginSupervisor.Service, { flush: Effect.void })), + PluginSupervisor.node.replace(Layer.mock(PluginSupervisor.Service, { awaitActivation: Effect.void })), ], ), ) diff --git a/packages/core/test/tool-shell.test.ts b/packages/core/test/tool-shell.test.ts index 779b0353a6a..0733a34680e 100644 --- a/packages/core/test/tool-shell.test.ts +++ b/packages/core/test/tool-shell.test.ts @@ -129,7 +129,7 @@ const shellPluginSupervisor = makeLocationNode({ service: PluginSupervisor.Service, layer: Layer.effect( PluginSupervisor.Service, - registerToolPlugin(ShellTool.Plugin).pipe(Effect.as(PluginSupervisor.Service.of({ flush: Effect.void }))), + registerToolPlugin(ShellTool.Plugin).pipe(Effect.as(PluginSupervisor.Service.of({ awaitActivation: Effect.void }))), ), deps: [ Config.node, @@ -220,7 +220,7 @@ const withSession = (directory: string, body: (registry: Tool.Interface const locationLayer = locations.get(location) return yield* Effect.gen(function* () { const plugins = yield* PluginSupervisor.Service - yield* plugins.flush + yield* plugins.awaitActivation const registry = yield* Tool.Service return yield* body(registry) }).pipe(Effect.provide(locationLayer), Effect.ensuring(locations.invalidate(location))) diff --git a/packages/core/test/tool-subagent.test.ts b/packages/core/test/tool-subagent.test.ts index 460b800cede..962473a479f 100644 --- a/packages/core/test/tool-subagent.test.ts +++ b/packages/core/test/tool-subagent.test.ts @@ -107,7 +107,9 @@ const subagentPluginSupervisor = makeLocationNode({ service: PluginSupervisor.Service, layer: Layer.effect( PluginSupervisor.Service, - registerToolPlugin(SubagentTool.Plugin).pipe(Effect.as(PluginSupervisor.Service.of({ flush: Effect.void }))), + registerToolPlugin(SubagentTool.Plugin).pipe( + Effect.as(PluginSupervisor.Service.of({ awaitActivation: Effect.void })), + ), ), deps: [Agent.node, Config.node, Permission.node, PluginRuntime.node, Tool.node], }) @@ -155,7 +157,9 @@ const completionIt = testEffect( const withSubagent = (location: Location.Ref) => Effect.gen(function* () { const locations = yield* LocationServiceMap.Service - yield* PluginSupervisor.Service.use((supervisor) => supervisor.flush).pipe(Effect.provide(locations.get(location))) + yield* PluginSupervisor.Service.use((supervisor) => supervisor.awaitActivation).pipe( + Effect.provide(locations.get(location)), + ) yield* Agent.Service.use((agents) => agents.transform((draft) => { // The caller identity used by executeTool; subagent permission asserts against it. diff --git a/packages/sdk/test/embedded.test.ts b/packages/sdk/test/embedded.test.ts index 2a7f7a13d88..e1f687b400d 100644 --- a/packages/sdk/test/embedded.test.ts +++ b/packages/sdk/test/embedded.test.ts @@ -48,7 +48,7 @@ for (const selection of ["explicit", "default"] as const) { PluginSupervisor.Service, Effect.gen(function* () { const plugins = yield* PluginSupervisor.Service - return { flush: release.open.pipe(Effect.andThen(plugins.flush)) } + return { awaitActivation: release.open.pipe(Effect.andThen(plugins.awaitActivation)) } }), ).pipe(Layer.provide(layer)), ) diff --git a/packages/server/src/handlers/plugin.ts b/packages/server/src/handlers/plugin.ts index 116b5bb59fb..afa45afe2ce 100644 --- a/packages/server/src/handlers/plugin.ts +++ b/packages/server/src/handlers/plugin.ts @@ -59,7 +59,7 @@ export const PluginHandler = HttpApiBuilder.group(Api, "server.plugin", (handler .handle("plugin.update", (ctx) => Effect.gen(function* () { const supervisor = yield* PluginSupervisor.Service - yield* supervisor.flush + yield* supervisor.awaitActivation const plugins = yield* Plugin.Service if ( !(yield* plugins.list()).some( diff --git a/packages/server/src/handlers/rpc.ts b/packages/server/src/handlers/rpc.ts index f6a80088171..4cd2dca26e6 100644 --- a/packages/server/src/handlers/rpc.ts +++ b/packages/server/src/handlers/rpc.ts @@ -9,7 +9,7 @@ export const RpcHandler = HttpApiBuilder.group(Api, "server.rpc", (handlers) => handlers.handle("rpc.call", ({ params, payload }) => Effect.gen(function* () { const supervisor = yield* PluginSupervisor.Service - yield* supervisor.flush + yield* supervisor.awaitActivation const rpc = yield* Rpc.Service const output = yield* rpc.call(params.rpcID, params.method, payload.input) return output === undefined ? {} : { output } diff --git a/packages/server/test/rpc.test.ts b/packages/server/test/rpc.test.ts index 33b2b0b736c..d314b8c0654 100644 --- a/packages/server/test/rpc.test.ts +++ b/packages/server/test/rpc.test.ts @@ -48,7 +48,7 @@ const fixture = Effect.fn(function* (plugins: readonly Plugin.Plugin[]) { boot: (directory: string) => Effect.gen(function* () { const supervisor = yield* PluginSupervisor.Service - yield* supervisor.flush + yield* supervisor.awaitActivation }).pipe(Effect.provide(locations.get(Location.Ref.make({ directory: AbsolutePath.make(directory) })))), call: ( route: string, diff --git a/packages/simulation/test/simulated-provider.test.ts b/packages/simulation/test/simulated-provider.test.ts index c6b17038a28..5f5fdb4223e 100644 --- a/packages/simulation/test/simulated-provider.test.ts +++ b/packages/simulation/test/simulated-provider.test.ts @@ -250,7 +250,7 @@ test("controls arbitrary tools through scoped SDK overlays", async () => { Layer.build(locations.get(Location.Ref.make({ directory: AbsolutePath.make(secondDirectory) }))), ]) yield* Effect.forEach([primary, secondary], (context) => - PluginSupervisor.Service.use((supervisor) => supervisor.flush).pipe(Effect.provide(context)), + PluginSupervisor.Service.use((supervisor) => supervisor.awaitActivation).pipe(Effect.provide(context)), ) expect(activations).toBe(2)