From 977ca522736bba53172e010494de5ac59fdb2a4a Mon Sep 17 00:00:00 2001 From: Adam Malczewski Date: Fri, 5 Jun 2026 01:22:21 +0900 Subject: refactor(host): expose getHostAPI(); host-bin drops duplicate adapter; storage-sqlite manifest honesty MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit host CR-1: createHost.getHostAPI() returns the canonical post-activation HostAPI (registration closed) via a single builder — host-bin deletes its buildPostActivationHostAPI duplicate and calls host.getHostAPI(). storage-sqlite CR-2: remove false contributes.services:["storage"] (backend is a kernel bootstrap dep injected as HostDeps.storageFactory, not a bus service); document the intentional no-op activate. typecheck clean, 218 tests pass, biome clean; live boot + curl verified. --- packages/kernel/src/host/host.test.ts | 63 +++++++++++++++++++++++++++++++++++ packages/kernel/src/host/host.ts | 10 +++++- 2 files changed, 72 insertions(+), 1 deletion(-) (limited to 'packages/kernel') diff --git a/packages/kernel/src/host/host.test.ts b/packages/kernel/src/host/host.test.ts index 82d5177..11c2356 100644 --- a/packages/kernel/src/host/host.test.ts +++ b/packages/kernel/src/host/host.test.ts @@ -687,4 +687,67 @@ describe("createHost", () => { expect(host.getDisabled()).toHaveLength(0); }); }); + + describe("getHostAPI", () => { + it("returns a HostAPI whose read-views reflect registrations from activation", async () => { + const tool = createFakeTool("read-file"); + const provider = createFakeProvider("anthropic"); + const auth = createFakeAuth("apikey"); + + const ext = createExtension("multi-ext", { + activate: (host) => { + host.defineTool(tool); + host.defineProvider(provider); + host.defineAuth(auth); + }, + }); + + const host = createHost([ext], deps); + await host.activate(); + + const api = host.getHostAPI(); + + expect(api.getTools().size).toBe(1); + expect(api.getTools().get("read-file")).toBe(tool); + + expect(api.getProviders().size).toBe(1); + expect(api.getProviders().get("anthropic")).toBe(provider); + + expect(api.getAuthProviders().size).toBe(1); + expect(api.getAuthProvider("apikey")).toBe(auth); + }); + + it("throws on defineTool after activation", async () => { + const ext = createExtension("ext", { activate: () => {} }); + const host = createHost([ext], deps); + await host.activate(); + + const api = host.getHostAPI(); + expect(() => api.defineTool(createFakeTool("late"))).toThrow( + "Registration not available after activation", + ); + }); + + it("throws on defineProvider after activation", async () => { + const ext = createExtension("ext", { activate: () => {} }); + const host = createHost([ext], deps); + await host.activate(); + + const api = host.getHostAPI(); + expect(() => api.defineProvider(createFakeProvider("late"))).toThrow( + "Registration not available after activation", + ); + }); + + it("throws on defineAuth after activation", async () => { + const ext = createExtension("ext", { activate: () => {} }); + const host = createHost([ext], deps); + await host.activate(); + + const api = host.getHostAPI(); + expect(() => api.defineAuth(createFakeAuth("late"))).toThrow( + "Registration not available after activation", + ); + }); + }); }); diff --git a/packages/kernel/src/host/host.ts b/packages/kernel/src/host/host.ts index 8592a16..dd61f9f 100644 --- a/packages/kernel/src/host/host.ts +++ b/packages/kernel/src/host/host.ts @@ -54,6 +54,7 @@ export interface Host { readonly getScheduledJobs: () => readonly ScheduledJob[]; readonly getMigrations: () => readonly string[]; readonly getDisabled: () => readonly DisabledExtension[]; + readonly getHostAPI: () => HostAPI; } export function createHost(extensions: readonly Extension[], deps: HostDeps): Host { @@ -95,15 +96,19 @@ export function createHost(extensions: readonly Extension[], deps: HostDeps): Ho } } - function buildHostAPI(): HostAPI { + function buildHostAPI(opts?: { readonly registrationClosed?: boolean }): HostAPI { + const closed = opts?.registrationClosed ?? false; return { defineTool(tool: ToolContract) { + if (closed) throw new Error("Registration not available after activation"); tools.set(tool.name, tool); }, defineProvider(provider: ProviderContract) { + if (closed) throw new Error("Registration not available after activation"); providers.set(provider.id, provider); }, defineAuth(auth: AuthContract) { + if (closed) throw new Error("Registration not available after activation"); authProviders.set(auth.id, auth); }, on(hook: EventHookDescriptor, handler: EventHandler) { @@ -201,5 +206,8 @@ export function createHost(extensions: readonly Extension[], deps: HostDeps): Ho getDisabled() { return disabled; }, + getHostAPI() { + return buildHostAPI({ registrationClosed: true }); + }, }; } -- cgit v1.2.3