summaryrefslogtreecommitdiffhomepage
path: root/packages/kernel/src/host
diff options
context:
space:
mode:
Diffstat (limited to 'packages/kernel/src/host')
-rw-r--r--packages/kernel/src/host/host.test.ts63
-rw-r--r--packages/kernel/src/host/host.ts10
2 files changed, 72 insertions, 1 deletions
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<TPayload>(hook: EventHookDescriptor<TPayload>, handler: EventHandler<TPayload>) {
@@ -201,5 +206,8 @@ export function createHost(extensions: readonly Extension[], deps: HostDeps): Ho
getDisabled() {
return disabled;
},
+ getHostAPI() {
+ return buildHostAPI({ registrationClosed: true });
+ },
};
}