Runtime 语义

Ageniti 把关键行为集中在 runtime 中,因此 CLI、HTTP、MCP、React、JSON automation、dev tool 和 LLM adapter 都共享同一套执行模型。

执行流程

调用一个 action 时,runtime 会按顺序执行这些步骤:

  1. 根据名称或对象引用解析 action。
  2. 检查该 action 是否支持当前 surface。
  3. 用输入 schema 做输入校验。
  4. 对受保护 action 执行确认检查。
  5. 执行权限检查。
  6. 执行中间件链。
  7. 结合超时和重试运行 action。
  8. 确保输出可以安全序列化为 JSON。
  9. 如果声明了输出 schema,则继续做输出校验。
  10. 返回结构化的成功或失败 envelope。

Surface 规则

每个 action 通过 supportedSurfaces 声明可用入口。

默认支持的 surface:

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

如果某个 surface 不被支持,runtime 会返回 UNSUPPORTED_SURFACE

确认规则

当 action 的 requiresConfirmation: true 时,runtime 需要显式确认标记。

默认情况下,destructive action 会自动继承该要求。

不过以下 surface 不会在 runtime 层强制阻塞确认:

  • react
  • dev

也就是说,UI 场景下确认流程应由 UI 自己负责,再去调用 action。

权限检查

permissionChecker({ action, input, context }) 可以返回:

  • true:允许执行
  • false:拒绝执行,并返回通用授权错误
  • 字符串:拒绝执行,并把该字符串作为错误信息

权限检查失败时,runtime 返回 AUTHORIZATION_ERROR

超时与重试

超时来源:

  • action 级别的 timeoutMs
  • 单次调用级别的 timeoutMs

重试来源:

  • action 级别的 retry
  • 单次调用级别的 retry

只有当错误是可重试的,runtime 才会重试。通常这意味着 action 抛出了 retryable: trueAgenitiError,或者超时逻辑把该错误标记为可重试。

retry: true 会被标准化为:

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

重试采用按尝试次数递增的线性退避。

日志、进度与 Artifacts

每次调用都会累积结构化日志和 artifacts。

context.logger 提供:

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

context.progress.report()

  • 会追加一条日志,其中 fields.type = "progress"
  • 可包含 percent
  • 可附带额外结构化字段

context.artifacts.add()

  • 若缺少 id 会自动补全
  • type 默认是 "file"
  • metadata 会被标准化成对象

流式事件

每次调用都可以被另一个 caller 通过 runtime.stream(name, input, options) 实时观察,返回 async iterable,runtime 执行过程中按顺序 yield { type: "log" | "artifact" | "progress" | "result", ... }。流总是以一个 result 事件收尾,里面是最终的 envelope。

事件来自 action 里调用的 ctx.logger.* / ctx.progress.report() / ctx.artifacts.add()。CLI 的 --ndjson 模式、React 的 useAction hook,以及类型化 client 的 $stream 方法都建在这个原语之上。

Idempotency Key

在 invoke options 里传 idempotencyKey 可以对 retry 去重。非 read 类 action 的成功 envelope 会按 action 名、key、校验后的输入、surface 以及可信调用方指纹共同缓存。命中时会返回缓存 envelope 的一份新副本,并在 meta.idempotent 里标记 "replayed"。缓存有大小上限(默认 1000 条,可通过 idempotencyMaxEntries 配置)和 TTL(默认 5 分钟,idempotencyTtlMs)。

并发限制

在 action 上设 concurrency: { max: N } 可以限制同时活跃的调用数。超出会返回 CONCURRENCY_LIMIT(retryable)。常用来保护上游限流。

取消

在 invoke options 里传 signal: AbortSignal 可以从外部取消调用。CLI 默认把 SIGINT 接到 abort。每次 retry attempt 内部会创建独立的 AbortController,所以 timeout 之后的 retry 仍能看到一个新鲜的 signal。

Hooks 与脱敏

createRuntime 里传 hooks 用于调用埋点,传 redact 配置 logs / artifact metadata / error message 的脱敏行为。

createRuntime({
  actions,
  hooks: {
    onInvocationStart: ({ action, invocationId, input }) => metrics.timer.start(invocationId),
    onInvocationEnd: ({ action, invocationId, envelope }) => metrics.timer.end(invocationId, envelope.ok),
  },
  redact: { keys: ["ssn", "credit_card"] },
});

默认脱敏 key:password / passwd / secret / token / apikey / api_key / authorization / cookie / session / x-api-key / access_token / refresh_token / private_key / client_secret。Error message 中的常见 token 形式(Bearer ...、JWT、sk-...ghp_...xoxb-...key=value)也会被自动脱敏。

成功 Envelope

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

失败 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
  }
}

常见 Runtime 错误码

核心 runtime 错误码:

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

你还可能在包装层中看到:

  • INVALID_JSON_RUNNER_PAYLOAD
  • DEV_SERVER_ERROR
  • 本地 dev server HTTP route 返回的 NOT_FOUND

错误是如何被标准化的

如果 action 抛出:

  • AgenitiError,则保留其 codemessageissuesretryable
  • AbortError,runtime 返回 CANCELLED
  • 其他任意错误,runtime 返回 INTERNAL_ERROR

CLI Exit Code

内建 CLI 会把 runtime 错误码映射成进程退出码。

重要映射:

  • VALIDATION_ERROR -> 2
  • AUTHENTICATION_ERROR -> 3
  • AUTHORIZATION_ERROR -> 3
  • ACTION_NOT_FOUND -> 4
  • EXTERNAL_SERVICE_ERROR -> 5
  • TIMEOUT -> 124
  • CANCELLED -> 130
  • 其他错误 -> 1

输出保证

Ageniti 只返回可安全序列化为 JSON 的结果。

如果 action 定义了输出 schema,runtime 会在返回成功前再做一次输出校验。这使得返回 envelope 可以稳定用于 CLI 输出、MCP structured content、自动化测试和机器间集成。