Invocation pipeline
Every execution channel converges on engine.invoke. Adapters can parse their
own transport, but they do not call capability handlers directly.
Ordered stages
Section titled “Ordered stages”For each invocation, the engine:
- generates or accepts a request ID;
- resolves the capability by ID;
- validates and transforms the input;
- creates the execution context and enforces the access rule;
- combines caller cancellation with the capability timeout, runs the handler, and validates the output; and
- emits a terminal event and returns typed data or throws an
EngineError.
Input validation happens before authorization because the access rule receives typed domain input. Output validation happens before the result crosses the engine boundary.
Isolation at the request boundary
Section titled “Isolation at the request boundary”Invokta snapshots transformed input and identity data before asynchronous work.
The access rule receives its own copy, while run receives the request-owned
execution copy. A caller or access rule cannot mutate what the capability later
observes.
Cancellation and timeouts
Section titled “Cancellation and timeouts”Pass an AbortSignal in the invocation options when the caller owns cancellation:
const controller = new AbortController();
const pending = engine.invoke( "support.classify-ticket", { ticketId: "T-123" }, { source: "direct", principal: { id: "user:42" }, signal: controller.signal, },);
controller.abort();await pending; // throws EngineError with code CANCELLEDA capability can also declare timeoutMs. Its timer starts after authorization,
so an expensive access check does not consume the capability’s execution budget.
Events
Section titled “Events”The optional onEvent hook receives a compact lifecycle stream:
invocation.startedinvocation.completedinvocation.failed
Events contain identifiers, timing, source, and error codes. Payloads and credentials are excluded. Delivery is best-effort and never blocks the result.
Read the error reference for the normalized failure taxonomy.