diff --git a/src/bundled/pi/index.test.ts b/src/bundled/pi/index.test.ts new file mode 100644 index 0000000000..f3eeff49c7 --- /dev/null +++ b/src/bundled/pi/index.test.ts @@ -0,0 +1,23 @@ +// @vitest-environment jsdom +import { expect, it, vi } from "vitest"; +import { appliedType } from "../../features/agents/plugin-testing"; +import * as pi from "./index"; + +vi.mock("@tauri-apps/api/core", () => ({ isTauri: () => true })); + +it("shows the Pi type's screens from the runtime its agent entry registers", () => { + // The UI needs no host; only the runtime starts Pi. + expect(pi.inject).toEqual(["agents"]); + expect(pi.agent.inject).toEqual(["agents", "host"]); + const type = appliedType(pi, { + pluginOwner: { id: "buzz.pi", revision: "r1" }, + host: { request: vi.fn() }, + agents: { relay: () => undefined }, + effect: vi.fn(), + }); + expect(type).toMatchObject({ id: "pi", title: "Pi", timeoutMs: 120_000 }); + expect(type?.Create).toBeTypeOf("function"); + expect(type?.CreateModel).toBeTypeOf("function"); + expect(type?.icon).toBeDefined(); + expect(type?.tabs?.map((tab) => tab.id)).toEqual(["settings", "activity"]); +}); diff --git a/src/bundled/pi/index.ts b/src/bundled/pi/index.ts index 6449c7e243..b816450be9 100644 --- a/src/bundled/pi/index.ts +++ b/src/bundled/pi/index.ts @@ -16,9 +16,45 @@ export const TYPE_ID = "pi"; * which includes the first install of Pi's packages. */ const HANDOVER_MS = 2 * 60_000; -export const inject = ["agents", "host"]; +/** What the type's UI reaches of its runtime. */ +type PiExposed = Parameters[0]; + +export const inject = ["agents"]; export const apply: PluginModule["apply"] = (ctx) => { // Only the desktop app can start the Pi host; no unusable type elsewhere. + if (!isTauri()) return; + ctx.agents.present(TYPE_ID, (exposed) => { + const { ActivityTab, Create, CreateModel, SettingsTab } = + createTabs(exposed); + return { + icon: PiLogoIcon, + Create, + CreateModel, + tabs: [ + { + id: "settings", + title: "Settings", + role: "settings", + splitSettings: true, + component: SettingsTab, + }, + { + id: "activity", + title: "Activity", + role: "activity", + component: ActivityTab, + }, + ], + }; + }); +}; + +// The runtime, which runs where the agents do. +export const agent: NonNullable = { + inject: ["agents", "host"], + apply: (ctx) => applyAgent(ctx), +}; +function applyAgent(ctx: Parameters[0]) { if (!isTauri()) return; const { agents, host } = ctx; const typeKey = `${ctx.pluginOwner?.id ?? "buzz.pi"}/${TYPE_ID}`; @@ -30,10 +66,6 @@ export const apply: PluginModule["apply"] = (ctx) => { repositoriesContext, (key) => agents.handle(key), ); - const { ActivityTab, Create, CreateModel, SettingsTab } = createTabs({ - setup: runtime.setup, - runtime, - }); const sync = () => { const snapshot = agents.snapshot(); if (snapshot.status === "loading") return; @@ -51,12 +83,10 @@ export const apply: PluginModule["apply"] = (ctx) => { agents.register({ id: TYPE_ID, title: "Pi", - icon: PiLogoIcon, description: "Pi on this computer, with any provider Pi supports. Each thread it is mentioned in is one continuing conversation.", defaults: () => ({ config: DEFAULT_CONFIG }), - Create, - CreateModel, + expose: { setup: runtime.setup, runtime } satisfies PiExposed, adopt: (carry, saved) => ({ ...saved, instructions: carry.instructions ?? saved.instructions, @@ -81,21 +111,6 @@ export const apply: PluginModule["apply"] = (ctx) => { list: (key) => runtime.sessions(key), subscribe: (listener) => runtime.subscribe(listener), }, - tabs: [ - { - id: "settings", - title: "Settings", - role: "settings", - splitSettings: true, - component: SettingsTab, - }, - { - id: "activity", - title: "Activity", - role: "activity", - component: ActivityTab, - }, - ], sessionControls: { scope: (agent) => config(agent.config).scope, state: (agent, place) => runtime.sessionState(agent, place), @@ -107,4 +122,4 @@ export const apply: PluginModule["apply"] = (ctx) => { runOnce: (turn) => runtime.runOnce(turn), timeoutMs: HANDOVER_MS, }); -}; +} diff --git a/src/bundled/pi/tabs.tsx b/src/bundled/pi/tabs.tsx index aabc413304..cc4c24075b 100644 --- a/src/bundled/pi/tabs.tsx +++ b/src/bundled/pi/tabs.tsx @@ -182,7 +182,7 @@ export function createTabs({ setup, runtime, }: Readonly<{ - setup: PiSetup; + setup: Pick; runtime: Pick; }>) { /** The provider, its key and its model, and a test that they answer. As