Skip to content

Compose capabilities

Use composition when one engine should publish capabilities from its own package and from reusable packages. The consuming engine owns the final IDs, supplies all dependencies, and detects collisions before serving traffic.

If you are publishing the reusable package first, read the capability packages guide or start with the atomic export and capability library recipes.

This recipe follows the composed-engine example. It combines one local capability, two atomic exports, and two selected capabilities from a library.

  1. Keep the composition module safe to import

    Create src/capabilities.ts as a module that starts no adapter and creates no engine. This lets the build gate inspect it without starting the application.

  2. Add the local and imported capabilities

    import {
    composeCapabilities,
    importCapabilities,
    importCapability,
    } from "@invokta/core";
    import {
    createScoreTicketPriorityExport,
    } from "@invokta/example-community-capabilities";
    import {
    createClassifyTicketExport,
    } from "@invokta/example-community-capabilities/classify-ticket";
    import {
    createCommunitySupportLibrary,
    } from "@invokta/example-community-capabilities/library";
    import type { OperationsDependencies } from "./application/ports.js";
    import { createGenerateReport } from "./capabilities/generate-report.js";
    import {
    createDefaultOperationsDependencies,
    } from "./infrastructure/default-dependencies.js";
    export function createOperationsCapabilities(
    dependencies: OperationsDependencies,
    ) {
    return composeCapabilities({
    local: {
    "operations.generate-report":
    createGenerateReport(dependencies.reports),
    },
    imports: [
    importCapability(
    createScoreTicketPriorityExport(dependencies.community),
    ),
    importCapability(
    createClassifyTicketExport(dependencies.community),
    { as: "operations.classify-ticket" },
    ),
    importCapabilities(
    createCommunitySupportLibrary(dependencies.community),
    {
    include: [
    "community.search-knowledge-base",
    "community.draft-reply",
    ],
    remap: {
    "community.draft-reply": "operations.draft-reply",
    },
    },
    ),
    ],
    });
    }
    export const capabilities = createOperationsCapabilities(
    createDefaultOperationsDependencies(),
    );

    as and remap replace a default ID. They do not create aliases. The effective capability set is:

    operations.generate-report
    community.score-ticket-priority
    operations.classify-ticket
    community.search-knowledge-base
    operations.draft-reply
  3. Create the engine from the composed map

    import { createEngine } from "@invokta/core";
    import { capabilities } from "./capabilities.js";
    export const engine = createEngine({
    name: "composed-engine",
    version: "0.1.0",
    capabilities,
    });

    Local and imported capabilities now use the same engine.invoke pipeline. The engine composition root remains responsible for constructing every repository, classifier, policy checker, and other dependency.

From the repository root, build and run the direct example:

Terminal window
yarn build
node examples/composed-engine/dist/direct.js

Inspect the effective IDs, then invoke an atomic and a remapped library capability through the CLI:

Terminal window
node examples/composed-engine/dist/cli.js list
node examples/composed-engine/dist/cli.js run community.score-ticket-priority \
--input '{"ticketId":"T-789"}'
node examples/composed-engine/dist/cli.js run operations.draft-reply \
--input '{"ticketId":"T-123","tone":"friendly"}'

Run the composition build gate before the example tests and type checks:

Terminal window
yarn workspace @invokta/example-composed check:capabilities
yarn workspace @invokta/example-composed test
yarn workspace @invokta/example-composed typecheck
yarn workspace @invokta/example-composed build

check:capabilities imports the built composition module and rejects duplicate effective IDs, invalid selections, and untracked raw maps without starting an adapter.

Read the complete composed-engine example for the export package, injected dependencies, inbound adapters, and collision tests.

The @invokta/tooling reference documents named exports, diagnostic boundaries, and every check-capabilities exit code.