Runtime Semantics

Ageniti keeps the important behaviour in the runtime so that CLI, HTTP, MCP, React, JSON automation, dev tools, and LLM adapters all share one execution model.

Execution Flow

When you invoke an action, the runtime performs these steps:

  1. Resolve the action by name or object reference.
  2. Check whether the action supports the requested surface.
  3. Validate input against the action input schema.
  4. Enforce confirmation for protected actions.
  5. Run the permission checker.
  6. Execute middleware.
  7. Run the action with timeout and retry handling.
  8. Ensure the output can be serialised as JSON.
  9. Validate output against the output schema, if present.
  10. Return a structured success or failure envelope.

Surface Rules

Supported surfaces are declared on each action through supportedSurfaces.

Default supported surfaces:

cli, json, http, mcp, react, dev, ai-sdk

If a surface is not supported, the runtime returns UNSUPPORTED_SURFACE.

Confirmation Rules

Actions with requiresConfirmation: true require an explicit confirmation flag.

By default, destructive actions inherit confirmation automatically.

Runtime confirmation is enforced for most surfaces, but not for:

  • react
  • dev

That means UI surfaces should handle confirmation in the UI layer before invoking the action.

Permission Checks

permissionChecker({ action, input, context }) must return either:

  • true to allow execution
  • false to deny execution with a generic authorisation message
  • a string to deny execution with a custom message

If the check fails, the runtime returns AUTHORIZATION_ERROR.

Timeout And Retry

Timeout is controlled by:

  • action-level timeoutMs
  • per-invocation timeoutMs

Retry is controlled by:

  • action-level retry
  • per-invocation retry

Retry only happens when the thrown error is retryable. That usually means the action throws an AgenitiError with retryable: true, or the timeout logic marks the failure as retryable.

retry: true normalises to:

{
  "retries": 2,
  "delayMs": 100
}

Retries use linear backoff based on attempt count.

Logging, Progress, And Artifacts

Every invocation accumulates structured logs and artifacts.

From context.logger:

  • debug(message, fields?)
  • info(message, fields?)
  • warn(message, fields?)
  • error(message, fields?)

From context.progress.report():

  • records a log entry with fields.type = "progress"
  • may include percent
  • may include additional structured fields

From context.artifacts.add():

  • normalises missing id
  • defaults type to "file"
  • stores metadata as an object

Success Envelope

{
  "ok": true,
  "data": {},
  "artifacts": [],
  "logs": [],
  "meta": {
    "action": "create_task",
    "invocationId": "invocation-id",
    "surface": "cli",
    "durationMs": 12
  }
}

Failure Envelope

{
  "ok": false,
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Invalid action input.",
    "issues": [],
    "retryable": false
  },
  "artifacts": [],
  "logs": [],
  "meta": {
    "action": "create_task",
    "invocationId": "invocation-id",
    "surface": "mcp",
    "durationMs": 3
  }
}

Common Runtime Error Codes

Core runtime codes:

  • ACTION_NOT_FOUND
  • UNSUPPORTED_SURFACE
  • VALIDATION_ERROR
  • CONFIRMATION_REQUIRED
  • AUTHORIZATION_ERROR
  • OUTPUT_SERIALIZATION_ERROR
  • OUTPUT_VALIDATION_ERROR
  • TIMEOUT
  • CANCELLED
  • INTERNAL_ERROR

Wrapper-specific codes you may also see:

  • INVALID_JSON_RUNNER_PAYLOAD
  • DEV_SERVER_ERROR
  • NOT_FOUND from the local dev server HTTP routes

How Errors Are Normalised

If an action throws:

  • AgenitiError, its code, message, issues, and retryable fields are preserved
  • AbortError, the runtime returns CANCELLED
  • any other error, the runtime returns INTERNAL_ERROR

CLI Exit Codes

The built-in CLI maps runtime errors to process exit codes.

Important mappings:

  • VALIDATION_ERROR -> 2
  • AUTHENTICATION_ERROR -> 3
  • AUTHORIZATION_ERROR -> 3
  • ACTION_NOT_FOUND -> 4
  • EXTERNAL_SERVICE_ERROR -> 5
  • TIMEOUT -> 124
  • CANCELLED -> 130
  • anything else -> 1

Output Guarantees

Ageniti only returns values that can be serialised safely as JSON.

If an output schema exists, the runtime validates the action result against it before returning success. This makes the result envelope suitable for CLI output, MCP structured content, tests, and machine-to-machine integrations.