summaryrefslogtreecommitdiffhomepage
path: root/packages/kernel
diff options
context:
space:
mode:
authorAdam Malczewski <[email protected]>2026-06-04 23:55:29 +0900
committerAdam Malczewski <[email protected]>2026-06-04 23:55:29 +0900
commite56b591d56ea3f3007c612da2f0fe2004d696f45 (patch)
tree01a3503b5c72cd78971d8dc304d80835d76201cf /packages/kernel
parent51a88160fbb8e06d93f7507b5e3e4cca80e98902 (diff)
downloaddispatch-e56b591d56ea3f3007c612da2f0fe2004d696f45.tar.gz
dispatch-e56b591d56ea3f3007c612da2f0fe2004d696f45.zip
fix(kernel): expose getProviders/getTools on HostAPI (CR-2) + runTurn uses input tabId/turnId (CR-3); simplify orchestrator wiring (167 tests)
Diffstat (limited to 'packages/kernel')
-rw-r--r--packages/kernel/src/contracts/extension.ts6
-rw-r--r--packages/kernel/src/host/host.test.ts50
-rw-r--r--packages/kernel/src/host/host.ts6
-rw-r--r--packages/kernel/src/runtime/run-turn.test.ts30
-rw-r--r--packages/kernel/src/runtime/run-turn.ts4
5 files changed, 94 insertions, 2 deletions
diff --git a/packages/kernel/src/contracts/extension.ts b/packages/kernel/src/contracts/extension.ts
index ac252b4..7078a9a 100644
--- a/packages/kernel/src/contracts/extension.ts
+++ b/packages/kernel/src/contracts/extension.ts
@@ -224,6 +224,12 @@ export interface HostAPI {
/** Logger — always available, even before other extensions activate. */
readonly logger: Logger;
+ /** Read-only view of all registered providers. */
+ readonly getProviders: () => ReadonlyMap<string, ProviderContract>;
+
+ /** Read-only view of all registered tools. */
+ readonly getTools: () => ReadonlyMap<string, ToolContract>;
+
/** Register a scheduled job with the host's scheduler. */
readonly scheduler: {
readonly register: (job: ScheduledJob) => void;
diff --git a/packages/kernel/src/host/host.test.ts b/packages/kernel/src/host/host.test.ts
index 862854e..f7b7eba 100644
--- a/packages/kernel/src/host/host.test.ts
+++ b/packages/kernel/src/host/host.test.ts
@@ -584,6 +584,56 @@ describe("createHost", () => {
});
});
+ describe("HostAPI registry access", () => {
+ it("getTools returns registered tools via HostAPI", async () => {
+ const tool = createFakeTool("read-file");
+ let capturedTools: ReadonlyMap<string, ToolContract> | undefined;
+
+ const producer = createExtension("tools-fs", {
+ activate: (host) => {
+ host.defineTool(tool);
+ },
+ });
+ const consumer = createExtension("consumer", {
+ dependsOn: ["tools-fs"],
+ activate: (host) => {
+ capturedTools = host.getTools();
+ },
+ });
+
+ const host = createHost([producer, consumer], deps);
+ await host.activate();
+
+ expect(capturedTools).toBeDefined();
+ expect(capturedTools?.size).toBe(1);
+ expect(capturedTools?.get("read-file")).toBe(tool);
+ });
+
+ it("getProviders returns registered providers via HostAPI", async () => {
+ const provider = createFakeProvider("anthropic");
+ let capturedProviders: ReadonlyMap<string, ProviderContract> | undefined;
+
+ const producer = createExtension("provider-anthropic", {
+ activate: (host) => {
+ host.defineProvider(provider);
+ },
+ });
+ const consumer = createExtension("consumer", {
+ dependsOn: ["provider-anthropic"],
+ activate: (host) => {
+ capturedProviders = host.getProviders();
+ },
+ });
+
+ const host = createHost([producer, consumer], deps);
+ await host.activate();
+
+ expect(capturedProviders).toBeDefined();
+ expect(capturedProviders?.size).toBe(1);
+ expect(capturedProviders?.get("anthropic")).toBe(provider);
+ });
+ });
+
describe("DAG errors", () => {
it("throws on missing dependency", () => {
const ext = createExtension("a", { dependsOn: ["missing"] });
diff --git a/packages/kernel/src/host/host.ts b/packages/kernel/src/host/host.ts
index 4ffef20..e0ca5d5 100644
--- a/packages/kernel/src/host/host.ts
+++ b/packages/kernel/src/host/host.ts
@@ -126,6 +126,12 @@ export function createHost(extensions: readonly Extension[], deps: HostDeps): Ho
permissions: deps.permissions,
events: deps.events,
logger: deps.logger,
+ getProviders() {
+ return providers;
+ },
+ getTools() {
+ return tools;
+ },
scheduler: {
register(job: ScheduledJob) {
scheduledJobs.push(job);
diff --git a/packages/kernel/src/runtime/run-turn.test.ts b/packages/kernel/src/runtime/run-turn.test.ts
index f63a0a8..1ea6406 100644
--- a/packages/kernel/src/runtime/run-turn.test.ts
+++ b/packages/kernel/src/runtime/run-turn.test.ts
@@ -52,6 +52,36 @@ const userMessage: ChatMessage = {
};
describe("runTurn", () => {
+ it("emits events with the tabId and turnId from input", async () => {
+ const provider = createFakeProvider([
+ [
+ { type: "text-delta", delta: "hi" },
+ { type: "usage", usage: { inputTokens: 1, outputTokens: 1 } },
+ { type: "finish", reason: "stop" },
+ ],
+ ]);
+
+ const { events, emit } = createCollectingEmit();
+
+ await runTurn({
+ provider,
+ messages: [userMessage],
+ tools: [],
+ dispatch: { maxConcurrent: 1, eager: false },
+ tabId: "conv-42",
+ turnId: "turn-99",
+ emit,
+ });
+
+ expect(events.length).toBeGreaterThan(0);
+ for (const event of events) {
+ expect(event.tabId).toBe("conv-42");
+ if (event.type !== "status") {
+ expect(event.turnId).toBe("turn-99");
+ }
+ }
+ });
+
it("text-only turn emits correct events and returns correct result", async () => {
const provider = createFakeProvider([
[
diff --git a/packages/kernel/src/runtime/run-turn.ts b/packages/kernel/src/runtime/run-turn.ts
index 46b1465..919491a 100644
--- a/packages/kernel/src/runtime/run-turn.ts
+++ b/packages/kernel/src/runtime/run-turn.ts
@@ -217,8 +217,8 @@ export async function runTurn(input: RunTurnInput): Promise<RunTurnResult> {
toolMap.set(tool.name, tool);
}
- const tabId = "";
- const turnId = "";
+ const tabId = input.tabId;
+ const turnId = input.turnId;
const signal = input.signal ?? new AbortController().signal;
for (let step = 0; step < MAX_STEPS; step++) {