diff --git a/orchestrator-v2/packages/agent-3-task/package.json b/orchestrator-v2/packages/agent-3-task/package.json index 541b6cd..c08848a 100644 --- a/orchestrator-v2/packages/agent-3-task/package.json +++ b/orchestrator-v2/packages/agent-3-task/package.json @@ -9,6 +9,7 @@ "type": "module", "exports": { ".": "./dist/index.mjs", + "./augment": "./dist/augment.mjs", "./package.json": "./package.json" }, "publishConfig": { @@ -23,7 +24,8 @@ }, "dependencies": { "@bifrost-ai/engine": "workspace:*", - "@bifrost-ai/interfaces-work": "workspace:*" + "@bifrost-ai/interfaces-work": "workspace:*", + "@bifrost-ai/runner": "workspace:*" }, "devDependencies": { "@types/node": "catalog:", diff --git a/orchestrator-v2/packages/agent-3-task/src/augment.spec.ts b/orchestrator-v2/packages/agent-3-task/src/augment.spec.ts new file mode 100644 index 0000000..04d4982 --- /dev/null +++ b/orchestrator-v2/packages/agent-3-task/src/augment.spec.ts @@ -0,0 +1,37 @@ +import { describe, expect } from "vite-plus/test"; +import test from "vitest-gwt"; +import { Runner } from "@bifrost-ai/runner"; + +import "./augment.js"; + +describe("agent-3-task/augment on a bare Runner", () => { + test("registers a task agent without pre-wiring a data registry", { + given: { a_bare_runner }, + when: { a_task_agent_is_registered }, + then: { the_handler_is_available }, + }); +}); + +type Context = { + runner: Runner; +}; + +function a_bare_runner(this: Context) { + // A bare `new Runner()` starts with an empty data registry; the augment must + // lazily create the agentDefinition registry (regression: it used to throw). + this.runner = new Runner(); +} + +function a_task_agent_is_registered(this: Context) { + this.runner.registerTaskAgent({ + name: "greeter", + description: "greets", + promptBody: "say hi", + template: { parameters: {} }, + tools: [], + }); +} + +function the_handler_is_available(this: Context) { + expect(this.runner.hasWorkItemHandler("task", "greeter")).toBe(true); +} diff --git a/orchestrator-v2/packages/agent-3-task/src/augment.ts b/orchestrator-v2/packages/agent-3-task/src/augment.ts new file mode 100644 index 0000000..538e2df --- /dev/null +++ b/orchestrator-v2/packages/agent-3-task/src/augment.ts @@ -0,0 +1,34 @@ +import type { AgentDefinition, Engine } from "@bifrost-ai/engine"; +import { Runner } from "@bifrost-ai/runner"; + +import { createTaskAgent } from "./create-task-agent.js"; +import { + AGENT_DEFINITION_DATA_TYPE, + ENGINE_DATA_TYPE, + isAgentDefinition, + isEngine, +} from "./types.js"; + +declare module "@bifrost-ai/runner" { + // oxlint-disable-next-line typescript/consistent-type-definitions -- module augmentation + interface Runner { + registerTaskAgent(agent: AgentDefinition): void; + registerEngine(name: string, engine: Engine): void; + } +} + +Runner.prototype.registerTaskAgent = function registerTaskAgent( + this: Runner, + agent: AgentDefinition, +): void { + this.data.ensure(AGENT_DEFINITION_DATA_TYPE, isAgentDefinition).register(agent.name, agent); + this.registerWorkItemHandler(createTaskAgent(agent)); +}; + +Runner.prototype.registerEngine = function registerEngine( + this: Runner, + name: string, + engine: Engine, +): void { + this.data.ensure(ENGINE_DATA_TYPE, isEngine).register(name, engine); +}; diff --git a/orchestrator-v2/packages/agent-3-task/vite.config.ts b/orchestrator-v2/packages/agent-3-task/vite.config.ts index 8ffe822..11c435a 100644 --- a/orchestrator-v2/packages/agent-3-task/vite.config.ts +++ b/orchestrator-v2/packages/agent-3-task/vite.config.ts @@ -1,3 +1,11 @@ +import { resolve } from "node:path"; import { defineConfig } from "vite-plus"; -export default defineConfig({}); +export default defineConfig({ + pack: { + entry: { + index: resolve(__dirname, "src/index.ts"), + augment: resolve(__dirname, "src/augment.ts"), + }, + }, +}); diff --git a/orchestrator-v2/packages/interfaces-work/src/types.ts b/orchestrator-v2/packages/interfaces-work/src/types.ts index da83d9d..84682a6 100644 --- a/orchestrator-v2/packages/interfaces-work/src/types.ts +++ b/orchestrator-v2/packages/interfaces-work/src/types.ts @@ -103,6 +103,10 @@ export type DataRegistry> = { export type MutableDataRegistry> = { get(type: K): Registry; + ensure( + type: K, + guard: (value: unknown) => value is T[K], + ): Registry; }; export type WorkItemHandlerRegistry = { diff --git a/orchestrator-v2/packages/runner/src/data-registry.ts b/orchestrator-v2/packages/runner/src/data-registry.ts index 815f8e0..1430e4d 100644 --- a/orchestrator-v2/packages/runner/src/data-registry.ts +++ b/orchestrator-v2/packages/runner/src/data-registry.ts @@ -15,8 +15,20 @@ export function createDataRegistry>( ): MutableDataRegistry { const registries = new Map>(); + const ensureRegistry = ( + type: K, + guard: (value: unknown) => value is T[K], + ): Registry => { + let registry = registries.get(type); + if (registry === undefined) { + registry = createGuardedRegistry(guard); + registries.set(type, registry); + } + return registry as Registry; + }; + for (const type of Object.keys(guards) as (keyof T & string)[]) { - registries.set(type, createGuardedRegistry(guards[type])); + ensureRegistry(type, guards[type]); } return { @@ -27,6 +39,7 @@ export function createDataRegistry>( } return registry as Registry; }, + ensure: ensureRegistry, }; } diff --git a/orchestrator-v2/pnpm-lock.yaml b/orchestrator-v2/pnpm-lock.yaml index 36bc74b..e8aa2e5 100644 --- a/orchestrator-v2/pnpm-lock.yaml +++ b/orchestrator-v2/pnpm-lock.yaml @@ -250,6 +250,9 @@ importers: '@bifrost-ai/interfaces-work': specifier: workspace:* version: link:../interfaces-work + '@bifrost-ai/runner': + specifier: workspace:* + version: link:../runner devDependencies: '@types/node': specifier: 'catalog:'