capabilities

Eleven ways to do work. One contract.

A capability is a typed unit of work the workflow can call. Behind each one sits an executor — the thing that actually runs. Whatever the kind, the kernel applies the same loop: grant, execute, validate.

The executor kinds

All eleven, declared the same way in YAML. No glue code, no per-tool wrappers.

KindWhat it runs
cliA shell command. Captures stdout, stderr, and exit code as structured output.
scriptA curated, hash-pinned script from the script library. Safe for deterministic steps.
restAn HTTP request — templated paths, mapped bodies, optional idempotency keys.
mcpA tool on a connected MCP server (stdio or SSE), with argument mapping.
humanQueues the step for a person and emits an audit event. Pairs with actor: human.
llmA governed LLM call — the state's legal transitions become the model's tool list.
agentA feature-gated LLM overlay, sibling to llm, for agentic sub-runs.
workflowA sub-workflow, run as its own instance while the parent step waits.
parallelFans out N executor branches inside one transition and aggregates results.
pipelineA sequence of executor steps, each output threaded into the next.
noopReturns immediately. Stubs, tests, and transitions that only move state.

MCP is one transport among many. Praxec has first-class MCP support — but a capability is just as happily a CLI, an HTTP service, a pinned script, a sub-workflow, or a person. You compose them freely inside one workflow.

processes

CLIs and scripts

The cli executor runs a command and gives you structured output — exitCode, stdout, stderr, and parsed JSON when stdout is JSON. Reliability policy is declared right next to it:

capability — cli with reliability
executor:
  kind: cli
  connection: dotnet
  args: [test, "$.arguments.project"]
reliability:
  timeoutMs: 120000
  retry: { maxAttempts: 3, backoff: exponential }

The script executor is the stricter sibling: it runs only curated, hash-pinned scripts from a library. If the script on disk doesn't match the pinned hash, it doesn't run. Use it for the deterministic steps you never want silently swapped.

services

HTTP services and MCP tools

The rest executor calls HTTP services through named connections, with path templating and body mapping from workflow context. Set idempotencyKey and the kernel derives a stable key across retries — your backend can deduplicate even if a fallback executor takes over:

capability — rest with idempotency
executor:
  kind: rest
  connection: payroll
  method: POST
  path: /reimbursements
  body:
    employee: "$.workflow.input.employee"
    amount: "$.workflow.input.amount"
  idempotencyKey: true     # retries can't double-pay

The mcp executor calls a tool on any connected MCP server. This is how you bring an existing MCP ecosystem under kernel governance — the tool stays where it is, but access to it becomes a granted, validated step instead of a standing menu item:

capability — mcp tool call
executor:
  kind: mcp
  connection: github
  tool: list_issues
  map:
    repo: "$.arguments.repo"

people

Humans are capabilities too

Approvals aren't an afterthought bolted onto the side — a person is a first-class executor. Two pieces work together: the humanexecutor routes the request to a named queue and puts it on the audit record, and the actor: human gate makes the approving move unsubmittable by the model:

capability — human approval
transitions:
  approve:
    actor: human             # the gate: only humans can submit this
    target: approved
    executor:
      kind: human            # the routing: queue it for review
      queue: approvals

The model walks the workflow up to the gate and stops cold. A human — through your dashboard, approval tool, or a Slack bot watching the queue — makes the actual decision.

models

LLM steps, inside the boundary

The llm executor hosts a governed model call inside the runtime. The transitions legal at the current state are the model's entire tool list; it picks one per turn and the kernel advances the workflow. Iterations, wall-clock time, tokens, and cumulative cost are all capped in config — and a tools: field is rejected at parse time, so nobody can widen the box from inside a workflow file.

The agent executor is its feature-gated sibling for agentic sub-runs. Same principle: the model works inside a bounded execution context the workflow defines, never the other way around.More on bounded contexts →

composition

Workflows compose like functions

  • workflow starts a sub-workflow as its own instance — its own state, its own audit trail — while the parent step waits.
  • parallel fans out multiple executor branches inside a single transition and aggregates the results. Branches can be any kind, including nested parallel.
  • pipeline runs executor steps in sequence, threading each output into the next input — a small data pipeline inside one transition.

And instead of declaring executors inline, you can reference a named capability. It resolves at config-load time and brings its guards and reliability policy with it:

capability — by reference
executor:
  capability: github.list_issues   # resolves at config-load time;
                                     # its guards + reliability come along

reliability

Failure handling is declarative

Any executor can carry a reliability policy: a timeout, retries with fixed or exponential backoff, and a fallback chain that tries alternate executors in order until one succeeds. You declare which failures are retryable — timeouts, transient errors, rate limits, connection errors — and idempotency keys stay stable across retries and fallbacks, so downstream systems can deduplicate.

That's the point of putting a kernel under the workflow: retries, timeouts, and fallbacks are execution concerns, so they live in the execution layer — not scattered through prompts and glue scripts.