diff --git a/workspaces/boost/AGENTS.md b/workspaces/boost/AGENTS.md index 3c5221e85a..ddf9077fd6 100644 --- a/workspaces/boost/AGENTS.md +++ b/workspaces/boost/AGENTS.md @@ -73,7 +73,8 @@ Agents, tools, models, MCP servers, and vector stores are Backstage catalog enti | Package | Purpose | | --------------------------------- | -------------------------------------------------------------------- | | `boost-frontend` | Chat UI, agent gallery, admin panels, composable routable extensions | -| `boost-common` | Shared types, permissions, `boostAiProviderServiceRef` | +| `boost-common` | Shared types, permissions (browser-safe, `common-library` role) | +| `boost-node` | `boostAiProviderServiceRef`, extension points (`node-library` role) | | `boost-backend` | Core routes, services, middleware, ProviderManager | | `boost-backend-module-llamastack` | Llama Stack provider module | | `boost-backend-module-kagenti` | Kagenti provider module | diff --git a/workspaces/boost/openspec/changes/platform-operations-deployment/proposal.md b/workspaces/boost/openspec/changes/platform-operations-deployment/proposal.md index fbdd0b1f23..9e4b88ee23 100644 --- a/workspaces/boost/openspec/changes/platform-operations-deployment/proposal.md +++ b/workspaces/boost/openspec/changes/platform-operations-deployment/proposal.md @@ -15,7 +15,8 @@ Boost ships as a set of modular RHDH dynamic plugins (OCI) and Backstage static | Package | Purpose | | --------------------------------- | ----------------------------------------------------------------- | | `boost-frontend` | Chat UI, agent gallery, admin panels, composable extensions | -| `boost-common` | Shared types, permissions, service refs | +| `boost-common` | Shared types, permissions (browser-safe, `common-library` role) | +| `boost-node` | Service refs, extension points (`node-library` role) | | `boost-backend` | Core routes, services, middleware, cross-cutting entity providers | | `boost-backend-module-llamastack` | Llama Stack agentic provider | | `boost-backend-module-kagenti` | Kagenti agentic provider | diff --git a/workspaces/boost/openspec/changes/platform-operations-deployment/specs/deployment/spec.md b/workspaces/boost/openspec/changes/platform-operations-deployment/specs/deployment/spec.md index 7041170cec..97f60b227d 100644 --- a/workspaces/boost/openspec/changes/platform-operations-deployment/specs/deployment/spec.md +++ b/workspaces/boost/openspec/changes/platform-operations-deployment/specs/deployment/spec.md @@ -29,7 +29,7 @@ Deploy Boost as a traditional Backstage plugin with npm packages. #### Scenario: Static plugin installation -- **WHEN** the developer installs `@boost/plugin-boost`, `@boost/plugin-boost-backend`, and `@boost/plugin-boost-common` +- **WHEN** the developer installs `@boost/plugin-boost`, `@boost/plugin-boost-backend`, `@boost/plugin-boost-common`, and `@boost/plugin-boost-node` - **THEN** frontend route, sidebar entry, icon, and backend plugin are registered manually - **AND** `app-config.yaml` is configured - **AND** the application is rebuilt and deployed diff --git a/workspaces/boost/openspec/changes/pluggable-ai-platform-architecture/design.md b/workspaces/boost/openspec/changes/pluggable-ai-platform-architecture/design.md index 1dd69a2f71..b87fa0b1c2 100644 --- a/workspaces/boost/openspec/changes/pluggable-ai-platform-architecture/design.md +++ b/workspaces/boost/openspec/changes/pluggable-ai-platform-architecture/design.md @@ -22,14 +22,23 @@ Boost implements the provider abstraction as modular RHDH dynamic plugins from t ## Decisions -### Decision 1: serviceRef lives in boost-common +### Decision 1: serviceRef lives in boost-node, types live in boost-common -The `boostAiProviderServiceRef` is exported from `boost-common` alongside the `AgenticProvider` interface types. This allows both backend consumers and frontend type consumers to reference the interface from a single package without depending on the full backend. +The `boostAiProviderServiceRef` is exported from `boost-node` (`backstage.role: node-library`), not `boost-common`. The `AgenticProvider` interface types remain in `boost-common` (`backstage.role: common-library`). + +This follows the Backstage convention established by the catalog plugin family: + +| Package | Role | Contains | +| ----------------------- | ---------------- | ------------------------------------------- | +| `plugin-catalog-common` | `common-library` | Types, permissions — no backend API imports | +| `plugin-catalog-node` | `node-library` | `catalogServiceRef`, extension points | + +A `common-library` package must be safe for browser bundling. `createServiceRef` is a runtime value from `@backstage/backend-plugin-api` — importing it into a `common-library` package forces the bundler to pull in the entire backend API, which fails in browser environments. Backstage's CLI enforces this role boundary. ```typescript -// plugins/boost-common/src/services.ts +// plugins/boost-node/src/services.ts import { createServiceRef } from '@backstage/backend-plugin-api'; -import type { AgenticProvider } from './types'; +import type { AgenticProvider } from '@boost/plugin-boost-common'; export const boostAiProviderServiceRef = createServiceRef({ id: 'boost.ai-provider', @@ -37,6 +46,21 @@ export const boostAiProviderServiceRef = createServiceRef({ }); ``` +The dependency graph: + +``` +boost-common (common-library) ←── boost-node (node-library) ←── boost-backend + │ ↑ + │ boost-backend-module-llamastack + │ boost-backend-module-kagenti + ↓ +boost-frontend (imports types only) +``` + +`boost-common` exports: `AgenticProvider`, `ProviderDescriptor`, `ProviderCapabilities`, `NormalizedStreamEvent`, conversation types, permission definitions. + +`boost-node` exports: `boostAiProviderServiceRef`, `boostProviderExtensionPoint`, and re-exports types from `boost-common` for backend convenience. + The core `boost-backend` plugin registers the default factory via `createServiceFactory` that resolves to the `ProviderManager`'s active provider. ### Decision 2: Providers as backend modules, not separate plugins @@ -82,9 +106,9 @@ const hasNamespaceScoping = capabilities?.namespaceScoping === true; ### Decision 5: Provider-specific types stay in their modules -Provider-specific types (e.g., Kagenti-specific interfaces) live in their respective provider modules. Only shared interfaces (`AgenticProvider`, `ProviderDescriptor`, `ProviderCapabilities`, conversation types, `NormalizedStreamEvent`) live in `boost-common`. +Provider-specific types (e.g., Kagenti-specific interfaces) live in their respective provider modules. Only shared interfaces (`AgenticProvider`, `ProviderDescriptor`, `ProviderCapabilities`, conversation types, `NormalizedStreamEvent`) live in `boost-common`. The `boostAiProviderServiceRef` and `boostProviderExtensionPoint` live in `boost-node`. ## Risks - **Cache key collisions:** Mitigated by using `cache.withOptions()` which namespace-scopes keys per plugin/module. -- **Provider module interdependency:** Provider modules must not import from each other. Shared utilities live in `boost-common` or standalone packages. Boost enforces this from the start. +- **Provider module interdependency:** Provider modules must not import from each other. Shared utilities live in `boost-common` or `boost-node`, and standalone packages. Boost enforces this from the start. diff --git a/workspaces/boost/openspec/changes/pluggable-ai-platform-architecture/proposal.md b/workspaces/boost/openspec/changes/pluggable-ai-platform-architecture/proposal.md index ab1a0ee446..1b6062067e 100644 --- a/workspaces/boost/openspec/changes/pluggable-ai-platform-architecture/proposal.md +++ b/workspaces/boost/openspec/changes/pluggable-ai-platform-architecture/proposal.md @@ -13,7 +13,7 @@ Boost builds this as modular RHDH dynamic plugins from the start: each provider - `AgenticProvider` interface with required (`chat`, `chatStream`) and optional capabilities - `ProviderDescriptor` declares ID, name, and capability matrix - Backstage extension point (`boostProviderExtensionPoint`) for provider registration -- `boostAiProviderServiceRef` in `boost-common` for cross-plugin AI provider consumption +- `boostAiProviderServiceRef` in `boost-node` for cross-plugin AI provider consumption ### Runtime Hot-Swap @@ -42,12 +42,13 @@ Two built-in provider modules, each as an independent `createBackendModule`: - **Providers as modules, not monoliths** — each provider is independently installable and removable - **Capability checks over identity checks** — `capabilities.agentCatalog` instead of `providerId === 'kagenti'` - **`cacheService` everywhere** — all provider caches use Backstage `cacheService` with namespace isolation via `cache.withOptions()` -- **Clean type boundaries** — provider-specific types stay in their modules; only shared interfaces in `boost-common` +- **Clean type boundaries** — provider-specific types stay in their modules; shared interfaces in `boost-common`, service refs and extension points in `boost-node` ## Impact -- `plugins/boost-common/` — `AgenticProvider`, `NormalizedStreamEvent`, `boostAiProviderServiceRef` -- `plugins/boost-backend/src/plugin.ts` — serviceRef registration, ProviderManager +- `plugins/boost-common/` — `AgenticProvider`, `NormalizedStreamEvent`, permission definitions +- `plugins/boost-node/` — `boostAiProviderServiceRef`, `boostProviderExtensionPoint` +- `plugins/boost-backend/src/plugin.ts` — serviceRef factory registration, ProviderManager - `plugins/boost-backend-module-llamastack/` — Llama Stack provider module - `plugins/boost-backend-module-kagenti/` — Kagenti provider module - `plugins/boost-frontend/src/` — capability-based rendering throughout diff --git a/workspaces/boost/openspec/changes/pluggable-ai-platform-architecture/specs/provider-abstraction/spec.md b/workspaces/boost/openspec/changes/pluggable-ai-platform-architecture/specs/provider-abstraction/spec.md index 3ccb99ee0e..63666acb57 100644 --- a/workspaces/boost/openspec/changes/pluggable-ai-platform-architecture/specs/provider-abstraction/spec.md +++ b/workspaces/boost/openspec/changes/pluggable-ai-platform-architecture/specs/provider-abstraction/spec.md @@ -51,12 +51,13 @@ Other Backstage plugins must be able to consume the active AI provider via Backs - **AND** it can call `chat()`, `chatStream()`, and any declared optional capabilities - **AND** if the active provider is hot-swapped, the consuming plugin receives the new provider on next request -#### Scenario: Service ref declared in boost-common +#### Scenario: Service ref declared in boost-node - **WHEN** the `boostAiProviderServiceRef` is created via `createServiceRef` from `@backstage/backend-plugin-api` -- **THEN** it is exported from the `boost-common` package (not `boost-backend`) -- **AND** its type parameter is the `AgenticProvider` interface +- **THEN** it is exported from the `boost-node` package (`backstage.role: node-library`), not `boost-common` or `boost-backend` +- **AND** its type parameter is the `AgenticProvider` interface (imported from `boost-common`) - **AND** its ID follows the pattern `boost.ai-provider` +- **AND** `boost-common` (`backstage.role: common-library`) has no dependency on `@backstage/backend-plugin-api` — keeping it safe for browser bundling ### Requirement: Shared Types in Common Package diff --git a/workspaces/boost/openspec/changes/pluggable-ai-platform-architecture/tasks.md b/workspaces/boost/openspec/changes/pluggable-ai-platform-architecture/tasks.md index 1403c57262..1174b1bd01 100644 --- a/workspaces/boost/openspec/changes/pluggable-ai-platform-architecture/tasks.md +++ b/workspaces/boost/openspec/changes/pluggable-ai-platform-architecture/tasks.md @@ -1,13 +1,14 @@ # Tasks: Pluggable AI Platform Architecture -## 1. Types and Service Ref (P0) +## 1. Types, Service Ref, and Package Scaffolding (P0) - [ ] 1.1 Define `AgenticProvider`, `ProviderDescriptor`, `ProviderCapabilities` interfaces in `boost-common` - [ ] 1.2 Define `NormalizedStreamEvent` union type in `boost-common` - [ ] 1.3 Define `ConversationSummary`, `ConversationDetails`, `InputItem` conversation types in `boost-common` -- [ ] 1.4 Create `boostAiProviderServiceRef` in `boost-common` via `createServiceRef` +- [ ] 1.4 Create `boost-node` package (`backstage.role: node-library`) with `boostAiProviderServiceRef` via `createServiceRef` — serviceRef must NOT live in `boost-common` (see Decision 1) - [ ] 1.5 Register default service factory in `boost-backend/plugin.ts` resolving to `ProviderManager.getActiveProvider()` - [ ] 1.6 Verify no provider-specific types in common package — only shared interfaces +- [ ] 1.7 Verify `boost-common` has no dependency on `@backstage/backend-plugin-api` ## 2. Provider Module Packages (P0) diff --git a/workspaces/boost/specifications/boost-context.md b/workspaces/boost/specifications/boost-context.md index fb1711199e..b5aa8ecd0f 100644 --- a/workspaces/boost/specifications/boost-context.md +++ b/workspaces/boost/specifications/boost-context.md @@ -32,7 +32,8 @@ All packages live at `rhdh-plugins/workspaces/boost/plugins/`: ``` workspace/boost/plugins/ ├── boost-frontend — Chat UI, agent gallery, admin panels, composable extensions -├── boost-common — Shared types, permissions, boostAiProviderServiceRef +├── boost-common — Shared types, permissions (browser-safe, common-library role) +├── boost-node — Service refs, extension points (node-library role) ├── boost-backend — Core routes, services, middleware, ProviderManager, cross-cutting entity providers ├── boost-backend-module-llamastack — Llama Stack agentic provider (composes llamastack-entity-provider) ├── boost-backend-module-kagenti — Kagenti agentic provider (composes kagenti-entity-provider) @@ -40,7 +41,7 @@ workspace/boost/plugins/ └── kagenti-entity-provider — Backstage backend service: Kagenti catalog entities (independently deployable) ``` -The core three packages (`boost-frontend`, `boost-common`, `boost-backend`) mirror augment's structure. Provider modules and entity providers are additive — deployers install only what they need. Entity providers are independently deployable as RHDH dynamic plugins for catalog-only use cases. +The core packages (`boost-frontend`, `boost-common`, `boost-node`, `boost-backend`) mirror augment's structure with the addition of `boost-node` for service refs and extension points (following the Backstage `plugin-catalog-common`/`plugin-catalog-node` pattern). Provider modules and entity providers are additive — deployers install only what they need. Entity providers are independently deployable as RHDH dynamic plugins for catalog-only use cases. ## Design Principles (Learned from Augment) @@ -60,7 +61,7 @@ _Augment lesson: 2,132 lines of custom governance code implementing 12 authoriza ### 3. Providers as Independent RHDH Dynamic Plugins -Each AI platform provider is a separate `createBackendModule` packaged as an RHDH dynamic plugin. Provider types live in the common package. Cross-plugin consumption via `boostAiProviderServiceRef`. +Each AI platform provider is a separate `createBackendModule` packaged as an RHDH dynamic plugin. Provider types live in `boost-common`; `boostAiProviderServiceRef` and the extension point live in `boost-node`. Cross-plugin consumption via the service ref. _Augment lesson: Monolithic plugin with providers locked inside. No serviceRef for cross-plugin consumption. 559 lines of Kagenti-specific types polluting the common package. 13+ provider ID string checks coupling frontend to specific providers._ diff --git a/workspaces/boost/specifications/prd/platform-operations-deployment.md b/workspaces/boost/specifications/prd/platform-operations-deployment.md index 689abdaf34..a9ed472d32 100644 --- a/workspaces/boost/specifications/prd/platform-operations-deployment.md +++ b/workspaces/boost/specifications/prd/platform-operations-deployment.md @@ -63,15 +63,16 @@ Admin panels, branding controls, RAG configuration, and onboarding flows must al All packages live at `rhdh-plugins/workspaces/boost/plugins/`: -| Package | Type | Description | -| --------------------------------- | --------------- | --------------------------------------------------------------------------------------------------------- | -| `boost-frontend` | Frontend plugin | Chat UI, agent gallery, admin panels, composable extensions | -| `boost-common` | Common library | Shared types (`AgenticProvider`, `NormalizedStreamEvent`, permissions), `boostAiProviderServiceRef` | -| `boost-backend` | Backend plugin | Core routes, services, middleware, `ProviderManager`, cross-cutting entity providers (MCP, vector stores) | -| `boost-backend-module-llamastack` | Backend module | Llama Stack agentic provider (composes `llamastack-entity-provider`) | -| `boost-backend-module-kagenti` | Backend module | Kagenti agentic provider (composes `kagenti-entity-provider`) | -| `llamastack-entity-provider` | Backend service | Llama Stack model + agent catalog entities (independently deployable) | -| `kagenti-entity-provider` | Backend service | Kagenti agent + tool catalog entities (independently deployable) | +| Package | Type | Description | +| --------------------------------- | --------------- | ------------------------------------------------------------------------------------------------------------- | +| `boost-frontend` | Frontend plugin | Chat UI, agent gallery, admin panels, composable extensions | +| `boost-common` | Common library | Shared types (`AgenticProvider`, `NormalizedStreamEvent`, permissions) — browser-safe, no backend API imports | +| `boost-node` | Node library | `boostAiProviderServiceRef`, `boostProviderExtensionPoint` — service refs and extension points | +| `boost-backend` | Backend plugin | Core routes, services, middleware, `ProviderManager`, cross-cutting entity providers (MCP, vector stores) | +| `boost-backend-module-llamastack` | Backend module | Llama Stack agentic provider (composes `llamastack-entity-provider`) | +| `boost-backend-module-kagenti` | Backend module | Kagenti agentic provider (composes `kagenti-entity-provider`) | +| `llamastack-entity-provider` | Backend service | Llama Stack model + agent catalog entities (independently deployable) | +| `kagenti-entity-provider` | Backend service | Kagenti agent + tool catalog entities (independently deployable) | **RHDH — Dynamic Plugin (recommended):** @@ -81,13 +82,13 @@ All packages live at `rhdh-plugins/workspaces/boost/plugins/`: 4. RHDH loads plugins dynamically via Scalprum — no code changes or rebuilds 5. Boost appears as a sidebar entry in RHDH -**Full portal deployment** installs `boost-frontend`, `boost-common`, `boost-backend`, plus one or both provider modules. Provider modules compose their entity providers internally. +**Full portal deployment** installs `boost-frontend`, `boost-common`, `boost-node`, `boost-backend`, plus one or both provider modules. Provider modules compose their entity providers internally. **Entity-provider-only deployment** installs `llamastack-entity-provider` or `kagenti-entity-provider` as standalone dynamic plugins — gets AI domain objects in the Backstage catalog without the rest of boost. **Backstage — Static Plugin:** -1. Install npm packages: `@boost/plugin-boost-frontend`, `@boost/plugin-boost-backend`, `@boost/plugin-boost-common` +1. Install npm packages: `@boost/plugin-boost-frontend`, `@boost/plugin-boost-backend`, `@boost/plugin-boost-common`, `@boost/plugin-boost-node` 2. Optionally install provider modules: `@boost/plugin-boost-backend-module-llamastack`, `@boost/plugin-boost-backend-module-kagenti` 3. Register frontend route, sidebar entry, and icon 4. Register backend plugin and provider modules in backend startup @@ -247,7 +248,7 @@ Backend services + Frontend via admin API ``` RHDH Dynamic Plugin (full portal): - boost-frontend + boost-common + boost-backend + provider module(s) + boost-frontend + boost-common + boost-node + boost-backend + provider module(s) OCI images → dynamic-plugins.override.yaml → Scalprum → sidebar entry No code changes, no rebuilds diff --git a/workspaces/boost/specifications/prd/pluggable-ai-platform-architecture.md b/workspaces/boost/specifications/prd/pluggable-ai-platform-architecture.md index d0786b067e..cff66ae18c 100644 --- a/workspaces/boost/specifications/prd/pluggable-ai-platform-architecture.md +++ b/workspaces/boost/specifications/prd/pluggable-ai-platform-architecture.md @@ -186,7 +186,7 @@ Hot-swaps between configured providers at runtime. Monitors capability differenc - Provider modules have IDs: `llamastack` and `kagenti` - Both are exported as OCI images for RHDH dynamic plugin loading - Deployers install only the providers they need — no unused provider code loaded -- Provider modules depend on `boost-common` for shared types and `boostAiProviderServiceRef` +- Provider modules depend on `boost-common` for shared types and `boost-node` for `boostAiProviderServiceRef` - Boost ships modular from day one — no monolithic fallback needed **RHDH deployment example:** @@ -245,7 +245,7 @@ AgenticProvider (in boost-common) ├── evaluation? — optional └── conversation? — optional -boostAiProviderServiceRef (in boost-common) +boostAiProviderServiceRef (in boost-node) └── enables cross-plugin consumption of the active provider ProviderManager