下载 OpenAPI 规范:下载
Brabo API。除了四个明确记录了不同 content-type 的端点(两个 SSE 端点、指标端点,以及会重定向的 OAuth 回调)之外,每个端点都返回 JSON。
访问令牌是一个 15 分钟有效的 EdDSA JWT,通过
Authorization: Bearer <token> 出示。公钥位于
GET /.well-known/jwks.json。
refresh 不是 JWT:它是一个不透明令牌,存放在
httpOnly cookie(brabo_refresh、Path=/auth、SameSite=Strict)中,
从不出现在任何响应体中。POST /auth/refresh 和
POST /auth/logout 读取该 cookie,并要求 X-CSRF-Token 头
与 brabo_csrf cookie 一致。
轮换是强制的。 每次 refresh 都会消耗所出示的令牌并 签发另一个。重复出示已被消耗的令牌是被盗的特征, 会撤销整个令牌族——包括合法的会话。一个 同时发起两次并发 refresh 的客户端会把自己踢下线;请只保留一个进行中的 调用。
/internal/* 路由是 api ↔ engine 之间的接口,位于 JWT 之外:
它们要求带有共享机密的 X-Brabo-Service-Token 头。
用户令牌打不开其中任何一个。
NestJS 默认格式:{ "statusCode": number, "message": string | string[], "error": string }。
| 代码 | 何时 |
|---|---|
400 |
请求体无效——ValidationPipe 以 whitelist 和 forbidNonWhitelisted 运行,因此未知字段也会失败 |
401 |
没有令牌、令牌已过期或凭据无效 |
403 |
已认证但没有 RBAC 要求的角色,或服务令牌错误 |
404 |
资源不存在或对调用方不可见 |
409 |
状态冲突——无效的会话状态转换、资源已存在 |
429 |
速率限制或渐进式锁定 |
认证路由对不存在的邮箱、错误的密码和被锁定的账户 刻意给出无法区分的响应:相同的响应体、相同的状态码、相同的 耗时。这正是防止账户枚举的手段,而不是诊断上的 缺陷。
基于 Postgres 的滑动窗口:每 60 秒每个用户 300 次请求、每个 IP 600 次请求,
可配置。@Public() 路由和服务路由豁免——保护
/auth/* 的是渐进式锁定,而不是速率限制。
不存在的邮箱、错误的密码和被锁定的账户都返回完全相同的 401 响应——相同的响应体,相同的状态码。
| email required | string |
| senha required | string |
{- "email": "fulano@brabo.dev",
- "senha": "correct horse battery staple"
}{- "accessToken": "string",
- "expiresIn": 900,
- "locale": "pt-BR"
}无论是新地址还是已注册地址都返回 202——响应不会透露账户是否存在。第二种情况下不会创建任何内容,地址所有者会收到通知。
| email required | string 账户的邮箱。 |
| senha required | string >= 12 characters 密码。至少 12 个字符;不要求大写字母、数字或符号。 |
| nome | string |
{- "email": "fulano@brabo.dev",
- "senha": "correct horse battery staple",
- "nome": "Fulano de Tal"
}{- "message": "If the address is available, we've sent an email."
}流程 gate 的声明式索引(ADR 0054)。界面从这里推导存在哪些步骤以及每一步由谁判定,而不是在代码中重复这份列表——过去正是这样在无人察觉中过时的。
{- "version": 1,
- "gates": [
- {
- "id": "qa-verificada",
- "fluxo": "pr",
- "dono": "area-qa",
- "entrada": [
- "pr-aberta"
], - "entregavel": "veredito-qa",
- "aprovacaoHumana": true,
- "severidade": "block"
}
]
}为已声明的 blame 待办项(PHASE 26b)打基础——逐行标注编辑器的界面尚不存在。当文件超过标注行数上限时,truncated 会发出提示。
| projectId required | string |
| path required | string 要标注的文件路径。 |
| ref | string 分支、tag 或 sha。缺省时使用仓库的默认分支。 |
{- "ref": "dev",
- "path": "apps/api/src/main.ts",
- "lines": [
- {
- "line": 12,
- "commitSha": "0f3e8181fbd010b10c78db17b90ecb35fb8cc89c",
- "author": "Daniel Souza",
- "authorDate": "2026-08-04T12:00:00.000Z",
- "summary": "fix(api): fix the credential resolver"
}
], - "truncated": false
}为丰富的下拉菜单(PHASE 26b)打基础——目前 ref 选择器是普通文本框。ahead/behind 相对于仓库的默认分支;提供商无法计算时为 null(诚实的降级,而非错误)。它不是 listBranches(Gitflow 引导使用的那个)——而是一个独立的操作,因为丰富信息需要为每个分支额外调用一次提供商。
| projectId required | string |
{- "items": [
- {
- "name": "feature/x",
- "commitSha": "0f3e8181fbd010b10c78db17b90ecb35fb8cc89c",
- "protected": false,
- "ahead": 3,
- "behind": 0,
- "pullRequest": {
- "number": 42,
- "state": "open"
}, - "producedBy": {
- "agentId": "dev-pieces",
- "moduleId": "pieces"
}
}
], - "truncated": false
}UTF-8,超过字节上限时截断并带 truncated: true。逃出项目文件夹的路径会被 RN-095 的中央检查以 400 拒绝——绝不用 404,那会招来探测。
| projectId required | string |
| path required | string 文件路径。 |
| ref | string 分支、tag 或 sha。 |
{- "ref": "dev",
- "path": "apps/api/src/main.ts",
- "content": "import { NestFactory } from '@nestjs/core';\n",
- "truncated": false,
- "bytes": 1284
}为可导航列表(PHASE 26b)打基础——目前 diff 只能通过已知 id 访问(例如从 Approvals 进入);此路由解决了无需记住 id 也能找到它的问题。当列表超过单次调用上限时,truncated 会发出提示。
| projectId required | string |
| state | string 枚举: "open" "merged" "closed" 缺省时列出所有状态。 |
{- "items": [
- {
- "id": "2401938475",
- "number": 42,
- "title": "fix(api): the agents area is born with the project",
- "author": "daneiel",
- "state": "open",
- "sourceBranch": "feature/x",
- "targetBranch": "dev",
- "updatedAt": "2026-08-04T12:00:00.000Z"
}
], - "truncated": false
}patch 为 null 用于区分提供商没有交付文本(二进制文件或补丁过大)与文本返回为空这两种情况。把两者混为一谈,界面就会说一个已更改的二进制文件没有变化。当文件列表超过上限时,truncated 字段会发出提示。
| projectId required | string |
| pullRequestId required | string |
{- "pullRequestId": "pr-1",
- "files": [
- {
- "path": "apps/api/src/main.ts",
- "previousPath": null,
- "status": "modified",
- "additions": 12,
- "deletions": 3,
- "patch": "@@ -1,3 +1,4 @@\n+import x\n"
}
], - "truncated": false
}搜索不是 git 契约中的操作:它是在树和内容之上组合而成的,因此是唯一一个成本随仓库大小增长的读取。三项预算为其设限——遍历的目录数、打开的文件数和返回的匹配数——truncated 表示它已停止。filesScanned 显示它产生的成本。
| projectId required | string |
| q required | string 搜索词,2 到 200 个字符。 |
| ref | string 分支、tag 或 sha。 |
| path | string 要搜索的子树。缩小范围才能避免截断。 |
{- "ref": "dev",
- "path": "",
- "query": "NestFactory",
- "matches": [
- {
- "path": "apps/api/src/main.ts",
- "line": 12,
- "text": " const app = await NestFactory.create(AppModule);"
}
], - "filesScanned": 87,
- "truncated": true
}按契约设计不递归:标签页按需导航,而请求大型仓库的整棵树正是本阶段禁止的流量放大器。当该层级超过条目上限时,truncated 会发出提示。
| projectId required | string |
| ref | string 分支、tag 或 sha。缺省时使用仓库的默认分支。 |
| path | string 要列出的目录。缺省或为空即根目录。 |
{- "ref": "dev",
- "path": "apps/api",
- "entries": [
- {
- "path": "apps/api/src/main.ts",
- "name": "main.ts",
- "type": "file",
- "size": 1284
}
], - "truncated": false
}同步执行:只有在整个引导过程(永久分支、保护规则、模板)运行完毕后才会返回响应——背后没有 worker 也没有队列。它是幂等且可恢复的,因此失败后再次调用会从失败的步骤继续。通过 GET /projects/:id/git/bootstrap 跟踪进度并读取失败原因。
| projectId required | string |
| provider required | string 枚举: "local" "github" "gitlab" |
| name required | string 仓库在提供商上的名称。 |
| visibility required | string 枚举: "public" "private" |
| namespace | string 组织或群组。省略时,仓库创建在登记凭据者的个人账户下。 |
{- "name": "checkout",
- "visibility": "private",
- "namespace": "acme"
}{- "repository": {
- "id": "01JC4Z0000REPOSITORIO000001",
- "projectId": "01JC4Z0000PROJETO0000000001",
- "provider": "github",
- "externalId": "904837261",
- "defaultBranch": "main",
- "visibility": "private",
- "origin": "created",
- "provisionedBy": "01JC4Z0000USUARIO0000000001",
- "createdAt": "2026-07-23T10:00:00.000Z",
- "updatedAt": "2026-07-23T10:00:00.000Z"
}, - "bootstrap": {
- "step": "create_dev_branch",
- "status": "running"
}
}用 getRepo 校验访问权限,并生成引导将要执行的计划(dry-run)——缺失的分支、缺失的保护规则、文件。不会在仓库上执行任何操作:之后通过 plan/approve 或 plan/skip 做决定。重新接入同一个仓库会收敛(重新生成计划而不会重复记录)。
| projectId required | string |
| provider required | string 枚举: "local" "github" "gitlab" |
| externalId required | string 仓库在提供商上的标识符:github 上为 |
{- "externalId": "acme/checkout"
}{- "repository": {
- "id": "01JC4Z0000REPOSITORIO000001",
- "projectId": "01JC4Z0000PROJETO0000000001",
- "provider": "github",
- "externalId": "904837261",
- "defaultBranch": "main",
- "visibility": "private",
- "origin": "created",
- "provisionedBy": "01JC4Z0000USUARIO0000000001",
- "createdAt": "2026-07-23T10:00:00.000Z",
- "updatedAt": "2026-07-23T10:00:00.000Z"
}, - "plan": {
- "generatedAt": "2026-08-01T23:45:00.000Z",
- "steps": [
- {
- "step": "create_qa_branch",
- "actionType": "git_branch_create",
- "payload": {
- "branchName": "qa",
- "fromRef": "dev"
}
}
], - "diagnostics": [
- {
- "kind": "extra_branch",
- "detail": {
- "branchName": "develop",
- "protected": false
}
}
]
}, - "alreadyAdopted": false
}protect_branches 在免费计划的私有仓库上会失败,向导会事先就此发出警告。没有这个出口,唯一的按钮是“重试”,而它总会因同样的原因失败——provision_failed 又会让仪表盘把项目重定向回置备页面,使其永远无法进入。只有保护步骤的失败可以被确认:它是最后一步,也是唯一一个失败后仍留下可用仓库的步骤。产品自身的合并锁(RN-006)不依赖提供商的保护,会继续生效。
| projectId required | string |
{- "status": "provisioned"
}存在计划且 decision: null 是什么都不会运行的状态——等待批准或按原样接入(RN-045)。
| projectId required | string |
{- "plan": {
- "generatedAt": "2026-08-01T23:45:00.000Z",
- "steps": [
- {
- "step": "create_qa_branch",
- "actionType": "git_branch_create",
- "payload": {
- "branchName": "qa",
- "fromRef": "dev"
}
}
], - "diagnostics": [
- {
- "kind": "extra_branch",
- "detail": {
- "branchName": "develop",
- "protected": false
}
}
]
}, - "decision": null,
- "decidedAt": null,
- "decidedBy": null
}批准是全有或全无的:批准零散的步骤会破坏 dev←main, qa←dev, rc←qa 级联。实际运行的是在执行时重新推导的计划——等于或小于所展示的,绝不会更大。
| projectId required | string |
| planGeneratedAt required | string <date-time> 你所看到的计划的 |
{- "planGeneratedAt": "2026-08-01T23:45:00.000Z"
}{- "repository": {
- "id": "01JC4Z0000REPOSITORIO000001",
- "projectId": "01JC4Z0000PROJETO0000000001",
- "provider": "github",
- "externalId": "904837261",
- "defaultBranch": "main",
- "visibility": "private",
- "origin": "created",
- "provisionedBy": "01JC4Z0000USUARIO0000000001",
- "createdAt": "2026-07-23T10:00:00.000Z",
- "updatedAt": "2026-07-23T10:00:00.000Z"
}, - "bootstrap": {
- "step": "create_dev_branch",
- "status": "running"
}
}记录决定,不触碰仓库。计划作为刻意未应用内容的证据保留,引导游标也不会被改动——没有任何步骤运行。
| projectId required | string |
| planGeneratedAt required | string <date-time> 你所看到的计划的 |
{- "planGeneratedAt": "2026-08-01T23:45:00.000Z"
}{- "repository": {
- "id": "01JC4Z0000REPOSITORIO000001",
- "projectId": "01JC4Z0000PROJETO0000000001",
- "provider": "github",
- "externalId": "904837261",
- "defaultBranch": "main",
- "visibility": "private",
- "origin": "created",
- "provisionedBy": "01JC4Z0000USUARIO0000000001",
- "createdAt": "2026-07-23T10:00:00.000Z",
- "updatedAt": "2026-07-23T10:00:00.000Z"
}, - "bootstrap": {
- "step": "create_dev_branch",
- "status": "running"
}
}{- "id": "01JC4Z0000REPOSITORIO000001",
- "projectId": "01JC4Z0000PROJETO0000000001",
- "provider": "github",
- "externalId": "904837261",
- "defaultBranch": "main",
- "visibility": "private",
- "origin": "created",
- "provisionedBy": "01JC4Z0000USUARIO0000000001",
- "createdAt": "2026-07-23T10:00:00.000Z",
- "updatedAt": "2026-07-23T10:00:00.000Z"
}不是 JSON:而是 exposition 格式的 text/plain。出于必要而公开(抓取器不携带令牌),并通过网络加以限制——生产环境的 Ingress 在边缘阻止此路径,NetworkPolicy 只允许监控命名空间访问。
# HELP oban_queue_depth Oban queue depth # TYPE oban_queue_depth gauge oban_queue_depth{queue="default",state="available"} 0
供一行命令安装器使用,它运行在未配置邮件传输的环境中:生成的 .env 不含任何邮件变量,MAIL_TRANSPORT 回落为 log,而常规注册会等待一封永远不会到达的邮件。账户一出生即已验证,因为运行安装器的人已经证明了比控制一个邮箱更强的东西——他们控制着这台机器、.env 和 Docker 守护进程。常规注册不受影响。当安装中已有任何用户时以 409 拒绝:条件针对的是安装,而非这个邮箱,因此迁移恢复会通过同一检查让这一步保持静默。密码绝不在此生成,除了 argon2id 哈希外也绝不存储在任何地方;个人 workspace(RN-410)在同一事务中创建。
| email required | string 所有者的邮箱,在 TTY 中输入。在写入任何内容之前进行规范化;安装器会把它记录在标记中,因为邮箱用于识别身份,并不是机密。 |
| senha required | string 无回显读取,并由安装器确认。用完即弃——绝不写入 |
| nome | string 显示名称。省略时:个人 workspace 的名称回落为邮箱的本地部分(RN-410)——不会编造任何东西。 |
{- "email": "voce@exemplo.dev",
- "senha": "uma frase longa e minha",
- "nome": "Fulana de Tal"
}{- "userId": "01JC4Z0000USUARIO0000000001",
- "email": "voce@exemplo.dev",
- "workspaceId": "01JC4Z0000WORKSPACE00000001"
}注册表只做描述,不做执行:没有任何 gate 会因为它而被强制执行,在此修改字段也不会改变任何行为。每个 gate 都说明其通过的证据存放在哪里(evidencia),因为并非所有证据都在事件日志中。
{- "version": 1,
- "gates": [
- {
- "id": "qa-verificada",
- "fluxo": "pr",
- "dono": "area-qa",
- "entrada": [
- "pr-aberta"
], - "entregavel": "veredito-qa",
- "verificacao": "script",
- "severidade": "block",
- "aprovacao_humana": true,
- "status": "active",
- "evidencia": { },
- "backlog": "ADR 0053"
}
]
}如果同一 name 已存在哈希相同的版本,则不会创建新版本——而是返回已有版本(幂等命中不会改变 HTTP 状态:为简单起见始终为 201——响应体与新建时完全相同)。
| name required | string |
| version required | string |
| body required | string |
| hash required | string |
{- "name": "dev-agent-kickoff",
- "version": "3",
- "body": "You are the dev agent for the {{modulo}} module...",
- "hash": "sha256:9f2c..."
}{- "name": "dev-agent-kickoff",
- "version": "3",
- "body": "You are the dev agent for the {{modulo}} module...",
- "hash": "sha256:9f2c...",
- "active": true
}指定 version 时按相等查找;省略时查找最新的 active 版本。模板(或所请求的版本)不存在时返回 404。
| name required | string |
| version | string 示例: version=3 |
{- "name": "dev-agent-kickoff",
- "version": "3",
- "body": "You are the dev agent for the {{modulo}} module...",
- "hash": "sha256:9f2c..."
}供一行命令安装器在创建第一个账户后立即使用:本地代理需要一份凭据,而此前唯一存在的凭据绑定在项目上——而此时安装中还没有任何项目。密钥对在机器上生成,只有公开的一半到达这里;请把返回的 id 作为 kid 写入私有 JWK(RN-475),这是磁盘上的文件与服务器上公开一半之间唯一的关联。请求体中故意没有 userId:所有者是该安装的唯一用户,由 api 解析,因此持有服务令牌绝不意味着可以选择为谁签发凭据。注册即替换:所有者现有的机器密钥在同一事务中被撤销,因此重装的机器可以工作,也不可能存在上千把机器密钥。项目密钥(ADR 0118)不受影响。
| name required | string 供人以后认出这台机器的名称,显示在它所服务的每个项目的设备密钥列表中。不要求唯一。安装器会建议主机名;api 不会从中推导任何东西。 |
| publicKeyJwk required | string Ed25519 公钥 JWK(RFC 8037),序列化为 JSON。密钥对在机器上生成,私有的一半从不传输——携带 |
{- "name": "servidor-de-casa",
- "publicKeyJwk": "{\"kty\":\"OKP\",\"crv\":\"Ed25519\",\"x\":\"…\"}"
}{- "id": "01JC4Z0000CHAVE000000000001",
- "userId": "01JC4Z0000USUARIO0000000001",
- "name": "servidor-de-casa",
- "createdAt": "2026-09-12T12:00:00.000Z",
- "replacedKeyIds": [ ]
}绝不会因为某一个提供商而失败:每个提供商在报告中各自作答,附带被跳过的原因以及失败时的来源。未响应的提供商会被跳过,而不是被标记为不可用。
{- "porProvider": [
- {
- "provider": "openai",
- "descobertos": 3,
- "reencontrados": 1,
- "indisponibilizados": 0,
- "pulado": "sem_capability",
- "origemDaFalha": "infra",
- "detalhe": "openai responded with status 401"
}
]
}与 Backlog 标签页相同的 epic → story → task 树,经由同一用例(每个项目三次读取,绝无 N+1)。PO 借此看到孤立的 epic 和没有任务的 story,而不是重新创建已有内容(RN-164)。
| projectId required | string |
[- {
- "id": "01JC4Z0000EPICO000000000001",
- "projectId": "01JC4Z0000PROJETO0000000001",
- "sessionId": "01JC4Z8QK3M7YV2N5T9B0PXHRA",
- "title": "Shopping cart",
- "description": "Everything the buyer does before paying.",
- "createdAt": "2026-07-25T08:30:00.000Z",
- "updatedAt": "2026-07-25T08:30:00.000Z",
- "stories": [
- {
- "id": "01JC4Z0000HISTORIA000000001",
- "epicId": "01JC4Z0000EPICO000000000001",
- "projectId": "01JC4Z0000PROJETO0000000001",
- "sessionId": "01JC4Z8QK3M7YV2N5T9B0PXHRA",
- "title": "Add item to cart",
- "description": "As a buyer, I want to gather items before paying.",
- "rf": [
- "The cart accepts up to 50 items"
], - "rnf": [
- "The response stays under 200 ms at p95"
], - "businessRuleIds": [
- "RN-014"
], - "dod": [
- "Unit tests green"
], - "dor": [
- "Module defined"
], - "moduleIds": [
- "api"
], - "status": "ready",
- "proposedReady": true,
- "returnedReason": "DoD too generic — spell out the acceptance criteria.",
- "returnedAt": "2026-08-02T14:00:00.000Z",
- "archivedAt": null,
- "archivedReason": null,
- "createdAt": "2026-07-25T09:00:00.000Z",
- "updatedAt": "2026-07-25T09:40:00.000Z",
- "tasks": [
- {
- "id": "01JC4Z0000TAREFA00000000001",
- "storyId": "01JC4Z0000HISTORIA000000001",
- "title": "POST /cart/items endpoint",
- "description": "Accepts SKU and quantity; validates stock.",
- "status": "in_progress",
- "assignedTo": "dev-api",
- "blocked": false,
- "blockedReason": null,
- "blockedOrigin": null,
- "gateStatus": null,
- "gateCorrectionCount": 0,
- "module": "api",
- "createdAt": "2026-07-25T10:00:00.000Z",
- "updatedAt": "2026-07-25T12:30:00.000Z"
}
]
}
]
}
]项目所有会话中的每一条 artifact.business_rule——而不仅是当前会话的,那曾是 PO 启动上下文的上限——带有完整的 description 以及哪些 story 已引用每条规则。uncoveredCount 是待办项:没有任何 story 覆盖的规则(RN-164)。完全没有规则的项目返回 200 和空列表:“尚未捕获任何规则”不是错误。
| projectId required | string |
{- "rules": [
- {
- "id": "evt_01jc4z0000regra0000000001",
- "title": "Cart accepts at most 50 items",
- "description": "Above that, the addition is refused with 409.",
- "coveredByStoryIds": [
- "01JC4Z0000HISTORIA000000001"
], - "covered": true
}
], - "uncoveredCount": 2
}仅由 engine 调用,当 Engine.Actions.TerminalExecutor 判定命令应在真实容器内执行时(execution_mode: container 且有已登记的 running 记录)。代理到 ContainerBrokerPort.exec(ADR 0130/0134)——broker 五个操作中最后一个获得真实调用方的。响应中的 sucesso: false——绝不是抛出的错误——是 broker 拒绝或不可达时的正常形态:在登记的 running 记录与本次调用之间,容器可能已在外部死亡或被移除(RN-486,登记状态与观测状态绝不合并),这是命令失败,而非传输错误。
| projectId required | string |
| comando required | string shell 命令,由 |
| cwd | string 容器内的工作目录。已由 engine 从宿主机路径转换(RN-492)——此 DTO 不会重复该转换。省略时在 |
| timeoutMs | number 超时时间,以毫秒计。省略时使用 broker 的默认值。 |
object AT-612: run in the task QA instance instead of the work container. Set by the engine from the gate context, never by the model. |
{- "comando": "npm test",
- "cwd": "/work/.worktrees/dev-api",
- "timeoutMs": 300000,
- "alvo": {
- "qa": true,
- "taskId": "e6e9d88a-9b63-468a-aec3-b7a11de27af8"
}
}{- "sucesso": true,
- "exitCode": 0,
- "output": "ok\n",
- "timedOut": false,
- "motivo": "o broker de container não respondeu em http://broker:8090: timeout"
}项目标识、执行模式以及 Architect 当前的镜像决定。broker 在把任何东西交给守护进程之前都会重新校验——从这里读取并不等于信任它,拒绝时会指明字段。不返回任何绝对路径:绑定源由守护进程依据宿主机文件系统解析,而来自 api 容器内部的路径会静默挂载一个空文件夹。传递的是 localizacao(RN-503)——使用 broker 两个根目录中的哪一个,以及该根目录未覆盖的相对段。
| projectId required | string |
| papel | string 值: "qa" |
| task | string |
{- "projectId": "f52be111-0000-4000-8000-000000000000",
- "projectSlug": "exp002",
- "workspaceId": "aaaaaaaa-0000-4000-8000-000000000000",
- "workspaceDirName": "exp002-f52be111",
- "executionMode": "container",
- "localizacao": {
- "tipo": "gerenciada",
- "segmento": "loja",
- "motivo": "o projeto está no modo \"runner\"…"
}, - "usuarioDaPasta": {
- "uid": 1000,
- "gid": 1000
}, - "imagem": {
- "image": "node:22-bookworm-slim",
- "network": "none",
- "resources": {
- "cpus": 2,
- "memoryMb": 4096,
- "pidsLimit": 512
}
}, - "imagemVersao": 3,
- "comandoDoApp": "string",
- "ports": [
- {
- "host": 8080,
- "container": 3000,
- "protocol": "tcp"
}
], - "dns": [
- "string"
], - "extraHosts": [
- {
- "name": "api.local",
- "ip": "10.0.0.5"
}
], - "qa": {
- "chave": "a1b2c3d4",
- "services": [
- { }
]
}
}返回干净的源地址(不嵌入凭据),对于远程提供商,还返回即时解密的 workspace 所有者令牌(RN-058)。使用方按每次调用注入令牌,绝不写入文件——.git/config 位于 RN-075 授予 dev agent 自动批准读取权限的文件夹内。
| projectId required | string |
{- "kind": "remote",
- "defaultBranch": "main",
- "token": "string",
- "username": "x-access-token"
}仅由 engine 调用,在 runner 通过通道推送 mirror_sync_result 之后——与 workspace_confirm 走同一路径(runner → 通道 → engine → api),绝无第二套机制:engine 负责报告,不写表。这是遥测,不是领域事件:绝不写入 session_events(一轮镜像没有会话,而 session_id 是 NOT NULL,与把 rag_searches 做成表的理由相同),也绝不是 proposed_action(镜像写入是用户声明的配置,不是代理请求执行操作)。决定结果的是 ok——绝不是计数或消息是否存在:复制了 0 个文件的一轮是正常的,绝不能与从未运行的一轮无法区分。记录绝不会破坏它所测量的东西:调用此接口时复制已经完成,engine 只会在此记录拒绝。目标在此期间被清除的项目仍然会记录——这一轮确实发生了,而它的错误通常正是解释问题所在的线索。
| projectId required | string |
| ok required | boolean 复制的真实结果,由本地代理在完成后报告——绝不是在这一轮结束前乐观地报“ok”。 |
| destination | string 这一轮实际解析到的目标(成功时)或尝试使用的目标(失败时),位于用户的机器上。在记录中冻结:把项目当前的目标显示在昨天的复制旁边,等于界面在对一个那轮从未触及的文件夹做出断言。当失败与目标本身有关时省略。 |
| filesCopied | number 这一轮复制的普通文件数。 |
| filesSkipped | number git 列出的、不是普通文件的条目——嵌套仓库(dev agent 的 worktree 就是一个)、符号链接,或在列出和复制之间消失的文件。 |
| filesRefused | number 被逐文件防护拒绝的目标(逃出目标目录的符号链接)。会被计数,绝不吞掉。 |
| error | string 具名的失败消息。很长时会截断存储,并说明已截断。它绝不会抹去最近一次成功的同步——哪条是当前的,由比较两个时间戳决定。 |
{- "ok": true,
- "destination": "/home/you/mirrors/store",
- "filesCopied": 412,
- "filesSkipped": 3,
- "filesRefused": 0,
- "error": "o espelho não conseguiu listar o trabalho com o git: not a git repository"
}{- "recorded": true,
- "status": "synced"
}当前 module_map 中每个模块一条记录:它消费什么(dependeDe,即映射中的 dependsOn)以及它暴露什么(expoe,来自 Architect 当前的 artifact.module_contracts,未声明时为 null)。dev agent 读取它,而不是打开另一个模块的 worktree(ADR 0200)。没有契约的项目返回 200 和 status: sem_contratos。
| projectId required | string |
{- "status": "sem_contratos",
- "version": 2,
- "modulos": [
- {
- "modulo": "game-session",
- "dependeDe": [
- "board-engine",
- "scoring"
], - "expoe": [
- {
- "tipo": "funcao",
- "assinatura": "placePiece(board: Board, piece: Piece, pos: Pos): Board",
- "descricao": "Returns a new board; never mutates the one passed in."
}
]
}
], - "contratosForaDoMapa": [ ]
}与 analise:funil 脚本(ADR 0089)相同的报告——会话 → 提交 → PR → 合并的漏斗、真实交付周期和真实部署频率——经由相同的纯函数和相同的查询(apps/api/src/application/services/funil-metrics.ts),因此两者对同一事实绝不会出现分歧。补上 docs/fluxo.yml(角色 po,输入 metricas-de-produto,此前为 status: lacuna)(RN-407)。
| projectId required | string |
{- "project": {
- "id": "01JC4Z0000PROJETO0000000001",
- "name": "exp003"
}, - "totalActionsConsidered": 18,
- "funnel": {
- "etapas": [
- {
- "etapa": "sessão produziu commit",
- "sessoes": 3,
- "taxaDaEtapaAnterior": 0.67
}
], - "sessoesComCommit": [
- "string"
], - "sessoesComPr": [
- "string"
], - "sessoesComMerge": [
- "string"
]
}, - "leadTimes": {
- "perSession": [
- {
- "sessionId": "01JC4Z0000SESSAO000000000001",
- "primeiroCommitEm": "2026-08-01T10:00:00.000Z",
- "primeiroMergeEm": "2026-08-01T12:00:00.000Z",
- "leadTimeMs": 7200000
}
], - "averageMs": 7200000
}, - "deploymentFrequency": [
- {
- "dia": "2026-08-01",
- "merges": 2
}
]
}Automatic, no proposal: it only reaches what carries the QA label (the broker guarantees it). Idempotent.
| projectId required | string |
| taskId required | string |
{- "sucesso": true,
- "status": "provisioning",
- "chave": "a1b2c3d4",
- "motivo": "string"
}Records the row, then asks the broker. sucesso: false is the NORMAL shape of a refused broker. At most ONE active instance per project: another task active -> 409 instancia_de_qa_ocupada. runner projects -> 409 qa_nao_suporta_runner.
| projectId required | string |
| taskId required | string |
{- "sucesso": true,
- "status": "provisioning",
- "chave": "a1b2c3d4",
- "motivo": "string"
}仅由 engine 调用,在 runner 连接并通过通道发送 workspace_confirm 之后。runner 是路径的事实来源——api 在对其进行词法重新校验后(即使来自 runner,系统根目录/与 Brabo 重叠仍被禁止),用它报告的值覆盖 workspacePath。幂等:以相同路径重新连接不会再次写入。
| projectId required | string |
| path required | string runner 在宿主机上确认的绝对路径——事实来源(RN-423)。写入之前在此进行词法重新校验;无效时返回 400,绝不写入。 |
| sessionId | string <uuid> 项目最近的会话( |
| actorId | string <uuid> 请求 runner 票据的人(确认路径的套接字的所有者)——成为 |
{- "path": "/home/you/projects/store",
- "sessionId": "01JC4Z0000SESSAO00000000001",
- "actorId": "01JC4Z0000USUARIO0000000001"
}{- "verified": true,
- "workspacePath": "/home/you/projects/store",
- "changed": true
}The engine sends the task ids it still has in flight; every other active or failed instance is removed. Shared services and the QA network have no broker operation yet (declared gap).
| taskIdsEmVoo required | 数组:strings Task ids that still have a gate in flight; every other active instance is removed. |
| projectId | string <uuid> Limit the sweep to one project. |
{- "taskIdsEmVoo": [
- "string"
], - "projectId": "5a8591dd-4039-49df-9202-96385ba3eff8"
}{- "removidas": [
- "string"
], - "falhas": [
- {
- "taskId": "string",
- "motivo": "string"
}
]
}与人工路由相同的用例——没有第二条判定路径。未知的 searchId/chunkId 返回 400,engine 会将其转为错误的工具结果供模型纠正(RN-061),绝不会崩溃。
| projectId required | string <uuid> |
| searchId required | string <uuid> |
| chunkId required | string <uuid> |
| verdict required | string 枚举: "util" "irrelevante" |
| agent required | string 代理 slug——投票者。 |
{- "projectId": "5a8591dd-4039-49df-9202-96385ba3eff8",
- "searchId": "9b1a67f9-9477-48e5-8a6c-11b70245e1d9",
- "chunkId": "90e1f3df-a8a1-438e-8dd5-10e45398d051",
- "verdict": "util",
- "agent": "qa"
}{- "searchId": "9b1a67f9-9477-48e5-8a6c-11b70245e1d9",
- "chunkId": "90e1f3df-a8a1-438e-8dd5-10e45398d051",
- "verdict": "util",
- "rank": 0
}与 POST /projects/:projectId/rag/search(人工路由)相同的引擎,投射成 engine 的 rag_search 工具所期望的格式。
| projectId required | string <uuid> |
| query required | string |
| topK | number |
| sessionId | string <uuid> 代理正在运行的会话。缺省表示调用方没有会话——遥测记录仍会写入(RN-479),只是跳过 |
| agent | string 代理 slug,记录为遥测参与者( |
{- "projectId": "5a8591dd-4039-49df-9202-96385ba3eff8",
- "query": "how the parallelism cap is decided",
- "topK": 5,
- "sessionId": "f6567dd8-e069-418e-8893-7d22fcf12459",
- "agent": "dev-lead"
}{- "searchId": "9b1a67f9-9477-48e5-8a6c-11b70245e1d9",
- "hits": [
- {
- "chunkId": "90e1f3df-a8a1-438e-8dd5-10e45398d051",
- "path": "docs/adr/0080-chat-rag-pipeline-indexacao.md",
- "chunk": "string",
- "score": 0,
- "excerpt": "string"
}
], - "degraded": true
}代理接触 git、终端或花费的唯一入口。permissions.json 在创建时做出决定,deny 优先于授予代理的任何自主权。
| sessionId required | string |
| projectId required | string <uuid> |
| actionType required | string 枚举: "terminal" "git_commit" "git_push" "pr_open" "spend" "git_repo_create" "git_branch_create" "git_branch_protect" "write_file" "open_adr_pr" "git_merge" "open_infra_pr" "instruction_patch" "parallelize" "raise_max_parallel" "propose_execution_plan" "assess_implementability" "container_start" "container_stop" "container_remove" "container_recreate" "container_start_via_runner" "qa_environment_start" |
required | object 在此路由上始终是代理。 |
required | object |
{- "projectId": "01JC4Z0000PROJETO0000000001",
- "actionType": "terminal",
- "actor": {
- "kind": "agent",
- "id": "dev-api"
}, - "payload": {
- "command": "pnpm test"
}
}{- "id": "01JC4Z8QK3M7YV2N5T9B0PXHRC",
- "projectId": "01JC4Z0000PROJETO0000000001",
- "sessionId": "01JC4Z8QK3M7YV2N5T9B0PXHRA",
- "seq": 7,
- "actionType": "terminal",
- "payload": {
- "command": "pnpm test"
}, - "status": "pending",
- "resolvedPolicy": "require_approval",
- "actor": {
- "kind": "agent",
- "id": "dev-api"
}, - "decidedBy": null,
- "decidedAt": null,
- "rejectionReason": null,
- "executionResult": null,
- "createdAt": "2026-07-27T14:33:10.900Z",
- "updatedAt": "2026-07-27T14:33:10.900Z"
}一次调用拿到全部,与 Psychologist 的上下文对应。成员列表已排除选择不被画像的人,而窗口内用户的决定通过这里传递,因为它们不在事件日志中。engine 直接从 Postgres 读取事件本身——通过 HTTP 传输会更昂贵,却不会更正确。
| projectId required | string |
{- "competencyCatalog": [
- "TypeScript",
- "Elixir",
- "Kubernetes"
], - "members": [
- {
- "userId": "01JC4Z0000USUARIO0000000002",
- "name": "Senior Dev",
- "email": "dev@brabo.dev",
- "role": "developer"
}
], - "queuedHypotheses": [
- {
- "queueId": "01JC4Z0000FILA00000000000001",
- "hypothesisId": "01JC4Z0000HIPOTESE000000001",
- "agenteAlvo": "dev-api",
- "hipotese": "The instructions don't say when the task is done.",
- "sugestao": "Add an explicit definition-of-done criterion.",
- "confiancaPercent": 72
}
], - "currentProfiles": [
- {
- "userId": "01JC4Z0000USUARIO0000000002",
- "competency": "TypeScript",
- "level": "advanced",
- "rationale": "Fixed three generic typing errors without help."
}
], - "instructions": [
- {
- "agent": "dev-api",
- "version": 4,
- "content": "# dev-api\n\n…"
}
], - "decisions": [
- {
- "actionType": "terminal",
- "status": "denied",
- "rejectionReason": "Command would delete the directory.",
- "decidedBy": "01JC4Z0000USUARIO0000000001",
- "decidedAt": "2026-07-27T13:00:00.000Z"
}
], - "windowFrom": "2026-07-27T12:00:00.000Z"
}记录一次零画像的 anamnese 运行,使下一轮从 windowTo 开始——没有它,窗口永远不会前进,每次 tick 都要为同一批材料付费运行新一轮。
| sessionId required | string |
| projectId required | string <uuid> |
| windowFrom required | string <date-time> |
| windowTo required | string <date-time> |
| eventCount required | number |
{- "projectId": "01JC4Z0000PROJETO0000000001",
- "windowFrom": "2026-07-27T12:00:00.000Z",
- "windowTo": "2026-07-27T12:15:00.000Z",
- "eventCount": 12
}{- "runId": "01JC4Z0000RODADA00000000001",
- "profiles": [
- { }
]
}制品就是 artifact.c4_diagram 事件:不可变、带版本、有作者,与 artifact.module_map 并列。需要当前的 module_map——没有它就没有可绘制的 Container 层级(400)。
| sessionId required | string |
| projectId required | string <uuid> |
| systemName required | string 系统/项目名称,用作图的标签。 |
| systemDescription | string |
数组:objects (C4AtorInternalDto) Context 层级的外部参与者(Simon Brown)。Container 层级的容器不放在这里:它们来自项目当前的 module_map。 |
{- "projectId": "5a8591dd-4039-49df-9202-96385ba3eff8",
- "systemName": "Brabo",
- "systemDescription": "Agent-orchestrated engineering platform.",
- "actors": [
- {
- "name": "User",
- "type": "person",
- "description": "Whoever operates the product via the web."
}
]
}{- "diagrama": {
- "systemName": "Brabo",
- "systemDescription": "Agent-orchestrated engineering platform.",
- "actors": [
- {
- "name": "User",
- "type": "person",
- "description": "Whoever operates the product through the web."
}
], - "contextDiagram": "C4Context\n title Context Diagram -- Brabo\n ...",
- "containerDiagram": "C4Container\n title Container Diagram -- Brabo\n ..."
}, - "version": 1
}completed(附裁决)、failed(附来源)或 dispensed(附理由)——lead 绝不会以半途的结果调用此路由:每次委派在这里诞生时就已经有了结论。
| sessionId required | string |
| projectId required | string <uuid> |
| area required | string 进行委派的区域——“qa”(Phase 8b)或 “infra”(Phase 8c)。是文本,不是枚举。 |
| taskId | string <uuid> 委派背后的 backlog 任务(如存在)。QA(Phase 8b)总会发送它;Infra(Phase 8c)从不发送——委派针对的是会话,而不是任务。 |
| leadAgent required | string |
| subagent required | string |
| status required | string 枚举: "completed" "failed" "dispensed" |
| parecerArtifactId | string 此子专业裁决对应的 |
| failureOrigin | string 枚举: "infra" "modelo" "codigo" "politica" 失败的来源(ADR 0020/0038),绝不通过排除法——当 |
| failureReason | string |
| justification | string lead 为何选择不委派——当 |
{- "projectId": "01JC4Z0000PROJETO0000000001",
- "area": "qa",
- "taskId": "01JC4Z0000TASK00000000001",
- "leadAgent": "qa-lead",
- "subagent": "qa-automacao",
- "status": "completed",
- "parecerArtifactId": "evt_01jc4z0000parecer000000001",
- "failureOrigin": "infra",
- "failureReason": "iteration limit reached without emit_perf_seguranca_verdict",
- "justification": "story with no relevant performance NFR"
}{- "id": "01JC4Z0000DELEGACAO00000001",
- "projectId": "01JC4Z0000PROJETO0000000001",
- "sessionId": "01JC4Z0000SESSAO000000000001",
- "taskId": "01JC4Z0000TAREFA00000000001",
- "area": "qa",
- "leadAgent": "qa-lead",
- "subagent": "qa-automacao",
- "status": "completed",
- "parecerArtifactId": "evt_01jc4z0000parecer000000001",
- "failureOrigin": null,
- "failureReason": null,
- "justification": null,
- "createdAt": "2026-07-30T12:00:00.000Z"
}一次调用即可拿到提示词所需的一切:完整的 story、已解析的业务规则以及适用的 ADR。module 将 ADR 限定为该 dev 的模块;未声明模块的 ADR 是横切的,始终包含在内。省略 module 会返回整个集合,这正是 QA 和 SecOps gate 复用同一上下文时所需要的。
| projectId required | string |
| taskId required | string |
| module | string 示例: module=api |
{- "task": {
- "id": "01JC4Z0000TAREFA00000000001",
- "storyId": "01JC4Z0000HISTORIA000000001",
- "title": "POST /cart/items endpoint",
- "description": "Accepts SKU and quantity; validates stock.",
- "status": "in_progress",
- "assignedTo": "dev-api",
- "blocked": false,
- "blockedReason": null,
- "blockedOrigin": null,
- "gateStatus": null,
- "gateCorrectionCount": 0,
- "module": "api",
- "createdAt": "2026-07-25T10:00:00.000Z",
- "updatedAt": "2026-07-25T12:30:00.000Z"
}, - "story": {
- "id": "01JC4Z0000HISTORIA000000001",
- "epicId": "01JC4Z0000EPICO000000000001",
- "projectId": "01JC4Z0000PROJETO0000000001",
- "sessionId": "01JC4Z8QK3M7YV2N5T9B0PXHRA",
- "title": "Add item to cart",
- "description": "As a buyer, I want to gather items before paying.",
- "rf": [
- "The cart accepts up to 50 items"
], - "rnf": [
- "The response stays under 200 ms at p95"
], - "businessRuleIds": [
- "RN-014"
], - "dod": [
- "Unit tests green"
], - "dor": [
- "Module defined"
], - "moduleIds": [
- "api"
], - "status": "ready",
- "proposedReady": true,
- "returnedReason": "DoD too generic — spell out the acceptance criteria.",
- "returnedAt": "2026-08-02T14:00:00.000Z",
- "archivedAt": null,
- "archivedReason": null,
- "createdAt": "2026-07-25T09:00:00.000Z",
- "updatedAt": "2026-07-25T09:40:00.000Z"
}, - "businessRules": [
- {
- "title": "Cart accepts at most 50 items",
- "description": "Above that, the addition is refused with 409."
}
], - "adrs": [
- {
- "title": "0007 — Postgres queue instead of Redis",
- "content": "## Context\n\n…",
- "securityRelevant": false
}
], - "siblingTasks": [
- {
- "id": "01J0000000000000000000000",
- "title": "Expose POST /login returning a JWT",
- "status": "todo"
}
], - "moduleOpenTasks": [
- {
- "id": "01J0000000000000000000000",
- "title": "Expose POST /links",
- "status": "todo",
- "storyTitle": "Members create short links"
}
], - "moduleOpenTasksTotal": 0,
- "moduleContract": {
- "modulo": "board-engine",
- "expoe": [
- {
- "tipo": "funcao",
- "assinatura": "placePiece(board: Board, piece: Piece, pos: Pos): Board",
- "descricao": "Returns a new board; never mutates the one passed in."
}
]
}, - "primeiraDoModulo": true,
- "decisoes": [
- {
- "id": "string",
- "questionId": "string",
- "pergunta": "string",
- "decisao": "string",
- "decididaEm": "2019-08-24T14:15:22Z"
}
], - "decisoesDoProjeto": [
- {
- "id": "string",
- "questionId": "string",
- "pergunta": "string",
- "decisao": "string",
- "decididaEm": "2019-08-24T14:15:22Z"
}
]
}| sessionId required | string |
| projectId required | string <uuid> |
| title required | string |
| description | string |
{- "projectId": "01JC4Z0000PROJETO0000000001",
- "title": "Shopping cart",
- "description": "Everything the buyer does before paying."
}{- "id": "01JC4Z0000EPICO000000000001",
- "projectId": "01JC4Z0000PROJETO0000000001",
- "sessionId": "01JC4Z8QK3M7YV2N5T9B0PXHRA",
- "title": "Shopping cart",
- "description": "Everything the buyer does before paying.",
- "createdAt": "2026-07-25T08:30:00.000Z",
- "updatedAt": "2026-07-25T08:30:00.000Z"
}用于重新水合对话式代理的历史,并读取其启动所需的制品。对应的人工路由受 RBAC 保护;这一个受服务令牌保护。latest=true 返回尾部(仍按 seq 升序)并忽略 afterSeq;types 将该页限定为这些事件类型。无论哪种情况,每页上限都是 200(ADR 0060)。
| sessionId required | string |
| projectId required | string |
| afterSeq | string 示例: afterSeq=40 |
| limit | string 示例: limit=200 |
| latest | string 示例: latest=true 获取日志尾部;忽略 |
| types | string 示例: types=artifact.product_brief,artifact.business_rule 以逗号分隔的事件类型(最多 20 个)。只有这些类型的事件计入 |
{- "items": [
- {
- "id": "01JC4Z8QK3M7YV2N5T9B0PXHRB",
- "sessionId": "01JC4Z8QK3M7YV2N5T9B0PXHRA",
- "seq": 41,
- "type": "chat.message",
- "actor": {
- "kind": "agent",
- "id": "dev-api"
}, - "payload": {
- "role": "assistant",
- "content": "I'll open the epic first."
}, - "createdAt": "2026-07-27T14:30:58.512Z"
}
], - "nextCursor": 41
}与人工路由相同的用例和相同的原子 seq 分配。Psychologist 的假设不经过这里:它们有自己的路由,附带证据校验。
| sessionId required | string |
| projectId required | string <uuid> |
| type required | string 见 |
| actorKind required | string 枚举: "user" "agent" "system" |
| actorId required | string 发出它的代理的 slug。 |
required | object |
{- "projectId": "01JC4Z0000PROJETO0000000001",
- "type": "agent.tool_call",
- "actorKind": "agent",
- "actorId": "dev-api",
- "payload": {
- "tool": "ler_arquivo"
}
}{- "id": "01JC4Z8QK3M7YV2N5T9B0PXHRB",
- "sessionId": "01JC4Z8QK3M7YV2N5T9B0PXHRA",
- "seq": 41,
- "type": "chat.message",
- "actor": {
- "kind": "agent",
- "id": "dev-api"
}, - "payload": {
- "role": "assistant",
- "content": "I'll open the epic first."
}, - "createdAt": "2026-07-27T14:30:58.512Z"
}响应中的 nextAction 是 engine 所遵从的:correct 退回给 dev,run_secops 推进 gate,done 将其交给用户,await_user 暂停周期直到未决问题得到回答(RN-860,不计入修正次数),blocked 表示修正上限已用尽。合并绝不自动进行——done 只表示轮到人来操作了。
| sessionId required | string |
| projectId required | string <uuid> |
| taskId required | string <uuid> |
| gate required | string 枚举: "qa" "secops" |
| veredito required | string 枚举: "approved" "changes_requested" "needs_user_decision"
|
| resumo required | string |
| itens required | 数组:strings 需要修改什么,逐项列出。 |
| pergunta | string 配合 |
| sugestao | string 配合 |
| openQuestionId | string <uuid> 配合 |
| maxCorrections | number 任务变为 |
{- "projectId": "01JC4Z0000PROJETO0000000001",
- "taskId": "01JC4Z0000TAREFA00000000001",
- "gate": "qa",
- "veredito": "changes_requested",
- "resumo": "Missing tests for the insufficient-stock path.",
- "itens": [
- "No test for a nonexistent SKU"
], - "pergunta": "Should a nonexistent code answer 404?",
- "sugestao": "404 with a plain message.",
- "openQuestionId": "4c8aa973-c398-4859-8f2e-3fe00dc96a12",
- "maxCorrections": 3
}{- "nextAction": "run_secops",
- "task": {
- "id": "01JC4Z0000TAREFA00000000001",
- "storyId": "01JC4Z0000HISTORIA000000001",
- "title": "POST /cart/items endpoint",
- "description": "Accepts SKU and quantity; validates stock.",
- "status": "in_progress",
- "assignedTo": "dev-api",
- "blocked": false,
- "blockedReason": null,
- "blockedOrigin": null,
- "gateStatus": null,
- "gateCorrectionCount": 0,
- "module": "api",
- "createdAt": "2026-07-25T10:00:00.000Z",
- "updatedAt": "2026-07-25T12:30:00.000Z"
}, - "openQuestionId": "4c8aa973-c398-4859-8f2e-3fe00dc96a12"
}以 offered 状态诞生。接受者是一个人,通过人工路由——代理不会激活代理。有两个例外。携带 product_brief、由一次“我准备好了——需求已验证”点击所请求的 Creative→PO 提议,会代表点击者被接受,以该人为参与者并在载荷中带有 implicito(RN-658,ADR 0185)。当 backlog 已被覆盖(至少一条业务规则,且没有一条缺少 story)、仓库为 local 且项目中没有 git 凭据时,PO 到 Arquiteto 的 handoff 由系统接受(RN-660,ADR 0186);aceiteAutomatico 说明是否发生,以及未发生的原因。无论哪种情况,响应随后都会带有 status: accepted。
| sessionId required | string |
| projectId required | string <uuid> |
| fromAgent required | string |
| toAgent required | string |
| artifactId | string 引发该 handoff 的制品对应的 session_event 的 ULID。 |
| seAusente | boolean 仅当还没有人拿到时(ADR 0182,RN-636):如果项目的任何会话中已有给该目标的待处理提议,则返回它( |
{- "projectId": "01JC4Z0000PROJETO0000000001",
- "fromAgent": "criativo",
- "toAgent": "po",
- "artifactId": "01JC4Z8QK3M7YV2N5T9B0PXHRB",
- "seAusente": true
}{- "id": "01JC4Z0000HANDOFF00000000001",
- "sessionId": "01JC4Z8QK3M7YV2N5T9B0PXHRA",
- "projectId": "01JC4Z0000PROJETO0000000001",
- "fromAgent": "criativo",
- "toAgent": "po",
- "artifactId": "01JC4Z0000ARTEFATO000000001",
- "status": "offered",
- "createdAt": "2026-07-24T10:00:00.000Z",
- "updatedAt": "2026-07-24T10:05:00.000Z",
- "desfecho": "criado",
- "aceiteAutomatico": {
- "aceito": true,
- "criterio": {
- "regras": 4,
- "cobertas": 4,
- "repositorio": "a_provisionar_local"
}, - "motivo": "regras_sem_historia"
}
}每条假设都需要引用本会话中确实存在的事件——编造的证据会以 400 被拒绝,这正是假设与意见的区别。本会话的上一轮将被取代。
| sessionId required | string |
| projectId required | string <uuid> |
| tier required | string 枚举: "leve" "pesada" 两个层级使用的确实是不同的模型;成本也不同。 |
| triggeredBy required | string 枚举: "auto" "manual" |
| eventCount required | number 被分析日志的大小。 |
| cause | string 枚举: "normal" "timeout" "kill" "crash" "node_shutdown" "unknown" 由 engine 分类的原因。决定是否需要 |
required | 数组:objects (HypothesisDraftDto) |
{- "projectId": "01JC4Z0000PROJETO0000000001",
- "tier": "leve",
- "triggeredBy": "auto",
- "eventCount": 128,
- "cause": "timeout",
- "hypotheses": [
- {
- "agenteAlvo": "dev-api",
- "observacao": "The agent reopened the same task three times.",
- "hipotese": "The instructions don't say when the task is done.",
- "sugestao": "Add an explicit definition-of-done criterion.",
- "confiancaPercent": 72,
- "evidenceEventIds": [
- "01JC4Z8QK3M7YV2N5T9B0PXHRB"
], - "terminationAnalysis": {
- "causa": "heartbeat_timeout",
- "estadoDaSessao": "closed_abnormally",
- "analise": "The agent stopped responding after a 90s tool call."
}
}
]
}{- "analysisId": "01JC4Z0000ANALISE0000000001",
- "hypotheses": [
- {
- "id": "01JC4Z0000HIPOTESE000000001",
- "projectId": "01JC4Z0000PROJETO0000000001",
- "sessionId": "01JC4Z8QK3M7YV2N5T9B0PXHRA",
- "analysisId": "01JC4Z0000ANALISE0000000001",
- "agenteAlvo": "dev-api",
- "observacao": "The agent reopened the same task three times.",
- "hipotese": "The instructions don't say when to consider the task done.",
- "sugestao": "Add an explicit definition-of-done criterion to the instruction.",
- "confiancaPercent": 72,
- "evidenceEventIds": [
- "01JC4Z8QK3M7YV2N5T9B0PXHRB"
], - "terminationAnalysis": {
- "causa": "heartbeat_timeout",
- "estadoDaSessao": "closed_abnormally",
- "analise": "The agent stopped responding after a 90s tool call."
}, - "status": "proposed",
- "decidedBy": null,
- "decidedAt": null,
- "createdAt": "2026-07-26T18:00:00.000Z",
- "updatedAt": "2026-07-26T18:00:00.000Z"
}
]
}内容来自 proposed_action 自身的载荷:基础设施制品从不接触 worktree,与 ADR 相同。
| prActionId required | string |
| projectId required | string |
{- "title": "api's production Dockerfile",
- "files": [
- {
- "path": "docker/api/Dockerfile.prod",
- "content": "FROM node:24.11.1-alpine3.21 AS build\n…"
}
]
}当前的 module_map 加上相关的基础设施 ADR,再加上 Architect 的模块路由(ADR 0131)——Infra Lead 在提议 container_start 时从中选择的候选项。
| projectId required | string |
{- "moduleMap": {
- "id": "01JC4Z0000MODULEMAP00000001",
- "projectId": "01JC4Z0000PROJETO0000000001",
- "sessionId": "01JC4Z8QK3M7YV2N5T9B0PXHRA",
- "modules": [
- {
- "name": "api",
- "stack": "NestJS + Drizzle",
- "responsibility": "Business rules and HTTP surface.",
- "dependsOn": [
- "db"
], - "resources": {
- "cpus": 2,
- "memoryMb": 4096,
- "pidsLimit": 512
}
}
], - "version": 3,
- "createdAt": "2026-07-24T16:00:00.000Z"
}, - "adrs": [
- {
- "title": "0025 — Kubernetes deploy with Kustomize",
- "content": "## Context\n\n…"
}
], - "gitProvider": "github",
- "moduleRouting": {
- "status": "roteado",
- "roteamento": [
- {
- "modulo": "checkout-api",
- "imagemCandidata": "node:22-bookworm-slim",
- "porque": "This module is TypeScript on Node 22; the slim variant has the runtime and nothing else."
}
], - "version": 1,
- "eventId": "01JC4Z0000EVENTOROTEAMENTO01",
- "createdAt": "2026-08-30T12:00:00.000Z"
}, - "projectImage": {
- "image": "node:22-bookworm-slim",
- "rationale": "The module_map is all TypeScript over Node; the slim variant is enough and reduces surface.",
- "network": "none",
- "resources": {
- "cpus": 2,
- "memoryMb": 4096,
- "pidsLimit": 512
}
}
}与 dev PR 相同的流水线和相同的状态机。
| sessionId required | string |
| projectId required | string <uuid> |
| prActionId required | string <uuid> 打开该 PR 的 |
| gate required | string 枚举: "qa" "secops" |
| veredito required | string 枚举: "approved" "changes_requested" |
| resumo required | string |
| itens required | 数组:strings |
| maxCorrections | number |
{- "projectId": "01JC4Z0000PROJETO0000000001",
- "prActionId": "01JC4Z8QK3M7YV2N5T9B0PXHRD",
- "gate": "secops",
- "veredito": "approved",
- "resumo": "Non-root image with no secret in ARG.",
- "itens": [ ],
- "maxCorrections": 3
}{- "nextAction": "done",
- "artifact": {
- "id": "01JC4Z0000INFRAARTEFATO00001",
- "projectId": "01JC4Z0000PROJETO0000000001",
- "sessionId": "01JC4Z8QK3M7YV2N5T9B0PXHRA",
- "title": "api's production Dockerfile",
- "prActionId": "01JC4Z8QK3M7YV2N5T9B0PXHRD",
- "gateStatus": "awaiting_qa",
- "gateCorrectionCount": 0,
- "blocked": false,
- "blockedReason": null,
- "createdAt": "2026-07-26T09:00:00.000Z",
- "updatedAt": "2026-07-26T09:20:00.000Z"
}
}会变成 proposed_action,而不是直接写入:改变代理的行为是一种外部效果,要经过与其他一切相同的审批流水线。这闭合了假设 → 补丁的循环。
| sessionId required | string |
| projectId required | string <uuid> |
| agent required | string |
| proposedContent required | string 文件最终的完整内容,而不是 diff。 |
| rationale required | string |
| hypothesisId | string <uuid> 引发该补丁的假设。正是它闭合了 Anamnesis 循环。 |
{- "projectId": "01JC4Z0000PROJETO0000000001",
- "agent": "dev-api",
- "proposedContent": "# dev-api\n\nAlways run the suite before opening the PR.",
- "rationale": "The task was reopened three times for lack of a criterion.",
- "hypothesisId": "01JC4Z0000HIPOTESE000000001"
}{- "id": "01JC4Z8QK3M7YV2N5T9B0PXHRC",
- "projectId": "01JC4Z0000PROJETO0000000001",
- "sessionId": "01JC4Z8QK3M7YV2N5T9B0PXHRA",
- "seq": 7,
- "actionType": "terminal",
- "payload": {
- "command": "pnpm test"
}, - "status": "pending",
- "resolvedPolicy": "require_approval",
- "actor": {
- "kind": "agent",
- "id": "dev-api"
}, - "decidedBy": null,
- "decidedAt": null,
- "rejectionReason": null,
- "executionResult": null,
- "createdAt": "2026-07-27T14:33:10.900Z",
- "updatedAt": "2026-07-27T14:33:10.900Z"
}计量是强制的:每个回合都记录 token_usage,正是这一点让预算有意义。不记录任何事件——由 engine 叙述事件日志。提供商失败时在 error 中以 200 返回,因为 usage 中的计费仍然有效:即使失败,该回合也产生了花费。
| sessionId required | string |
| projectId required | string <uuid> |
| agentId | string 在团队面板中把成本归到此代理名下。 |
required | 数组:objects 已由 engine 的 ContextManager 组装好的历史。 |
数组:objects 提供给模型的工具; | |
| catalogoCompleto | boolean 请求完整的工具目录:此次调用跳过 Jev 工具路由器(ADR 0179)。当某一步受限的菜单导致模型没有调用工具就作答、engine 重复这一步时会设置它。 |
{- "projectId": "01JC4Z0000PROJETO0000000001",
- "agentId": "dev-api",
- "messages": [
- {
- "role": "user",
- "content": "Read file X."
}
], - "tools": [
- { }
], - "catalogoCompleto": true
}{- "message": {
- "role": "assistant",
- "content": "I'll read the file before answering.",
- "toolCalls": [
- {
- "id": "call_01",
- "name": "read_file",
- "arguments": {
- "path": "src/index.ts"
}
}
]
}, - "usage": {
- "inputTokens": 1820,
- "outputTokens": 340,
- "costMicros": 52700,
- "estimated": false
}, - "error": null,
- "errorCode": null,
- "modelName": "llama3.2:3b",
- "truncated": true,
- "toolRouting": {
- "modelo": "typesafe/jev-1.13",
- "ofertadas": 9,
- "menuAntes": [
- "string"
], - "menuDepois": [
- "string"
], - "escolha": "create_story",
- "confianca": 0.91,
- "segunda": {
- "opcao": "create_task",
- "probabilidade": 0.06
}, - "anterior": "read_file",
- "aplicado": true,
- "motivoDaQueda": "timeout",
- "origemDaQueda": "infra",
- "detalheDaQueda": { },
- "latenciaMs": 212,
- "custoMicros": 52,
- "gastoNaoRegistrado": true
}
}与 llm-turn 语义相同,逐帧交付。done 帧携带 usage——没有它,该回合将没有计费记录。
| sessionId required | string |
| projectId required | string <uuid> |
| agentId | string |
required | 数组:objects |
数组:objects | |
| catalogoCompleto | boolean 请求完整的工具目录:此次调用跳过 Jev 工具路由器(ADR 0179)。当某一步受限的菜单导致模型没有调用工具就作答、engine 重复这一步时会设置它。 |
{- "projectId": "01JC4Z0000PROJETO0000000001",
- "agentId": "criativo",
- "messages": [
- {
- "role": "user",
- "content": "Summarize what we decided."
}
], - "tools": [
- { }
], - "catalogoCompleto": true
}拒绝提议等于或低于当前值的上限:Anamnesis 周期性运行,否则每一轮都会重新提议同样的内容,用噪声填满用户需要阅读的队列。
| sessionId required | string |
| projectId required | string <uuid> |
| area required | string 上限将要提高的区域。 |
| proposto required | number >= 1 提议的上限。必须高于当前值——提议相同或更低的值会成为用户需要阅读的队列中的噪声,而 Anamnesis 是周期性运行的。 |
| rationale required | string 以观察到的决定为依据,而不是印象。 |
{- "projectId": "01JC4Z0000PROJETO0000000001",
- "area": "dev",
- "proposto": 4,
- "rationale": "You authorized the same request four times in this window, and none was denied."
}{- "id": "01JC4Z8QK3M7YV2N5T9B0PXHRC",
- "projectId": "01JC4Z0000PROJETO0000000001",
- "sessionId": "01JC4Z8QK3M7YV2N5T9B0PXHRA",
- "seq": 7,
- "actionType": "terminal",
- "payload": {
- "command": "pnpm test"
}, - "status": "pending",
- "resolvedPolicy": "require_approval",
- "actor": {
- "kind": "agent",
- "id": "dev-api"
}, - "decidedBy": null,
- "decidedAt": null,
- "rejectionReason": null,
- "executionResult": null,
- "createdAt": "2026-07-27T14:33:10.900Z",
- "updatedAt": "2026-07-27T14:33:10.900Z"
}制品就是 artifact.module_contracts 事件:不可变、带版本、有作者,与 artifact.module_map 并列。每次调用携带完整列表并替换上一版本。模块消费什么不在这里写:那是当前 module_map 的 dependsOn。
| sessionId required | string |
| projectId required | string <uuid> |
required | 数组:objects (ContratoDeModuloInternalDto) 完整的契约列表:每次调用都是一个新版本,替换上一个版本。模块消费的内容不在这里——它在读取时由当前 module_map 的 |
{- "projectId": "5a8591dd-4039-49df-9202-96385ba3eff8",
- "contratos": [
- {
- "modulo": "board-engine",
- "expoe": [
- {
- "tipo": "funcao",
- "assinatura": "placePiece(board: Board, piece: Piece, pos: Pos): Board",
- "descricao": "Returns a new board; never mutates the one passed in."
}
]
}
]
}{- "contratos": [
- {
- "modulo": "board-engine",
- "expoe": [
- {
- "tipo": "funcao",
- "assinatura": "placePiece(board: Board, piece: Piece, pos: Pos): Board",
- "descricao": "Returns a new board; never mutates the one passed in."
}
]
}
], - "version": 1
}历史不可变:每次发布都是新增一个版本,当前版本是 version 最高的那个。模块之间存在依赖环时映射会以 400 被拒绝——图必须是无环的。
| sessionId required | string |
| projectId required | string <uuid> |
required | 数组:objects 模块图。存在环会使映射以 400 被拒绝——校验在领域层。每个模块可以声明 |
{- "projectId": "01JC4Z0000PROJETO0000000001",
- "modules": [
- {
- "name": "api",
- "stack": "NestJS",
- "responsibility": "Rules and HTTP",
- "dependsOn": [
- "db"
], - "resources": {
- "cpus": 1,
- "memoryMb": 1024,
- "pidsLimit": 256
}
}
]
}{- "id": "01JC4Z0000MODULEMAP00000001",
- "projectId": "01JC4Z0000PROJETO0000000001",
- "sessionId": "01JC4Z8QK3M7YV2N5T9B0PXHRA",
- "modules": [
- {
- "name": "api",
- "stack": "NestJS + Drizzle",
- "responsibility": "Business rules and HTTP surface.",
- "dependsOn": [
- "db"
], - "resources": {
- "cpus": 2,
- "memoryMb": 4096,
- "pidsLimit": 512
}
}
], - "version": 3,
- "createdAt": "2026-07-24T16:00:00.000Z"
}制品就是 artifact.module_routing 事件:不可变、带版本、有作者,与 artifact.module_map/artifact.project_image/artifact.c4_diagram 并列。每个条目都按与 choose_project_image 相同的规则校验(显式 tag/digest,拒绝 latest,rationale 不能过于简单);模块名不在当前 module_map 中、列表为空或模块重复,都会以 400 被拒绝。
| sessionId required | string |
| projectId required | string <uuid> |
required | 数组:objects (RoteamentoDeModuloInternalDto) 当前 module_map 的每个模块一项。候选镜像是 Architect 的提议,而不是决定——Infra 在之后的步骤中从候选项中选择。 |
{- "projectId": "5a8591dd-4039-49df-9202-96385ba3eff8",
- "roteamento": [
- {
- "modulo": "checkout-api",
- "imagemCandidata": "node:22-bookworm-slim",
- "porque": "This module is TypeScript on Node 22; the slim variant has the runtime and nothing else."
}
]
}{- "roteamento": [
- {
- "modulo": "checkout-api",
- "imagemCandidata": "node:22-bookworm-slim",
- "porque": "This module is TypeScript on Node 22; the slim variant has the runtime and nothing else."
}
], - "version": 1
}在心跳超时时由 engine 的 SessionServer 查询。pending: true 会让超时被重新调度而不是关闭会话,motivo 会写入 engine 的日志——一个不说明原因就不关闭的会话是无法诊断的。
| sessionId required | string |
{- "pending": true,
- "motivo": "handoff po → arquiteto aguardando aceite",
- "aguardandoUsuarioDesde": "2026-09-18T12:00:00.000Z"
}目录之外的能力会被拒绝,引用其他项目事件的证据也会被拒绝——两种校验都存在,因为模型在这两点上都会出错。
| sessionId required | string |
| projectId required | string <uuid> |
| windowFrom required | string <date-time> |
| windowTo required | string <date-time> |
| eventCount required | number |
required | 数组:objects (ProficiencyDraftDto) |
{- "projectId": "01JC4Z0000PROJETO0000000001",
- "windowFrom": "2026-07-27T12:00:00.000Z",
- "windowTo": "2026-07-27T12:15:00.000Z",
- "eventCount": 240,
- "profiles": [
- {
- "userId": "01JC4Z0000USUARIO0000000002",
- "competency": "TypeScript",
- "level": "avancado",
- "rationale": "Fixed three generic typing errors without help.",
- "evidenceEventIds": [
- "01JC4Z8QK3M7YV2N5T9B0PXHRB"
]
}
]
}{- "runId": "01JC4Z0000RODADA00000000001",
- "profiles": [
- { }
]
}制品就是 artifact.project_image 事件:不可变、带版本、有作者。修订意味着发出新版本,绝不覆盖。在尚未有版本之前,项目的容器不会启动,Code 标签页返回 409。
| sessionId required | string |
| projectId required | string <uuid> |
| image required | string 带有显式 TAG 或 digest 的 OCI 引用。 |
| rationale required | string 为什么选这个镜像。至少 10 个字符。 |
| network | string 默认值: "none" 枚举: "none" "egress" 容器的网络姿态。 |
object 资源上限。超过最大值时,决定会被拒绝,绝不会被悄悄降级。 |
{- "projectId": "5a8591dd-4039-49df-9202-96385ba3eff8",
- "image": "node:22-bookworm-slim",
- "rationale": "The module_map is TypeScript on Node 22; the slim variant has the runtime and nothing else.",
- "network": "none",
- "resources": {
- "cpus": 2,
- "memoryMb": 4096,
- "pidsLimit": 512
}
}{- "decisao": {
- "image": "node:22-bookworm-slim",
- "rationale": "The module_map is all TypeScript over Node; the slim variant is enough and reduces surface.",
- "network": "none",
- "resources": {
- "cpus": 2,
- "memoryMb": 4096,
- "pidsLimit": 512
}
}, - "version": 1,
- "containerDePe": {
- "status": "running",
- "imageVersion": 2
}
}alreadyAnalyzed 为自动路径提供幂等性:为 true 时 worker 直接短路,不花费任何 token。之前的假设也会一并带上,以免本轮重复。
| sessionId required | string |
| projectId required | string |
{- "alreadyAnalyzed": false,
- "sessionStatus": "closed_abnormally",
- "terminationReason": "heartbeat_timeout",
- "businessRules": [
- {
- "id": "RN-014",
- "title": "Cart accepts at most 50 items"
}
], - "priorHypotheses": [
- {
- "agenteAlvo": "dev-api",
- "hipotese": "The instructions don't say when the task is done.",
- "sugestao": "Add an explicit definition-of-done criterion.",
- "confiancaPercent": 72
}
]
}用 RAG 嵌入模型对标题和项目规则的标题(最近 100 条)进行嵌入,余弦相似度 ≥ 阈值时发出警告。从不拒绝。没有嵌入提供商时检查会被跳过并说明原因,同样会在事件日志中叙述。嵌入花费作为单独的 token_usage 记录计量(参与者 system/duplicata-semantica)。
| sessionId required | string |
| projectId required | string <uuid> |
| kind required | string 值: "business_rule" |
| title required | string 刚刚追加的规则的标题。规范化后标题相同的规则(RN-080 让新的生效)不参与比较。 |
{- "projectId": "01JC4Z0000PROJETO0000000001",
- "kind": "business_rule",
- "title": "Greeting with the caller name"
}{- "status": "warned",
- "similarTo": {
- "id": "01JC4Z0000HISTORIA000000001",
- "title": "Deterministic public greeting endpoint",
- "similarity": 0.83
}, - "closest": {
- "id": "01JC4Z0000HISTORIA000000001",
- "title": "Deterministic public greeting endpoint",
- "similarity": 0.83
}, - "similarity": 0.83,
- "threshold": 0.8,
- "compared": 12,
- "total": 12,
- "reason": "provider \"ollama\" did not answer",
- "message": "string"
}businessRuleIds 为规则→story 覆盖提供数据。每个 id 必须引用一个确实存在的 artifact.business_rule 事件——校验会拒绝编造的 id。响应还携带 semanticDuplicate(RN-681):一个基于嵌入、与项目 story 对比的警告,绝不是拒绝。
| sessionId required | string |
| projectId required | string <uuid> |
| epicId required | string <uuid> |
| title required | string |
| description | string |
| rf | 数组:strings |
| rnf | 数组:strings |
| dod | 数组:strings |
| dor | 数组:strings |
| businessRuleIds | 数组:strings 每个 id 都必须引用一个确实存在的 |
{- "projectId": "01JC4Z0000PROJETO0000000001",
- "epicId": "01JC4Z0000EPICO000000000001",
- "title": "Add item to cart",
- "description": "As a buyer, I want to gather items before paying.",
- "rf": [
- "The cart accepts up to 50 items"
], - "rnf": [
- "Response under 200ms at p95"
], - "dod": [
- "Unit tests green"
], - "dor": [
- "Module defined"
], - "businessRuleIds": [
- "RN-014"
]
}{- "id": "01JC4Z0000HISTORIA000000001",
- "epicId": "01JC4Z0000EPICO000000000001",
- "projectId": "01JC4Z0000PROJETO0000000001",
- "sessionId": "01JC4Z8QK3M7YV2N5T9B0PXHRA",
- "title": "Add item to cart",
- "description": "As a buyer, I want to gather items before paying.",
- "rf": [
- "The cart accepts up to 50 items"
], - "rnf": [
- "The response stays under 200 ms at p95"
], - "businessRuleIds": [
- "RN-014"
], - "dod": [
- "Unit tests green"
], - "dor": [
- "Module defined"
], - "moduleIds": [
- "api"
], - "status": "ready",
- "proposedReady": true,
- "returnedReason": "DoD too generic — spell out the acceptance criteria.",
- "returnedAt": "2026-08-02T14:00:00.000Z",
- "archivedAt": null,
- "archivedReason": null,
- "createdAt": "2026-07-25T09:00:00.000Z",
- "updatedAt": "2026-07-25T09:40:00.000Z",
- "semanticDuplicate": {
- "status": "warned",
- "similarTo": {
- "id": "01JC4Z0000HISTORIA000000001",
- "title": "Deterministic public greeting endpoint",
- "similarity": 0.83
}, - "closest": {
- "id": "01JC4Z0000HISTORIA000000001",
- "title": "Deterministic public greeting endpoint",
- "similarity": 0.83
}, - "similarity": 0.83,
- "threshold": 0.8,
- "compared": 12,
- "total": 12,
- "reason": "provider \"ollama\" did not answer",
- "message": "string"
}
}该 story 及其任务离开 backlog、覆盖、计划和认领;记录保留,backlog.story_archived 记录此事,以 PO 为参与者(RN-727)。
| storyId required | string |
| projectId required | string <uuid> |
| reason | string |
{- "projectId": "01JC4Z0000PROJETO0000000001",
- "reason": "Duplicate of \"Register a user\"."
}{- "id": "01JC4Z0000HISTORIA000000001",
- "epicId": "01JC4Z0000EPICO000000000001",
- "projectId": "01JC4Z0000PROJETO0000000001",
- "sessionId": "01JC4Z8QK3M7YV2N5T9B0PXHRA",
- "title": "Add item to cart",
- "description": "As a buyer, I want to gather items before paying.",
- "rf": [
- "The cart accepts up to 50 items"
], - "rnf": [
- "The response stays under 200 ms at p95"
], - "businessRuleIds": [
- "RN-014"
], - "dod": [
- "Unit tests green"
], - "dor": [
- "Module defined"
], - "moduleIds": [
- "api"
], - "status": "ready",
- "proposedReady": true,
- "returnedReason": "DoD too generic — spell out the acceptance criteria.",
- "returnedAt": "2026-08-02T14:00:00.000Z",
- "archivedAt": null,
- "archivedReason": null,
- "createdAt": "2026-07-25T09:00:00.000Z",
- "updatedAt": "2026-07-25T09:40:00.000Z"
}businessRuleIds 会追加到已关联的规则中;非空列表替换当前列表,省略的列表保持不变。写入后,story 按与创建 story 相同的标准提升(ready 用于 auto 模式,proposedReady 用于 manual 模式)(RN-720)。
| sessionId required | string |
| storyId required | string |
| projectId required | string <uuid> |
| description | string |
| rf | 数组:strings |
| rnf | 数组:strings |
| dod | 数组:strings |
| dor | 数组:strings |
| businessRuleIds | 数组:strings 追加到 story 已引用的规则中(补全绝不会解除规则的关联);每个新 id 都必须引用一个已存在的 |
| questionId | string <uuid> RN-859(ADR 0214):一个用户已经回答过的未决问题——只有这样, |
{- "projectId": "01JC4Z0000PROJETO0000000001",
- "description": "As a buyer, I want to pay by card.",
- "rf": [
- "The cart accepts up to 50 items"
], - "rnf": [
- "Response under 200ms at p95"
], - "dod": [
- "Unit tests green"
], - "dor": [
- "Module defined"
], - "businessRuleIds": [
- "RN-014"
], - "questionId": "ffa0420e-f70b-4e5b-99a0-434bff860467"
}{- "id": "01JC4Z0000HISTORIA000000001",
- "epicId": "01JC4Z0000EPICO000000000001",
- "projectId": "01JC4Z0000PROJETO0000000001",
- "sessionId": "01JC4Z8QK3M7YV2N5T9B0PXHRA",
- "title": "Add item to cart",
- "description": "As a buyer, I want to gather items before paying.",
- "rf": [
- "The cart accepts up to 50 items"
], - "rnf": [
- "The response stays under 200 ms at p95"
], - "businessRuleIds": [
- "RN-014"
], - "dod": [
- "Unit tests green"
], - "dor": [
- "Module defined"
], - "moduleIds": [
- "api"
], - "status": "ready",
- "proposedReady": true,
- "returnedReason": "DoD too generic — spell out the acceptance criteria.",
- "returnedAt": "2026-08-02T14:00:00.000Z",
- "archivedAt": null,
- "archivedReason": null,
- "createdAt": "2026-07-25T09:00:00.000Z",
- "updatedAt": "2026-07-25T09:40:00.000Z"
}仅限没有执行中任务的 draft story;以 PO 为参与者写入 backlog.story_updated(RN-727)。状态为 ready 的也可以,前提是 questionId 引用了一个用户已回答的未决问题(RN-859)。
| storyId required | string |
| projectId required | string <uuid> |
| title | string |
| description | string |
| questionId | string <uuid> RN-859(ADR 0214):一个用户已经回答过的未决问题——只有这样, |
{- "projectId": "01JC4Z0000PROJETO0000000001",
- "title": "Register a user",
- "description": "As a visitor, I want to sign up.",
- "questionId": "ffa0420e-f70b-4e5b-99a0-434bff860467"
}{- "id": "01JC4Z0000HISTORIA000000001",
- "epicId": "01JC4Z0000EPICO000000000001",
- "projectId": "01JC4Z0000PROJETO0000000001",
- "sessionId": "01JC4Z8QK3M7YV2N5T9B0PXHRA",
- "title": "Add item to cart",
- "description": "As a buyer, I want to gather items before paying.",
- "rf": [
- "The cart accepts up to 50 items"
], - "rnf": [
- "The response stays under 200 ms at p95"
], - "businessRuleIds": [
- "RN-014"
], - "dod": [
- "Unit tests green"
], - "dor": [
- "Module defined"
], - "moduleIds": [
- "api"
], - "status": "ready",
- "proposedReady": true,
- "returnedReason": "DoD too generic — spell out the acceptance criteria.",
- "returnedAt": "2026-08-02T14:00:00.000Z",
- "archivedAt": null,
- "archivedReason": null,
- "createdAt": "2026-07-25T09:00:00.000Z",
- "updatedAt": "2026-07-25T09:40:00.000Z"
}这是交叉校验:当前映射中不存在的模块会变成一项架构待办,而不是悄无声息地溜过去。
| sessionId required | string |
| projectId required | string <uuid> |
| storyId required | string <uuid> |
| moduleIds required | 数组:strings 来自当前 |
{- "projectId": "01JC4Z0000PROJETO0000000001",
- "storyId": "01JC4Z0000HISTORIA000000001",
- "moduleIds": [
- "api",
- "web"
]
}{- "id": "01JC4Z0000HISTORIA000000001",
- "epicId": "01JC4Z0000EPICO000000000001",
- "projectId": "01JC4Z0000PROJETO0000000001",
- "sessionId": "01JC4Z8QK3M7YV2N5T9B0PXHRA",
- "title": "Add item to cart",
- "description": "As a buyer, I want to gather items before paying.",
- "rf": [
- "The cart accepts up to 50 items"
], - "rnf": [
- "The response stays under 200 ms at p95"
], - "businessRuleIds": [
- "RN-014"
], - "dod": [
- "Unit tests green"
], - "dor": [
- "Module defined"
], - "moduleIds": [
- "api"
], - "status": "ready",
- "proposedReady": true,
- "returnedReason": "DoD too generic — spell out the acceptance criteria.",
- "returnedAt": "2026-08-02T14:00:00.000Z",
- "archivedAt": null,
- "archivedReason": null,
- "createdAt": "2026-07-25T09:00:00.000Z",
- "updatedAt": "2026-07-25T09:40:00.000Z"
}| sessionId required | string |
| projectId required | string <uuid> |
| storyId required | string <uuid> |
| title required | string |
| description | string |
{- "projectId": "01JC4Z0000PROJETO0000000001",
- "storyId": "01JC4Z0000HISTORIA000000001",
- "title": "Endpoint POST /cart/items",
- "description": "Accepts SKU and quantity; validates stock."
}{- "id": "01JC4Z0000TAREFA00000000001",
- "storyId": "01JC4Z0000HISTORIA000000001",
- "title": "POST /cart/items endpoint",
- "description": "Accepts SKU and quantity; validates stock.",
- "status": "in_progress",
- "assignedTo": "dev-api",
- "blocked": false,
- "blockedReason": null,
- "blockedOrigin": null,
- "gateStatus": null,
- "gateCorrectionCount": 0,
- "module": "api",
- "createdAt": "2026-07-25T10:00:00.000Z",
- "updatedAt": "2026-07-25T12:30:00.000Z"
}没有自动解除阻塞:解除阻塞的是人,通过人工路由。正是这一点阻止了代理无限空转。
| sessionId required | string |
| taskId required | string |
| projectId required | string <uuid> |
| agentId required | string |
| reason required | string |
| diagnosis required | string 自由文本诊断——对所发生情况的人类可读细节,而不是失败的结构化来源。 |
| origin | string 枚举: "infra" "modelo" "codigo" "politica" 失败的来源: |
{- "projectId": "01JC4Z0000PROJETO0000000001",
- "agentId": "dev-api",
- "reason": "Gate correction cap reached.",
- "diagnosis": "Correction cycle exhausted without exit 0.",
- "origin": "infra"
}{- "id": "01JC4Z0000TAREFA00000000001",
- "storyId": "01JC4Z0000HISTORIA000000001",
- "title": "POST /cart/items endpoint",
- "description": "Accepts SKU and quantity; validates stock.",
- "status": "in_progress",
- "assignedTo": "dev-api",
- "blocked": false,
- "blockedReason": null,
- "blockedOrigin": null,
- "gateStatus": null,
- "gateCorrectionCount": 0,
- "module": "api",
- "createdAt": "2026-07-25T10:00:00.000Z",
- "updatedAt": "2026-07-25T12:30:00.000Z"
}在 dev agent 的 PR 准备好评审时调用。
| sessionId required | string |
| taskId required | string |
| projectId required | string <uuid> |
| agentId required | string |
{- "projectId": "01JC4Z0000PROJETO0000000001",
- "agentId": "dev-api"
}{- "gateStatus": "awaiting_qa"
}| sessionId required | string |
| taskId required | string |
| projectId required | string <uuid> |
| agentId required | string 只有认领了任务的代理才能移动它。 |
| status required | string 枚举: "todo" "in_progress" "in_review" "done" |
{- "projectId": "01JC4Z0000PROJETO0000000001",
- "agentId": "dev-api",
- "status": "in_review"
}{- "id": "01JC4Z0000TAREFA00000000001",
- "storyId": "01JC4Z0000HISTORIA000000001",
- "title": "POST /cart/items endpoint",
- "description": "Accepts SKU and quantity; validates stock.",
- "status": "in_progress",
- "assignedTo": "dev-api",
- "blocked": false,
- "blockedReason": null,
- "blockedOrigin": null,
- "gateStatus": null,
- "gateCorrectionCount": 0,
- "module": "api",
- "createdAt": "2026-07-25T10:00:00.000Z",
- "updatedAt": "2026-07-25T12:30:00.000Z"
}由 engine 的重试任务调用(最多 3 次,分别在 1、3 和 10 分钟后)。与人工解除阻塞相同的用例,参与者为 system;已不再阻塞的任务保持不变。
| sessionId required | string |
| taskId required | string |
| projectId required | string <uuid> |
| agentId required | string 因短暂失败而安排了重试的 dev agent。 |
{- "projectId": "01JC4Z0000PROJETO0000000001",
- "agentId": "dev-api"
}{- "id": "01JC4Z0000TAREFA00000000001",
- "storyId": "01JC4Z0000HISTORIA000000001",
- "title": "POST /cart/items endpoint",
- "description": "Accepts SKU and quantity; validates stock.",
- "status": "in_progress",
- "assignedTo": "dev-api",
- "blocked": false,
- "blockedReason": null,
- "blockedOrigin": null,
- "gateStatus": null,
- "gateCorrectionCount": 0,
- "module": "api",
- "createdAt": "2026-07-25T10:00:00.000Z",
- "updatedAt": "2026-07-25T12:30:00.000Z"
}按设计是原子的:同一模块上有多个 dev agent 时,两次并发认领不可能返回同一个任务。没有可用任务时返回空,而不是错误。
| sessionId required | string |
| projectId required | string <uuid> |
| module required | string 只有此模块的任务参与抽取。 |
| agentId required | string |
{- "projectId": "01JC4Z0000PROJETO0000000001",
- "module": "api",
- "agentId": "dev-api"
}{- "id": "01JC4Z0000TAREFA00000000001",
- "storyId": "01JC4Z0000HISTORIA000000001",
- "title": "POST /cart/items endpoint",
- "description": "Accepts SKU and quantity; validates stock.",
- "status": "in_progress",
- "assignedTo": "dev-api",
- "blocked": false,
- "blockedReason": null,
- "blockedOrigin": null,
- "gateStatus": null,
- "gateCorrectionCount": 0,
- "module": "api",
- "createdAt": "2026-07-25T10:00:00.000Z",
- "updatedAt": "2026-07-25T12:30:00.000Z"
}未阻塞且不在 PR gate 中;除非会话是执行会话,否则为空。engine 在停止执行时使用它,以阻塞那些没有存活 dev agent 持有的任务。
| sessionId required | string |
[- {
- "projectId": "5a8591dd-4039-49df-9202-96385ba3eff8",
- "taskId": "e6e9d88a-9b63-468a-aec3-b7a11de27af8",
- "assignedTo": "dev-api"
}
]只有不是由 api 引起的终止才会到达这里——崩溃、kill、heartbeat_timeout、防御性关闭。由 api 自己计划的停止不经过这里:engine 已经通过 outbox 得知,再报告回来只是回声。
| sessionId required | string |
| projectId required | string <uuid> |
| to required | string 枚举: "closing" "closed" "closed_abnormally"
|
| reason | string 终止的来源,而不是通过排除法得出的诊断。 |
{- "projectId": "01JC4Z0000PROJETO0000000001",
- "to": "closed_abnormally",
- "reason": "heartbeat_timeout"
}{- "id": "01JC4Z8QK3M7YV2N5T9B0PXHRA",
- "projectId": "01JC4Z0000PROJETO0000000001",
- "createdBy": "01JC4Z0000USUARIO0000000001",
- "status": "active",
- "kind": "criativa",
- "name": "Cart checkout",
- "nextSeq": 42,
- "createdAt": "2026-07-27T14:03:22.187Z",
- "updatedAt": "2026-07-27T14:31:09.004Z",
- "closedAt": null,
- "terminationReason": null,
- "traceParent": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
}九个提供商能力(ADR 0041)的提供商层,读取自处理调用的同一批实例。只有经真实 API 验证的能力才为 true。界面在为 binding 提供路由偏好之前从这里读取 routingPreference(ADR 0166)。
[- {
- "provider": "openrouter",
- "capabilities": {
- "streaming": true,
- "toolCalling": true,
- "listModels": true,
- "embeddings": false,
- "routingPreference": false,
- "stickySession": false,
- "priceCeiling": false,
- "promptCaching": false
}
}
]代理 → 区域 → 项目 → workspace 级联,不含会话:这是代理在项目上的配置,而不是某次特定对话的配置。origin: "agent" 表示该代理偏离了其区域的默认值;任何其他来源都表示它在继承。
| projectId required | string |
| agentSlug required | string 示例: dev-api |
{- "modelId": "01JC4Z0000MODELO00000000001",
- "origin": "project",
- "routingPreference": null,
- "skipped": [
- {
- "scope": "agent",
- "modelId": "01JC4Z0000MODELO00000000001",
- "reason": "unavailable"
}
], - "herdadoDoLead": "secops"
}适用于本项目中所有没有自身 binding 的会话,且仅适用于这些会话——在 ADR 0064 之前 binding 是全局的,也会影响其他项目。正是这样才能在同一项目中给 Psychologist 一个便宜的模型、给 Architect 一个昂贵的模型,也正是这样代理才能偏离其区域的默认值。
| projectId required | string |
| agentSlug required | string 示例: dev-api |
| modelId required | string <uuid> 要固定到此范围的模型。必须存在于 |
| routingPreference | string or null 枚举: "price" "throughput" "latency" 中转平台如何选择服务该模型的上游(ADR 0166,RN-583)。缺省时,若新模型的提供商接受则保留已存储的值,否则清除; |
{- "modelId": "9b1c2d3e-4f50-4a61-8b72-0c3d4e5f6a7b",
- "routingPreference": "throughput"
}{- "id": "01JC4Z0000BINDING00000000001",
- "scope": "project",
- "scopeId": "01JC4Z0000PROJETO0000000001",
- "modelId": "01JC4Z0000MODELO00000000001",
- "routingPreference": null,
- "createdBy": "01JC4Z0000USUARIO0000000001",
- "createdAt": "2026-07-11T09:00:00.000Z",
- "updatedAt": "2026-07-11T09:00:00.000Z"
}一个滑动的 30 天窗口,而不是当前自然月:这是界面显示的标签,若用自然月,估算会在每月 1 日因翻页而骤降,而不是因为使用量变化。只计入 actor_kind = agent(RN-038)。从未运行过的代理不会出现在列表中——界面为其显示一个短横线,这与零不同。
| projectId required | string |
[- {
- "actorId": "dev-api",
- "costMicros": 3400000,
- "inputTokens": 128000,
- "outputTokens": 14200
}
]lead 与区域子代理共享的默认值。origin: "area" 表示有人为这个区域做了选择;任何其他来源都表示区域本身从项目或 workspace 继承。
| projectId required | string |
| areaKey required | string 示例: qa |
{- "modelId": "01JC4Z0000MODELO00000000001",
- "origin": "project",
- "routingPreference": null,
- "skipped": [
- {
- "scope": "agent",
- "modelId": "01JC4Z0000MODELO00000000001",
- "reason": "unavailable"
}
], - "herdadoDoLead": "secops"
}适用于 lead 以及该区域中所有没有自身 binding 的子代理(RN-102)。需要 maintainer,因为它一次影响整个区域,而选择模型就是决定花费——与并行度上限的理由相同。
| projectId required | string |
| areaKey required | string 示例: qa |
| modelId required | string <uuid> 要固定到此范围的模型。必须存在于 |
| routingPreference | string or null 枚举: "price" "throughput" "latency" 中转平台如何选择服务该模型的上游(ADR 0166,RN-583)。缺省时,若新模型的提供商接受则保留已存储的值,否则清除; |
{- "modelId": "9b1c2d3e-4f50-4a61-8b72-0c3d4e5f6a7b",
- "routingPreference": "throughput"
}{- "id": "01JC4Z0000BINDING00000000001",
- "scope": "project",
- "scopeId": "01JC4Z0000PROJETO0000000001",
- "modelId": "01JC4Z0000MODELO00000000001",
- "routingPreference": null,
- "createdBy": "01JC4Z0000USUARIO0000000001",
- "createdAt": "2026-07-11T09:00:00.000Z",
- "updatedAt": "2026-07-11T09:00:00.000Z"
}数值以微美元计。需要 maintainer——这是成本信息。
| projectId required | string |
{- "id": "01JC4Z0000ORCAMENTO000000001",
- "projectId": "01JC4Z0000PROJETO0000000001",
- "sessionId": null,
- "limitMicros": 50000000,
- "spentMicros": 12400000,
- "policy": "block",
- "lastThresholdNotified": 70,
- "createdAt": "2026-07-12T08:00:00.000Z",
- "updatedAt": "2026-07-27T15:00:00.000Z"
}限额以美元传入并转换为微美元。在 policy=block 下,会超出上限的调用会被拒绝,而不仅仅是被记录。
| projectId required | string |
| limitUsd required | number >= 0 上限,以美元计——契约中唯一以美元表述的地方,因为那是人会输入的单位。api 将其转换为微美元,响应以微美元返回。 |
| policy required | string 枚举: "block" "allow"
|
{- "limitUsd": 50,
- "policy": "block"
}{- "id": "01JC4Z0000ORCAMENTO000000001",
- "projectId": "01JC4Z0000PROJETO0000000001",
- "sessionId": null,
- "limitMicros": 50000000,
- "spentMicros": 12400000,
- "policy": "block",
- "lastThresholdNotified": 70,
- "createdAt": "2026-07-12T08:00:00.000Z",
- "updatedAt": "2026-07-27T15:00:00.000Z"
}此范围的原始 binding——如果项目只从 workspace 继承则为 null。
| projectId required | string |
{- "id": "01JC4Z0000BINDING00000000001",
- "scope": "project",
- "scopeId": "01JC4Z0000PROJETO0000000001",
- "modelId": "01JC4Z0000MODELO00000000001",
- "routingPreference": null,
- "createdBy": "01JC4Z0000USUARIO0000000001",
- "createdAt": "2026-07-11T09:00:00.000Z",
- "updatedAt": "2026-07-11T09:00:00.000Z"
}| projectId required | string |
| modelId required | string <uuid> 要固定到此范围的模型。必须存在于 |
| routingPreference | string or null 枚举: "price" "throughput" "latency" 中转平台如何选择服务该模型的上游(ADR 0166,RN-583)。缺省时,若新模型的提供商接受则保留已存储的值,否则清除; |
{- "modelId": "9b1c2d3e-4f50-4a61-8b72-0c3d4e5f6a7b",
- "routingPreference": "throughput"
}{- "id": "01JC4Z0000BINDING00000000001",
- "scope": "project",
- "scopeId": "01JC4Z0000PROJETO0000000001",
- "modelId": "01JC4Z0000MODELO00000000001",
- "routingPreference": null,
- "createdBy": "01JC4Z0000USUARIO0000000001",
- "createdAt": "2026-07-11T09:00:00.000Z",
- "updatedAt": "2026-07-11T09:00:00.000Z"
}GET .../agent-bindings/:agentSlug 和 GET .../area-bindings/:areaKey 的批量形式:每个请求的键得到的值与其单键路由的回答完全相同(相同的级联、相同的来源,没有任何层级有模型时为 null)。它的存在让 Settings 标签页用一次请求而不是 20 次读取 20 个已解析的 binding——速率限制是按用户计的。键以逗号分隔并去重,格式错误的键或总数超过 64 个键返回 400,绝不悄悄丢弃。
| projectId required | string |
| agents | string 示例: agents=criativo,po,dev-lead 以逗号分隔的代理 slug。 |
| areas | string 示例: areas=dev,qa,infra 以逗号分隔的区域键。 |
{- "agents": [
- {
- "key": "dev-lead",
- "binding": {
- "modelId": "01JC4Z0000MODELO00000000001",
- "origin": "project",
- "routingPreference": null,
- "skipped": [
- {
- "scope": "agent",
- "modelId": "01JC4Z0000MODELO00000000001",
- "reason": "unavailable"
}
], - "herdadoDoLead": "secops"
}
}
], - "areas": [
- {
- "key": "dev-lead",
- "binding": {
- "modelId": "01JC4Z0000MODELO00000000001",
- "origin": "project",
- "routingPreference": null,
- "skipped": [
- {
- "scope": "agent",
- "modelId": "01JC4Z0000MODELO00000000001",
- "reason": "unavailable"
}
], - "herdadoDoLead": "secops"
}
}
]
}local 类别是 Ollama 的模型,不花钱;cloud 是付费 API 的模型。workspace 未激活的模型不会出现在这里,但仍保留在历史成本中——要查看它请使用目录路由。该列表属于项目的所有者 workspace:同一模型可以在一个 workspace 中开启而在相邻的 workspace 中关闭(ADR 0049)。
| projectId required | string |
{- "local": {
- "ollama": [ ]
}, - "cloud": {
- "anthropic": [ ],
- "openai": [ ]
}
}{- "id": "01JC4Z0000ORCAMENTO000000001",
- "projectId": "01JC4Z0000PROJETO0000000001",
- "sessionId": null,
- "limitMicros": 50000000,
- "spentMicros": 12400000,
- "policy": "block",
- "lastThresholdNotified": 70,
- "createdAt": "2026-07-12T08:00:00.000Z",
- "updatedAt": "2026-07-27T15:00:00.000Z"
}与项目预算相同的美元到微美元转换。
| sessionId required | string |
| limitUsd required | number >= 0 上限,以美元计——契约中唯一以美元表述的地方,因为那是人会输入的单位。api 将其转换为微美元,响应以微美元返回。 |
| policy required | string 枚举: "block" "allow"
|
{- "limitUsd": 50,
- "policy": "block"
}{- "id": "01JC4Z0000ORCAMENTO000000001",
- "projectId": "01JC4Z0000PROJETO0000000001",
- "sessionId": null,
- "limitMicros": 50000000,
- "spentMicros": 12400000,
- "policy": "block",
- "lastThresholdNotified": 70,
- "createdAt": "2026-07-12T08:00:00.000Z",
- "updatedAt": "2026-07-27T15:00:00.000Z"
}Server-Sent Events。delta 帧携带增量文本,done 以 token 和成本计费收尾。metering_failed 帧表示响应已发出但成本没有计入——失败会显现出来而不是消失。如果在 policy=block 下超出预算,流会携带 error 且没有 delta。此路由不携带代理:它把文本单独发送给绑定的模型,没有历史也没有系统提示词。在从未激活过任何代理的 consultiva 会话中,它会在产生任何效果之前被拒绝(RN-682)——请改用 .../agents/:agent/message 与代理对话。
| projectId required | string |
| sessionId required | string |
| text required | string 用户的消息。响应以 SSE 流返回。 |
{- "text": "Summarize what we have already decided about checkout."
}返回由级联解析得出的 binding,而不是会话的原始 binding:没有自身 binding 的会话使用项目的 binding,在这里回答 null 就是对正确问题的错误回答。origin 字段说明该值来自哪个范围。
agentId 是可选的,表示此刻在会话中真正活跃的代理(与 RunLlmTurnUseCase 运行回合时使用的相同)——没有它,级联只能看到 会话→项目→workspace(加上固定回落到 Creative),在 handoff 之后永远无法反映 PO/Architect/Dev Lead/区域。
| projectId required | string |
| sessionId required | string |
| agentId | string 此会话中当前活跃的代理(例如 “po”、“arquiteto”、“dev-lead”)。 |
{- "modelId": "01JC4Z0000MODELO00000000001",
- "origin": "project",
- "routingPreference": null,
- "skipped": [
- {
- "scope": "agent",
- "modelId": "01JC4Z0000MODELO00000000001",
- "reason": "unavailable"
}
], - "herdadoDoLead": "secops"
}在会话存续期间优先于级联中的其他一切。
| projectId required | string |
| sessionId required | string |
| modelId required | string <uuid> 要固定到此范围的模型。必须存在于 |
| routingPreference | string or null 枚举: "price" "throughput" "latency" 中转平台如何选择服务该模型的上游(ADR 0166,RN-583)。缺省时,若新模型的提供商接受则保留已存储的值,否则清除; |
{- "modelId": "9b1c2d3e-4f50-4a61-8b72-0c3d4e5f6a7b",
- "routingPreference": "throughput"
}{- "id": "01JC4Z0000BINDING00000000001",
- "scope": "project",
- "scopeId": "01JC4Z0000PROJETO0000000001",
- "modelId": "01JC4Z0000MODELO00000000001",
- "routingPreference": null,
- "createdBy": "01JC4Z0000USUARIO0000000001",
- "createdAt": "2026-07-11T09:00:00.000Z",
- "updatedAt": "2026-07-11T09:00:00.000Z"
}数据一直在 token_usage 中,但没有聚合也没有路由,团队面板就无法显示每张卡片的成本。所需角色与会话预算相同。
| sessionId required | string |
[- {
- "actorId": "dev-api",
- "costMicros": 3400000,
- "inputTokens": 128000,
- "outputTokens": 14200,
- "lastCall": {
- "inputTokens": 42000,
- "modelName": "qwen2.5-coder:7b",
- "contextWindow": 128000
}
}
]面向成员的视图。只有调用方自己的记录——参与者来自已认证的令牌,没有可替换它的参数。以 token 和估算成本呈现,不按提供商或凭据细分:运行的密钥属于所有者(RN-058),其账单也属于所有者(RN-060)。提供商维度在所有者的报告中才存在(ADR 0076),从这里仍不可达——此路由没有维度参数,仓储类型也拒绝这种组合(RN-187)。代理不计入:token_usage 记录的是谁花费了,而不是谁下令花费。
| projectId required | string |
| dias | string 滑动窗口。默认 30,最大 180。 |
{- "projectId": "9b1c2d3e-4f50-4a61-8b72-0c3d4e5f6a7b",
- "dias": 30,
- "actorId": "9b1c2d3e-4f50-4a61-8b72-0c3d4e5f6a7b",
- "totalMicros": 1250000,
- "assinaturaMicros": 0,
- "inputTokens": 120000,
- "outputTokens": 35000,
- "chamadas": 42,
- "porSessao": [
- {
- "chave": "anthropic/claude-sonnet-4",
- "rotulo": "Loja",
- "actorKind": "agent",
- "costMicros": 1250000,
- "assinaturaMicros": 0,
- "inputTokens": 120000,
- "outputTokens": 35000,
- "chamadas": 42
}
], - "porDia": [
- {
- "dia": "2026-08-09",
- "costMicros": 1250000,
- "chamadas": 42
}
]
}The MEMBER's view of the PROJECT (RN-953, ADR 0233, revising 0063): total and cost per agent/area, NEVER provider or credential — the route has no dimension parameter and never asks for provider.
| projectId required | string |
| dias | string 滑动窗口。默认 30,最大 180。 |
{- "projectId": "5a8591dd-4039-49df-9202-96385ba3eff8",
- "dias": 30,
- "totalMicros": 1250000,
- "assinaturaMicros": 0,
- "inputTokens": 120000,
- "outputTokens": 35000,
- "chamadas": 42,
- "porAgente": [
- {
- "chave": "anthropic/claude-sonnet-4",
- "rotulo": "Loja",
- "actorKind": "agent",
- "costMicros": 1250000,
- "assinaturaMicros": 0,
- "inputTokens": 120000,
- "outputTokens": 35000,
- "chamadas": 42
}
], - "porArea": [
- {
- "chave": "anthropic/claude-sonnet-4",
- "rotulo": "Loja",
- "actorKind": "agent",
- "costMicros": 1250000,
- "assinaturaMicros": 0,
- "inputTokens": 120000,
- "outputTokens": 35000,
- "chamadas": 42
}
]
}之所以存在,是因为代理花费的密钥属于 workspace 所有者(RN-058)——付账的人需要看到账单。按提供商分组,因为那是凭据的单位,并把代理花出去的与人在聊天中花出去的分开:两者出自同一把密钥,回答的是不同的问题。不返回任何机密。
| workspaceId required | string |
| meses required | string |
{- "workspaceId": "9b1c2d3e-4f50-4a61-8b72-0c3d4e5f6a7b",
- "ownerId": "9b1c2d3e-4f50-4a61-8b72-0c3d4e5f6a7b",
- "meses": 6,
- "totalMicros": 1250000,
- "porProvider": [
- {
- "provider": "openrouter",
- "temCredencial": true,
- "costMicros": 1250000,
- "inputTokens": 120000,
- "outputTokens": 35000,
- "chamadas": 42,
- "costMicrosAgentes": 900000,
- "costMicrosPessoas": 350000,
- "porMes": [
- {
- "mes": "2026-08-01T00:00:00.000Z",
- "costMicros": 1250000,
- "chamadas": 42
}
]
}
]
}默认只筛选官方发布者(允许列表位于 domain/llm/huggingface-official-publishers.ts)——与 LLM 目录(ADR 0042)“始终人工策展”的精神相同,只是应用于“官方”徽章而不是激活。includeCommunity=true 会纳入所有发布者,每个都标记为 official: false,供界面显示安全警告——绝不隐藏。
| q required | string 搜索文本。 |
| includeCommunity | string 传入 |
[- {
- "repoId": "meta-llama/Llama-3.1-8B-Instruct-GGUF",
- "publisher": "meta-llama",
- "downloads": 182034,
- "likes": 210,
- "official": true
}
]两个显式步骤中的第一步:此调用只记录意图,状态为 pending_confirmation。在另行调用 POST .../pull-requests/:id/confirm 之前不会拉取任何东西——产品绝不会运行自动的、静默的拉取。
| workspaceId required | string |
| repoId required | string Hugging Face Hub 上的 |
| estimatedSizeBytes | number 当 Hub 公布了大小时来自搜索结果——绝不在客户端凭猜测估算。 |
{- "repoId": "meta-llama/Llama-3.1-8B-Instruct-GGUF",
- "estimatedSizeBytes": 4900000000
}{- "id": "9b1c2d3e-4f50-4a61-8b72-0c3d4e5f6a7b",
- "workspaceId": "9b1c2d3e-4f50-4a61-8b72-0c3d4e5f6a7b",
- "requestedBy": "9b1c2d3e-4f50-4a61-8b72-0c3d4e5f6a7b",
- "repoId": "meta-llama/Llama-3.1-8B-Instruct-GGUF",
- "estimatedSizeBytes": null,
- "status": "pending_confirmation",
- "confirmedAt": null,
- "failedReason": null,
- "createdAt": "2026-08-26T12:00:00.000Z",
- "updatedAt": "2026-08-26T12:00:00.000Z"
}{- "id": "9b1c2d3e-4f50-4a61-8b72-0c3d4e5f6a7b",
- "workspaceId": "9b1c2d3e-4f50-4a61-8b72-0c3d4e5f6a7b",
- "requestedBy": "9b1c2d3e-4f50-4a61-8b72-0c3d4e5f6a7b",
- "repoId": "meta-llama/Llama-3.1-8B-Instruct-GGUF",
- "estimatedSizeBytes": null,
- "status": "pending_confirmation",
- "confirmedAt": null,
- "failedReason": null,
- "createdAt": "2026-08-26T12:00:00.000Z",
- "updatedAt": "2026-08-26T12:00:00.000Z"
}将 pending_confirmation → confirmed → pulling,调用 Ollama 守护进程,成功后将模型 upsert 到目录(models)并为此 workspace 激活它。失败时请求以 failed 结束,附带声明其来源的原因(infra | model | code | policy)——绝不是静默失败。此调用在整个拉取过程中同步运行(原因见用例);请使用下面的 GET 路由轮询,而不要一直保持此连接。
| workspaceId required | string |
| id required | string |
{- "id": "9b1c2d3e-4f50-4a61-8b72-0c3d4e5f6a7b",
- "workspaceId": "9b1c2d3e-4f50-4a61-8b72-0c3d4e5f6a7b",
- "requestedBy": "9b1c2d3e-4f50-4a61-8b72-0c3d4e5f6a7b",
- "repoId": "meta-llama/Llama-3.1-8B-Instruct-GGUF",
- "estimatedSizeBytes": null,
- "status": "pending_confirmation",
- "confirmedAt": null,
- "failedReason": null,
- "createdAt": "2026-08-26T12:00:00.000Z",
- "updatedAt": "2026-08-26T12:00:00.000Z"
}The owner of record (workspaces.created_by) is whose key the agents spend (RN-058). Only provider NAMES — never a secret, id or date. ollama (local, no key) never appears.
| workspaceId required | string |
{- "providers": [
- "anthropic",
- "openrouter"
]
}此范围的原始 binding。它是级联的根。
| workspaceId required | string |
{- "id": "01JC4Z0000BINDING00000000001",
- "scope": "project",
- "scopeId": "01JC4Z0000PROJETO0000000001",
- "modelId": "01JC4Z0000MODELO00000000001",
- "routingPreference": null,
- "createdBy": "01JC4Z0000USUARIO0000000001",
- "createdAt": "2026-07-11T09:00:00.000Z",
- "updatedAt": "2026-07-11T09:00:00.000Z"
}适用于所有没有自身 binding 的项目。
| workspaceId required | string |
| modelId required | string <uuid> 要固定到此范围的模型。必须存在于 |
| routingPreference | string or null 枚举: "price" "throughput" "latency" 中转平台如何选择服务该模型的上游(ADR 0166,RN-583)。缺省时,若新模型的提供商接受则保留已存储的值,否则清除; |
{- "modelId": "9b1c2d3e-4f50-4a61-8b72-0c3d4e5f6a7b",
- "routingPreference": "throughput"
}{- "id": "01JC4Z0000BINDING00000000001",
- "scope": "project",
- "scopeId": "01JC4Z0000PROJETO0000000001",
- "modelId": "01JC4Z0000MODELO00000000001",
- "routingPreference": null,
- "createdBy": "01JC4Z0000USUARIO0000000001",
- "createdAt": "2026-07-11T09:00:00.000Z",
- "updatedAt": "2026-07-11T09:00:00.000Z"
}Same curation level as the pricing route. subscription makes the spend report show the cost apart from the US$ paid (RN-954). Never re-prices the past.
| modelId required | string |
| billing required | string 枚举: "api" "subscription" "free" |
{- "billing": "subscription"
}{- "id": "01JC4Z0000MODELO00000000001",
- "provider": "anthropic",
- "name": "claude-opus-4-8",
- "displayName": "Claude Opus 4.8",
- "inputPricePerMillionMicros": 15000000,
- "outputPricePerMillionMicros": 75000000,
- "contextWindow": 200000,
- "supportsToolCalling": true,
- "supportsStreaming": true,
- "supportsVision": false,
- "supportsReasoning": true,
- "generatesImage": false,
- "reasoningBudget": 2048,
- "manualPricing": true,
- "availability": "available",
- "billing": "api",
- "lastSeenAt": "2026-07-30T03:00:00.000Z",
- "createdAt": "2026-07-01T00:00:00.000Z",
- "updatedAt": "2026-07-01T00:00:00.000Z"
}最新的在前。这是不可变的日志:绝不执行 UPDATE,每条记录都携带前/后值对,因此审计不依赖于从上一条记录重建“之前”的值。
| modelId required | string |
[- {
- "id": "01JC4Z0000PRECO000000000001",
- "modelId": "01JC4Z0000MODELO00000000001",
- "inputBeforeMicros": 2500000,
- "inputAfterMicros": 3000000,
- "outputBeforeMicros": 10000000,
- "outputAfterMicros": 12000000,
- "source": "manual",
- "changedBy": "01JC4Z0000USUARIO0000000001",
- "createdAt": "2026-08-01T12:00:00.000Z"
}
]从现在起生效,绝不重新为过去定价:每条 token_usage 记录都保留产生其成本的价格。变更会连同前/后值对一起记录——可通过历史路由查询。
| modelId required | string |
| inputPricePerMillionMicros required | number 每百万输入 token 的价格,以微美元计。 |
| outputPricePerMillionMicros required | number 每百万输出 token 的价格,以微美元计。 |
{- "inputPricePerMillionMicros": 2500000,
- "outputPricePerMillionMicros": 10000000
}{- "id": "01JC4Z0000MODELO00000000001",
- "provider": "anthropic",
- "name": "claude-opus-4-8",
- "displayName": "Claude Opus 4.8",
- "inputPricePerMillionMicros": 15000000,
- "outputPricePerMillionMicros": 75000000,
- "contextWindow": 200000,
- "supportsToolCalling": true,
- "supportsStreaming": true,
- "supportsVision": false,
- "supportsReasoning": true,
- "generatesImage": false,
- "reasoningBudget": 2048,
- "manualPricing": true,
- "availability": "available",
- "billing": "api",
- "lastSeenAt": "2026-07-30T03:00:00.000Z",
- "createdAt": "2026-07-01T00:00:00.000Z",
- "updatedAt": "2026-07-01T00:00:00.000Z"
}所有者的策展,仅适用于此 workspace(ADR 0049)。只改动 isActive——availability 是同步在提供商处观测到的,是全局的,在这里不会改变。整批全有或全无:不存在的 id 会让调用失败,不应用任何一行。
| workspaceId required | string |
| modelIds required | 数组:strings <uuid> [ items <uuid > ] 要开启或关闭的模型。整批全有或全无。 |
| isActive required | boolean
|
{- "modelIds": [
- "9b1c2d3e-4f50-4a61-8b72-0c3d4e5f6a7b"
], - "isActive": true
}[- {
- "id": "01JC4Z0000MODELO00000000001",
- "provider": "anthropic",
- "name": "claude-opus-4-8",
- "displayName": "Claude Opus 4.8",
- "inputPricePerMillionMicros": 15000000,
- "outputPricePerMillionMicros": 75000000,
- "contextWindow": 200000,
- "supportsToolCalling": true,
- "supportsStreaming": true,
- "supportsVision": false,
- "supportsReasoning": true,
- "generatesImage": false,
- "reasoningBudget": 2048,
- "manualPricing": true,
- "availability": "available",
- "billing": "api",
- "lastSeenAt": "2026-07-30T03:00:00.000Z",
- "createdAt": "2026-07-01T00:00:00.000Z",
- "updatedAt": "2026-07-01T00:00:00.000Z",
- "isActive": true,
- "uses": [
- "codigo"
], - "freeRoutingAlias": false
}
]策展界面。同步发现的模型以 isActive: false 进入,在有人激活它之前只出现在这里;从提供商消失的模型会带有 availability: "unavailable",且绝不删除,因为 binding 和成本历史都指向它。isActive 是针对此 workspace 的(ADR 0049);availability 是全局的,因为它是同步在提供商处观测到的。
| workspaceId required | string |
{- "local": {
- "ollama": [ ]
}, - "cloud": {
- "anthropic": [ ],
- "openai": [ ]
}
}与 engine 调度的内部路由相同的用例——策展界面的刷新按钮没有自己的路径,因此绝不会出现两个可能产生分歧的对账。
{- "porProvider": [
- {
- "provider": "openai",
- "descobertos": 3,
- "reencontrados": 1,
- "indisponibilizados": 0,
- "pulado": "sem_capability",
- "origemDaFalha": "infra",
- "detalhe": "openai responded with status 401"
}
]
}没有任何目录会公布的维度:“适合写代码”不是任何提供商声明的能力,而是运营者的意见——且仅适用于此 workspace(ADR 0049)。与激活相互独立的维度:标记某种用途不会在选择器中开启模型,更改用途也不会关闭已开启的模型。用途列表会替换之前的列表。
| workspaceId required | string |
| modelIds required | 数组:strings <uuid> [ items <uuid > ] 要标记的模型。整批全有或全无。 |
| uses required | 数组:strings 项 枚举: "codigo" "documentacao" "analise" "imagem" "conversa" 完整的用途列表——替换之前的列表,而不是追加。空列表会取消全部标记。封闭词汇:词汇之外的用途会让调用失败,因为自由文本会在同一个过滤器中出现 |
{- "modelIds": [
- "9b1c2d3e-4f50-4a61-8b72-0c3d4e5f6a7b"
], - "uses": [
- "codigo",
- "analise"
]
}[- {
- "id": "01JC4Z0000MODELO00000000001",
- "provider": "anthropic",
- "name": "claude-opus-4-8",
- "displayName": "Claude Opus 4.8",
- "inputPricePerMillionMicros": 15000000,
- "outputPricePerMillionMicros": 75000000,
- "contextWindow": 200000,
- "supportsToolCalling": true,
- "supportsStreaming": true,
- "supportsVision": false,
- "supportsReasoning": true,
- "generatesImage": false,
- "reasoningBudget": 2048,
- "manualPricing": true,
- "availability": "available",
- "billing": "api",
- "lastSeenAt": "2026-07-30T03:00:00.000Z",
- "createdAt": "2026-07-01T00:00:00.000Z",
- "updatedAt": "2026-07-01T00:00:00.000Z",
- "isActive": true,
- "uses": [
- "codigo"
], - "freeRoutingAlias": false
}
]面向所有者的视图。提供商维度回来了(ADR 0076,RN-186),并且在这里而不是在成员的路由上,因为按提供商细分就是按凭据细分——而凭据是付钱的人的事(RN-060)。补充 credential-spend,后者继续回答账单问题(按月,并与当前存在的密钥对应)。人和代理也按 actor_kind 分成单独的块。以天为单位的滑动窗口,每日序列是稠密的——没有花费的日子以零出现,否则迷你图会在节奏上说谎。
| workspaceId required | string |
| dias | string 滑动窗口。默认 30,最大 180。 |
| projectId | string Slice to ONE project of this workspace (RN-952). A project from another workspace answers 404. |
{- "workspaceId": "9b1c2d3e-4f50-4a61-8b72-0c3d4e5f6a7b",
- "ownerId": "9b1c2d3e-4f50-4a61-8b72-0c3d4e5f6a7b",
- "dias": 30,
- "projectId": { },
- "totalMicros": 1250000,
- "assinaturaMicros": 0,
- "inputTokens": 120000,
- "outputTokens": 35000,
- "chamadas": 42,
- "porModelo": [
- {
- "chave": "anthropic/claude-sonnet-4",
- "rotulo": "Loja",
- "actorKind": "agent",
- "costMicros": 1250000,
- "assinaturaMicros": 0,
- "inputTokens": 120000,
- "outputTokens": 35000,
- "chamadas": 42
}
], - "porProvider": [
- {
- "chave": "anthropic/claude-sonnet-4",
- "rotulo": "Loja",
- "actorKind": "agent",
- "costMicros": 1250000,
- "assinaturaMicros": 0,
- "inputTokens": 120000,
- "outputTokens": 35000,
- "chamadas": 42
}
], - "porProjeto": [
- {
- "chave": "anthropic/claude-sonnet-4",
- "rotulo": "Loja",
- "actorKind": "agent",
- "costMicros": 1250000,
- "assinaturaMicros": 0,
- "inputTokens": 120000,
- "outputTokens": 35000,
- "chamadas": 42
}
], - "porSessao": [
- {
- "chave": "anthropic/claude-sonnet-4",
- "rotulo": "Loja",
- "actorKind": "agent",
- "costMicros": 1250000,
- "assinaturaMicros": 0,
- "inputTokens": 120000,
- "outputTokens": 35000,
- "chamadas": 42
}
], - "porAtor": [
- {
- "chave": "anthropic/claude-sonnet-4",
- "rotulo": "Loja",
- "actorKind": "agent",
- "costMicros": 1250000,
- "assinaturaMicros": 0,
- "inputTokens": 120000,
- "outputTokens": 35000,
- "chamadas": 42
}
], - "porOwner": [
- {
- "chave": "anthropic/claude-sonnet-4",
- "rotulo": "Loja",
- "actorKind": "agent",
- "costMicros": 1250000,
- "assinaturaMicros": 0,
- "inputTokens": 120000,
- "outputTokens": 35000,
- "chamadas": 42
}
], - "porAgente": [
- {
- "chave": "anthropic/claude-sonnet-4",
- "rotulo": "Loja",
- "actorKind": "agent",
- "costMicros": 1250000,
- "assinaturaMicros": 0,
- "inputTokens": 120000,
- "outputTokens": 35000,
- "chamadas": 42
}
], - "porDia": [
- {
- "dia": "2026-08-09",
- "costMicros": 1250000,
- "chamadas": 42
}
]
}The non-owner's global page (RN-953). Only the caller's rows, by PROJECT and by day, without provider or credential.
| workspaceId required | string |
| dias | string 滑动窗口。默认 30,最大 180。 |
{- "workspaceId": "ef0efa32-d1c1-43d4-a5e2-fe7b4f00403c",
- "dias": 30,
- "actorId": "d2f1b55c-8cee-4a0d-af57-aebaeff77518",
- "totalMicros": 1250000,
- "assinaturaMicros": 0,
- "inputTokens": 120000,
- "outputTokens": 35000,
- "chamadas": 42,
- "porProjeto": [
- {
- "chave": "anthropic/claude-sonnet-4",
- "rotulo": "Loja",
- "actorKind": "agent",
- "costMicros": 1250000,
- "assinaturaMicros": 0,
- "inputTokens": 120000,
- "outputTokens": 35000,
- "chamadas": 42
}
], - "porDia": [
- {
- "dia": "2026-08-09",
- "costMicros": 1250000,
- "chamadas": 42
}
]
}{- "id": "01JC4Z0000PROJETO0000000001",
- "workspaceId": "01JC4Z0000WORKSPACE00000001",
- "name": "Checkout",
- "slug": "checkout",
- "workspaceDirName": "checkout-3f2b1c8e",
- "executionMode": "container",
- "workspacePath": null,
- "workspaceVerifiedAt": null,
- "mirrorPath": null,
- "language": "pt-BR",
- "createdBy": "01JC4Z0000USUARIO0000000001",
- "taskBudgetMicros": 500000,
- "maxConsecutiveBlocked": 3,
- "storyPromotion": "auto",
- "createdAt": "2026-07-21T11:00:00.000Z",
- "updatedAt": "2026-07-21T11:00:00.000Z"
}| projectId required | string |
| name | string >= 2 characters |
| slug | string^[a-z0-9]+(-[a-z0-9]+)*$ kebab-case。在 workspace 内唯一。 |
| maxConsecutiveBlocked | number 熔断器(Phase 12b——RN-047):连续多少个任务以阻塞结束会让该模块的 dev agent 停在 idle_tripped。省略时使用领域默认值(DEFAULT_MAX_CONSECUTIVE_BLOCKED)。从下一次激活执行起生效——不影响已在运行的代理。 |
| storyPromotion | string 枚举: "manual" "auto" 谁把 story 从 |
| language | string <= 35 characters 项目的语言(RN-619)——共享制品和没有人类作者的回合使用它。服务器能识别的任何 BCP-47 代码,以规范形式存储;无法识别的返回 400。省略时,项目以其创建者的有效回复语言开始(RN-618:账户选择 > 已确认的检测 > 界面语言)。 |
{- "name": "Checkout",
- "slug": "checkout",
- "maxConsecutiveBlocked": 3,
- "storyPromotion": "auto",
- "language": "pt-BR"
}{- "id": "01JC4Z0000PROJETO0000000001",
- "workspaceId": "01JC4Z0000WORKSPACE00000001",
- "name": "Checkout",
- "slug": "checkout",
- "workspaceDirName": "checkout-3f2b1c8e",
- "executionMode": "container",
- "workspacePath": null,
- "workspaceVerifiedAt": null,
- "mirrorPath": null,
- "language": "pt-BR",
- "createdBy": "01JC4Z0000USUARIO0000000001",
- "taskBudgetMicros": 500000,
- "maxConsecutiveBlocked": 3,
- "storyPromotion": "auto",
- "createdAt": "2026-07-21T11:00:00.000Z",
- "updatedAt": "2026-07-21T11:00:00.000Z"
}{- "id": "01JC4Z0000PROJETO0000000001",
- "workspaceId": "01JC4Z0000WORKSPACE00000001",
- "name": "Checkout",
- "slug": "checkout",
- "workspaceDirName": "checkout-3f2b1c8e",
- "executionMode": "container",
- "workspacePath": null,
- "workspaceVerifiedAt": null,
- "mirrorPath": null,
- "language": "pt-BR",
- "createdBy": "01JC4Z0000USUARIO0000000001",
- "taskBudgetMicros": 500000,
- "maxConsecutiveBlocked": 3,
- "storyPromotion": "auto",
- "createdAt": "2026-07-21T11:00:00.000Z",
- "updatedAt": "2026-07-21T11:00:00.000Z"
}当状态为 sem_decisao 时,项目容器不会启动,Code 标签页返回 409——这就是 RN-105 的 gate。查看该状态所需的权限与查看项目相同,即 viewer。
| projectId required | string |
{- "status": "sem_decisao",
- "decisao": {
- "image": "node:22-bookworm-slim",
- "rationale": "The module_map is all TypeScript over Node; the slim variant is enough and reduces surface.",
- "network": "none",
- "resources": {
- "cpus": 2,
- "memoryMb": 4096,
- "pidsLimit": 512
}
}, - "version": 0,
- "eventId": "01JC4Z0000EVENTO000000001",
- "decidedAt": { }
}项目从未置备时为 null——这是目前的常见情况,因为还没有真正的编排器转换这张表(RN-243/RN-267)。status/imageVersion/resources 是登记的内容;observado 是 Docker 守护进程此刻报告的内容,经由 broker 查询(ADR 0130)。两者绝不合并:从外部被杀掉的容器读起来是登记为 running、观测为 exited。当无法查看时,observado 为 null,naoObservado 说明原因——它绝不继承登记状态(RN-468)。
| projectId required | string |
{- "status": "provisioning",
- "imageVersion": 1,
- "resources": {
- "cpus": 2,
- "memoryMb": 4096,
- "pidsLimit": 512
}, - "failureReason": null,
- "createdAt": "2019-08-24T14:15:22Z",
- "statusChangedAt": "2019-08-24T14:15:22Z",
- "observado": {
- "containerId": "c0ffeebabe...",
- "nome": "brabo-exp002-f52be111",
- "estado": "running",
- "imagem": "node:22-bookworm-slim",
- "iniciadoEm": { }
}, - "naoObservado": "broker-nao-configurado",
- "detalheDaObservacao": null
}The current declaration (ports on 127.0.0.1, DNS, extra hosts), or null. It is what was DECLARED; it only takes effect when the container is recreated (container_recreate).
| projectId required | string |
{- "ports": [
- {
- "host": 8080,
- "container": 3000,
- "protocol": "tcp"
}
], - "dns": [
- "string"
], - "extraHosts": [
- {
- "name": "api.local",
- "ip": "10.0.0.5"
}
], - "version": 1
}Emits a new version of artifact.container_network (the current one replaces the previous). Not an external effect: nothing changes in Docker until a container_recreate is approved (AT-608, RN-932). 400 rede_invalida names the field.
| projectId required | string |
数组:objects (PortaDto) Up to 10 TCP ports published on 127.0.0.1 only. | |
| dns | 数组:strings Up to 3 literal IPs. |
数组:objects (HostExtraDto) Up to 10 |
{- "ports": [
- {
- "host": 8080,
- "container": 3000,
- "protocol": "tcp"
}
], - "dns": [
- "string"
], - "extraHosts": [
- {
- "name": "api.local",
- "ip": "10.0.0.5"
}
]
}{- "ports": [
- {
- "host": 8080,
- "container": 3000,
- "protocol": "tcp"
}
], - "dns": [
- "string"
], - "extraHosts": [
- {
- "name": "api.local",
- "ip": "10.0.0.5"
}
], - "version": 1
}为已存在的项目迁移 execution_mode/workspacePath(RN-447..450,ADR 0111)——专用路由,与 PATCH 分开,因为它编排的不只是写一个列:它把 permissions.json 移动到新的范围根目录,在离开 container 模式时撤下容器生命周期记录,并且只要项目中有任何 dev agent 不处于空闲状态就拒绝(409),因为运行中的代理不会自行重新解析其 worktree 位置。
| projectId required | string |
| executionMode required | string 枚举: "container" "mounted" "runner" 新的执行模式。可以与项目当前的模式相同——这种情况下,真正改变的只有 |
| workspacePath | string 用户文件夹的绝对路径——当 |
{- "executionMode": "runner",
- "workspacePath": "/home/you/projects/store"
}{- "id": "01JC4Z0000PROJETO0000000001",
- "workspaceId": "01JC4Z0000WORKSPACE00000001",
- "name": "Checkout",
- "slug": "checkout",
- "workspaceDirName": "checkout-3f2b1c8e",
- "executionMode": "container",
- "workspacePath": null,
- "workspaceVerifiedAt": null,
- "mirrorPath": null,
- "language": "pt-BR",
- "createdBy": "01JC4Z0000USUARIO0000000001",
- "taskBudgetMicros": 500000,
- "maxConsecutiveBlocked": 3,
- "storyPromotion": "auto",
- "createdAt": "2026-07-21T11:00:00.000Z",
- "updatedAt": "2026-07-21T11:00:00.000Z"
}该角色会覆盖此人在 workspace 中的角色,两个方向都是:在这里把某人关联为 viewer 确实会在此项目上限制一个 workspace developer。有两种变动会以 403 拒绝,且在任何地方都无法启用:降级一个 workspace owner,以及修改你自己的角色——无论降级还是升级,因为自我提升正是提权的那一半。
| projectId required | string |
| userId required | string <uuid> 要关联的用户 id。 |
| role required | string 枚举: "viewer" "developer" "maintainer" "owner" 此关联中的角色。在项目上,它在两个方向上都覆盖 workspace 角色——只要关联存在,有效角色就是这个,无论更高还是更低——并且有两种变动会以 403 拒绝:降级 workspace |
{- "userId": "3f1b2c8e-5a4d-4b7e-9c10-2d6f8a1b4c33",
- "role": "developer"
}{- "projectId": "01JC4Z0000PROJETO0000000001",
- "userId": "01JC4Z0000USUARIO0000000002",
- "role": "maintainer",
- "createdAt": "2026-07-22T08:05:00.000Z"
}用户机器上的绝对路径,本地代理会把工作复制到这里(RN-515,ADR 0147)——一个位于挂载基目录之外的文件夹,这正是镜像存在的全部理由。按项目设置,绝不全局:一个全局目标会把项目 B 的制品放进项目 A 的文件夹,而用户会从内容中发现,而不是从错误中发现。mirrorPath: null 会清除它并关闭镜像——该键是必需的,省略它返回 400,因为省略字段的请求体与请求清除无法区分。null 是项目的正常状态。maintainer,与 execution-mode 和 projects-base 的最低要求相同:此路由涉及运营者自己文件系统上的路径。它只校验词法形态,并以 400 拒绝位于 workspacePath 内部或包含它的目标(同一循环的两个方向),以及 container 项目上的任何目标。它从不触碰磁盘:API 看不到目标所在的机器。写入镜像不是 proposed_action——它是用户声明的配置,不是代理请求执行操作。
| projectId required | string |
| mirrorPath required | string or null 用户机器上的绝对路径,本地代理会把项目工作复制到这里(RN-515,ADR 0147)。发送 |
{- "mirrorPath": "/home/you/mirrors/store"
}{- "id": "01JC4Z0000PROJETO0000000001",
- "workspaceId": "01JC4Z0000WORKSPACE00000001",
- "name": "Checkout",
- "slug": "checkout",
- "workspaceDirName": "checkout-3f2b1c8e",
- "executionMode": "container",
- "workspacePath": null,
- "workspaceVerifiedAt": null,
- "mirrorPath": null,
- "language": "pt-BR",
- "createdBy": "01JC4Z0000USUARIO0000000001",
- "taskBudgetMicros": 500000,
- "maxConsecutiveBlocked": 3,
- "storyPromotion": "auto",
- "createdAt": "2026-07-21T11:00:00.000Z",
- "updatedAt": "2026-07-21T11:00:00.000Z"
}本地代理在把工作复制到用户文件夹后通过通道推送的遥测(ADR 0147,第 7 点)——绝不进入事件日志,因为一轮镜像没有会话,而 session_events.session_id 是 NOT NULL(与把 rag_searches 做成表的理由相同)。三种答案绝不合并为一(RN-088):never(从未报告过任何一轮)、synced(最近一轮已复制——filesCopied 可能为 0,表示“看过了,没有需要复制的”)和 failed。失败绝不会抹去最近一次成功的同步,成功也绝不会抹去最近一次错误:哪个是当前状态由比较两个时间戳得出,因此界面可以说“从今天开始失败,最近一次成功复制是昨天,共 412 个文件”。lastDestination 是冻结的——在有人修改目标后它会与 mirrorPath 不同,因为授权随 join 传递,只在 runner 重连时才会改变(RN-516)。viewer,与读取项目的最低要求相同,项目本身已携带 mirrorPath。
| projectId required | string |
{- "mirrorPath": "/home/you/mirrors/store",
- "status": "synced",
- "lastSyncedAt": "2026-09-07T12:04:00.000Z",
- "filesCopied": 412,
- "filesSkipped": 3,
- "filesRefused": 0,
- "lastDestination": "/home/you/mirrors/store",
- "lastError": null,
- "lastErrorAt": null
}这是项目 workspace 根目录下的物理文件,而不是数据库列。
| projectId required | string |
{- "allow": [
- "Terminal(pnpm test:*)",
- "Terminal(pnpm lint)"
], - "deny": [
- "Terminal(rm -rf *)",
- "Terminal(git push --force*)"
], - "ask": [
- "Terminal(pnpm add *)"
]
}一次替换全部三个列表。评估时 deny 仍优先于 allow——无法通过这里允许被拒绝的内容。
| projectId required | string |
| allow required | 数组:strings 自动批准的模式。 |
| deny required | 数组:strings 禁止的模式。 |
| ask required | 数组:strings 始终需要人工决定的模式。 |
{- "allow": [
- "Terminal(pnpm test:*)",
- "Terminal(pnpm lint)"
], - "deny": [
- "Terminal(rm -rf *)",
- "Terminal(git push --force*)"
], - "ask": [
- "Terminal(pnpm add *)"
]
}{- "allow": [
- "Terminal(pnpm test:*)",
- "Terminal(pnpm lint)"
], - "deny": [
- "Terminal(rm -rf *)",
- "Terminal(git push --force*)"
], - "ask": [
- "Terminal(pnpm add *)"
]
}从不包含原始令牌——只包含已签发的信息。
| projectId required | string |
[- {
- "id": "01JC4Z0000PAT000000000001",
- "name": "laptop",
- "projectId": "01JC4Z0000PROJETO000000001",
- "createdAt": "2026-08-22T12:00:00.000Z",
- "expiresAt": null,
- "revokedAt": null,
- "lastUsedAt": null
}
]原始令牌(brb_...)只出现在这个响应中——之后无法找回,只保存哈希。用于 --token/BRABO_ACCOUNT_TOKEN(brabo-runner 的参数)。
| projectId required | string |
| name required | string 供你以后认出这个令牌的名称——不要求唯一。 |
| expiresInDays | number N 天后过期。省略 = 不过期(随时可撤销,但绝不会自行过期)。 |
{- "name": "laptop",
- "expiresInDays": 90
}{- "id": "01JC4Z0000PAT000000000001",
- "name": "laptop",
- "projectId": "01JC4Z0000PROJETO000000001",
- "createdAt": "2026-08-22T12:00:00.000Z",
- "expiresAt": null,
- "revokedAt": null,
- "lastUsedAt": null,
- "token": "brb_9f8a..."
}供 maintainer 用于事件响应的视图(RN-427)——包含每个令牌的所有者。从不包含原始令牌。
| projectId required | string |
[- {
- "id": "01JC4Z0000PAT000000000001",
- "name": "laptop",
- "projectId": "01JC4Z0000PROJETO000000001",
- "createdAt": "2026-08-22T12:00:00.000Z",
- "expiresAt": null,
- "revokedAt": null,
- "lastUsedAt": null,
- "userId": "01JC4Z0000USUARIO000000001",
- "userEmail": "dev@exemplo.com"
}
]没有人能撤销看不到的东西(RN-519)。包含已撤销的密钥——让记录消失会让界面声称该密钥从未存在。从不返回 JWK 公钥,私钥 api 从未见过。lastUsedAt 为空是孤立密钥的信号:已登记但从未被任何 runner 使用。自 RN-543 起,还包含调用方的机器密钥(especie: "maquina",projectId 为空),它们服务于此项目却不属于它——如果列表中没有它们,机器密钥在任何界面上都将不可见。
| projectId required | string |
[- {
- "id": "01JC4Z0000CHAVE000000000001",
- "name": "laptop",
- "projectId": "01JC4Z0000PROJETO000000001",
- "especie": "projeto",
- "createdAt": "2026-08-27T12:00:00.000Z",
- "revokedAt": null,
- "lastUsedAt": null
}
]幂等——再次撤销不是错误。自 RN-520 起还会断开活动连接:此前撤销只阻止新票据,已连接的 runner 会继续执行已批准的命令。自 RN-685(ADR 0201)起,目标是密钥本身:只断开用它打开的连接——如果是机器密钥则遍及所有项目——同一用户用 PAT 或其他密钥连接的另一个 runner 不受影响。它尚未使用的票据会被作废。engine 宕机或没有 runner 连接都不会导致撤销失败。
| projectId required | string |
| deviceKeyId required | string |
用于 connect/3 认证,对象是 Phoenix 套接字 /runner(主题 terminal:<projectId>,角色 “runner”)——不是复用的 JWT,此路由本身只接受 Personal Access Token 代替会话 JWT(ADR 0105)。TTL 30 秒,一次性使用。如果项目不处于 “runner” 模式则以 400 拒绝(RN-421):在代码位于托管容器中的项目里,runner 无事可做。
| projectId required | string |
{- "ticket": "string",
- "expiresAt": "2026-08-09T12:00:30.000Z",
- "engineWsUrl": "ws://localhost:4000/runner"
}同一个 /runner 套接字、同一个主题,角色为 “terminal”——即查看终端的一方(命令本身只有在有 runner 连接时才会真正运行)。适用于任何项目模式。
| projectId required | string |
{- "ticket": "string",
- "expiresAt": "2026-08-09T12:00:30.000Z",
- "engineWsUrl": "ws://localhost:4000/runner"
}通过机器设备密钥认证(ADR 0154)——绝不通过会话 JWT,也绝不通过绑定到项目的凭据,后者返回 403。返回处于 execution_mode: "runner" 的项目,其中密钥所有者至少拥有 developer(与 POST .../runner-ticket 的最低要求相同),并附带每个项目的文件夹名称和验证状态。代理会询问而不是扫描磁盘:机器的基目录属于用户,其中可能有并非任何项目的文件夹。
[- {
- "projectId": "01JC4Z0000PROJETO000000001",
- "name": "Brabo",
- "workspaceDirName": "brabo-01jc4z",
- "workspaceVerifiedAt": null
}
]workspace 的每个项目一行——自 RN-521 起,从未置备过容器的项目会以 registrado: null 出现,而不是缺席,因为这个页面是人工启动第一个容器的途径。只对 provisioning/running 的行向 broker 查询观测状态,且仅在每次加载的预算之内——被跳过的行见 naoVerificado,原因见 ADR 0136。
| workspaceId required | string |
[- {
- "projectId": "01JC4Z0000PROJETO000001",
- "projectName": "exp002",
- "projectSlug": "exp002",
- "executionMode": "runner",
- "registrado": {
- "status": "running",
- "imageVersion": 1,
- "imagem": "node:22-bookworm-slim",
- "resources": {
- "cpus": 2,
- "memoryMb": 4096,
- "pidsLimit": 512
}, - "failureReason": null,
- "createdAt": "2019-08-24T14:15:22Z",
- "statusChangedAt": "2019-08-24T14:15:22Z"
}, - "temImagemDecidida": true,
- "workspaceVerifiedAt": { },
- "observado": {
- "containerId": "c0ffeebabe...",
- "nome": "brabo-exp002-f52be111",
- "estado": "running",
- "imagem": "node:22-bookworm-slim",
- "iniciadoEm": { }
}, - "naoObservado": "broker-nao-configurado",
- "detalheDaObservacao": null,
- "naoVerificado": null,
- "acaoPendente": {
- "id": "01JC4Z8QK3M7YV2N5T9B0PXHRC",
- "projectId": "01JC4Z0000PROJETO0000000001",
- "sessionId": "01JC4Z8QK3M7YV2N5T9B0PXHRA",
- "seq": 7,
- "actionType": "terminal",
- "payload": {
- "command": "pnpm test"
}, - "status": "pending",
- "resolvedPolicy": "require_approval",
- "actor": {
- "kind": "agent",
- "id": "dev-api"
}, - "decidedBy": null,
- "decidedAt": null,
- "rejectionReason": null,
- "executionResult": null,
- "createdAt": "2026-07-27T14:33:10.900Z",
- "updatedAt": "2026-07-27T14:33:10.900Z"
}, - "decisaoVigente": {
- "version": 3,
- "image": "node:22",
- "network": "egress"
}, - "imagemDesatualizada": false,
- "brokerConfigurado": false
}
]status 为 pending(默认)或 failed。actionType 按类型过滤——例如 git_merge,供 PR 标签页在不知道是哪个会话创建的情况下找到某个 PR 的合并提议。status=failed 要求提供 actionType,并列出执行失败的操作——PR 标签页从中读取每个 PR 最近一次被拒绝的合并(合并冲突会携带 executionResult.conflictingFiles)。
| projectId required | string |
| status | string 示例: status=pending |
| actionType | string 示例: actionType=git_merge |
[- {
- "id": "01JC4Z8QK3M7YV2N5T9B0PXHRC",
- "projectId": "01JC4Z0000PROJETO0000000001",
- "sessionId": "01JC4Z8QK3M7YV2N5T9B0PXHRA",
- "seq": 7,
- "actionType": "terminal",
- "payload": {
- "command": "pnpm test"
}, - "status": "pending",
- "resolvedPolicy": "require_approval",
- "actor": {
- "kind": "agent",
- "id": "dev-api"
}, - "decidedBy": null,
- "decidedAt": null,
- "rejectionReason": null,
- "executionResult": null,
- "createdAt": "2026-07-27T14:33:10.900Z",
- "updatedAt": "2026-07-27T14:33:10.900Z"
}
]按(代理,类型)upsert。它不会覆盖 permissions.json:已在 deny 中的模式无论在此授予多少自主权都保持阻止。actionType: "*" 即“自动模式”(RN-153)——对该代理任意操作类型的自主权;之后记录的特定规则在该类型上仍优先于通配符。
| projectId required | string |
| agentId required | string 代理的 slug。 |
| actionType required | string 枚举: "terminal" "git_commit" "git_push" "pr_open" "spend" "git_repo_create" "git_branch_create" "git_branch_protect" "write_file" "open_adr_pr" "git_merge" "open_infra_pr" "instruction_patch" "parallelize" "raise_max_parallel" "propose_execution_plan" "assess_implementability" "container_start" "container_stop" "container_remove" "container_recreate" "container_start_via_runner" "qa_environment_start" "*" 操作类型,或 |
| mode required | string 枚举: "auto_approve" "require_approval" "deny" 该代理对此类型的自主权。不会覆盖 |
{- "agentId": "dev-api",
- "actionType": "terminal",
- "mode": "auto_approve"
}按 seq 排序;将 nextCursor 用作 afterSeq。latest=true 改为获取尾部(最新的 limit 个操作,仍为升序)并忽略 afterSeq。status=pending 只保留等待决定的操作——配合 latest 时是最新的待处理操作,这样拥有 200 个以上操作的会话不会把新的待处理操作挤出窗口(RN-637)。
| projectId required | string |
| sessionId required | string |
| afterSeq | string 示例: afterSeq=6 |
| limit | string 示例: limit=50 |
| latest | string 示例: latest=true 获取会话尾部;忽略 |
| status | string 示例: status=pending 只支持 |
{- "items": [
- {
- "id": "01JC4Z8QK3M7YV2N5T9B0PXHRC",
- "projectId": "01JC4Z0000PROJETO0000000001",
- "sessionId": "01JC4Z8QK3M7YV2N5T9B0PXHRA",
- "seq": 7,
- "actionType": "terminal",
- "payload": {
- "command": "pnpm test"
}, - "status": "pending",
- "resolvedPolicy": "require_approval",
- "actor": {
- "kind": "agent",
- "id": "dev-api"
}, - "decidedBy": null,
- "decidedAt": null,
- "rejectionReason": null,
- "executionResult": null,
- "createdAt": "2026-07-27T14:33:10.900Z",
- "updatedAt": "2026-07-27T14:33:10.900Z"
}
], - "nextCursor": 7
}操作在创建时依据项目的 permissions.json 进行评估。响应中的 resolvedPolicy 说明策略的决定:auto_approve 出来时即可执行,require_approval 等待人工决定,deny 一出生即被拒绝。不存在不经过这里就执行的路径。
| projectId required | string |
| sessionId required | string |
| actionType required | string 枚举: "terminal" "git_commit" "git_push" "pr_open" "spend" "git_repo_create" "git_branch_create" "git_branch_protect" "write_file" "open_adr_pr" "git_merge" "open_infra_pr" "instruction_patch" "parallelize" "raise_max_parallel" "propose_execution_plan" "assess_implementability" "container_start" "container_stop" "container_remove" "container_recreate" "container_start_via_runner" "qa_environment_start" 操作类型。每种类型都有自己的最低角色: |
required | object 提议者。 |
required | object 操作参数,形态因 |
{- "actionType": "terminal",
- "actor": {
- "kind": "agent",
- "id": "dev-api"
}, - "payload": {
- "command": "pnpm test"
}
}{- "id": "01JC4Z8QK3M7YV2N5T9B0PXHRC",
- "projectId": "01JC4Z0000PROJETO0000000001",
- "sessionId": "01JC4Z8QK3M7YV2N5T9B0PXHRA",
- "seq": 7,
- "actionType": "terminal",
- "payload": {
- "command": "pnpm test"
}, - "status": "pending",
- "resolvedPolicy": "require_approval",
- "actor": {
- "kind": "agent",
- "id": "dev-api"
}, - "decidedBy": null,
- "decidedAt": null,
- "rejectionReason": null,
- "executionResult": null,
- "createdAt": "2026-07-27T14:33:10.900Z",
- "updatedAt": "2026-07-27T14:33:10.900Z"
}只适用于此操作。决定连同决定者的 id 一起保存在事件日志中。
| projectId required | string |
| sessionId required | string |
| actionId required | string |
{- "id": "01JC4Z8QK3M7YV2N5T9B0PXHRC",
- "projectId": "01JC4Z0000PROJETO0000000001",
- "sessionId": "01JC4Z8QK3M7YV2N5T9B0PXHRA",
- "seq": 7,
- "actionType": "terminal",
- "payload": {
- "command": "pnpm test"
}, - "status": "pending",
- "resolvedPolicy": "require_approval",
- "actor": {
- "kind": "agent",
- "id": "dev-api"
}, - "decidedBy": null,
- "decidedAt": null,
- "rejectionReason": null,
- "executionResult": null,
- "createdAt": "2026-07-27T14:33:10.900Z",
- "updatedAt": "2026-07-27T14:33:10.900Z"
}除了放行此操作外,还会把相应的模式加入项目的 allow 列表——今后匹配的操作会以 auto_approved 出来而无需询问。已在 deny 中的模式保持阻止。批准和模式在同一事务中记录。点击已被批准的操作属于幂等成功(desfecho: ja_aprovada),仅在模式缺失时才记录它。
| projectId required | string |
| sessionId required | string |
| actionId required | string |
{- "id": "01JC4Z8QK3M7YV2N5T9B0PXHRC",
- "projectId": "01JC4Z0000PROJETO0000000001",
- "sessionId": "01JC4Z8QK3M7YV2N5T9B0PXHRA",
- "seq": 7,
- "actionType": "terminal",
- "payload": {
- "command": "pnpm test"
}, - "status": "pending",
- "resolvedPolicy": "require_approval",
- "actor": {
- "kind": "agent",
- "id": "dev-api"
}, - "decidedBy": null,
- "decidedAt": null,
- "rejectionReason": null,
- "executionResult": null,
- "createdAt": "2026-07-27T14:33:10.900Z",
- "updatedAt": "2026-07-27T14:33:10.900Z",
- "desfecho": "aprovada",
- "padraoGravado": true
}如果提供了理由,它会与决定一起保存在事件日志中。
| projectId required | string |
| sessionId required | string |
| actionId required | string |
| reason | string 与拒绝一起保存在事件日志中。可选,但建议填写。 |
{- "reason": "The command would delete the working directory."
}{- "id": "01JC4Z8QK3M7YV2N5T9B0PXHRC",
- "projectId": "01JC4Z0000PROJETO0000000001",
- "sessionId": "01JC4Z8QK3M7YV2N5T9B0PXHRA",
- "seq": 7,
- "actionType": "terminal",
- "payload": {
- "command": "pnpm test"
}, - "status": "pending",
- "resolvedPolicy": "require_approval",
- "actor": {
- "kind": "agent",
- "id": "dev-api"
}, - "decidedBy": null,
- "decidedAt": null,
- "rejectionReason": null,
- "executionResult": null,
- "createdAt": "2026-07-27T14:33:10.900Z",
- "updatedAt": "2026-07-27T14:33:10.900Z"
}项目诞生时就带有三个区域(RN-094),因此即使还没有 module_map,列表也是完整的。激活执行所增加的是 dev 区域的成员,每个模块一个。
| projectId required | string |
[- {
- "id": "01JC4Z8QK3M7YV2N5T9B0PXHRA",
- "projectId": "01JC4Z8QK3M7YV2N5T9B0PXHRA",
- "key": "dev",
- "leadAgentId": "dev-lead",
- "maxParallel": 2,
- "budgetMicros": 20000000,
- "spentMicros": 4300000,
- "members": [
- "dev-api",
- "dev-web"
]
}
]需要 maintainer,理由与并行度上限相同:这是与项目和会话预算并列的独立、叠加的检查(ADR 0110)——不是级联,也不替代二者中的任何一个。限额以美元传入,并在服务端转换为微美元,与 BudgetsController 的约定相同。null 清除上限。
| projectId required | string |
| key required | string |
| limitUsd required | object or null >= 0 区域的上限,以美元计——与项目/会话预算( |
{- "limitUsd": 20
}{- "id": "01JC4Z8QK3M7YV2N5T9B0PXHRA",
- "projectId": "01JC4Z8QK3M7YV2N5T9B0PXHRA",
- "key": "dev",
- "leadAgentId": "dev-lead",
- "maxParallel": 2,
- "budgetMicros": 20000000,
- "spentMicros": 4300000,
- "members": [
- "dev-api",
- "dev-web"
]
}需要 maintainer 而非 developer,理由与激活执行相同:提高上限就是决定产品在不询问的情况下可以花多少。适用于后续请求——已在等待你决定的内容会继续等待。
| projectId required | string |
| key required | string |
| maxParallel required | number >= 1 区域 lead 在会话中无需请求你授权即可拥有的代理数量。最小为 1——零不表示“无限制”,而是无效配置,把它当作无限制会让一个笔误变成不受限制的花费。 |
{- "maxParallel": 4
}{- "id": "01JC4Z8QK3M7YV2N5T9B0PXHRA",
- "projectId": "01JC4Z8QK3M7YV2N5T9B0PXHRA",
- "key": "dev",
- "leadAgentId": "dev-lead",
- "maxParallel": 2,
- "budgetMicros": 20000000,
- "spentMicros": 4300000,
- "members": [
- "dev-api",
- "dev-web"
]
}需要 maintainer 而非 developer,因为 dev agent 的 git 操作在 IAM 评估中继承激活者的角色——以 developer 身份激活会让代理无法打开 PR。module_map 中的每个模块都会在隔离的 worktree 中获得一个代理。
| projectId required | string |
| taskBudgetMicros | number 每个任务的预算,以微美元计。省略时使用领域默认值(DEFAULT_TASK_BUDGET_MICROS)。 |
| maxGateCorrections | number 在任务变为 |
| devAgentImpl | string 枚举: "real" "noop"
|
| terminalAllowPatterns | 数组:strings 在 |
| originSessionId | string “激活执行”点击所来自的聊天会话的 id——Dev Lead/PO 的创作式/咨询式会话,绝不是执行会话。省略时(旧版调用方,例如在没有会话上下文的 Overview 中激活)不关闭任何会话,与以前相同。提供时,会在最后关闭它——但仅当它没有待处理的 handoff/操作/回合时(RN-135),并且如果它就是执行会话本身则绝不关闭。 |
{- "taskBudgetMicros": 500000,
- "maxGateCorrections": 3,
- "devAgentImpl": "real",
- "terminalAllowPatterns": [
- "Terminal(pnpm test:*)",
- "Terminal(pnpm lint)"
], - "originSessionId": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
}{- "sessionId": "01JC4Z8QK3M7YV2N5T9B0PXHRA",
- "modules": [
- "api",
- "web"
]
}最近的 active 会话,且已携带 execution.activated——与 activate 决定是重新激活还是新建时使用的标准相同(RN-139)。没有执行在进行时为 null。它的存在让 Executors 标签页不再从项目最近的会话推断这个会话,那会随会话变化而变化,界面中毫无线索。
| projectId required | string |
{- "id": "01JC4Z8QK3M7YV2N5T9B0PXHRA",
- "projectId": "01JC4Z0000PROJETO0000000001",
- "createdBy": "01JC4Z0000USUARIO0000000001",
- "status": "active",
- "kind": "criativa",
- "name": "Cart checkout",
- "nextSeq": 42,
- "createdAt": "2026-07-27T14:03:22.187Z",
- "updatedAt": "2026-07-27T14:31:09.004Z",
- "closedAt": null,
- "terminationReason": null,
- "traceParent": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
}受 dev 区域上限约束(RN-083):在上限之内,代理会立即在自己的 worktree 中启动;超过上限时,响应是一个 proposed_action,类型为 parallelize,等待你的决定,在你决定之前什么都不会启动。上限设为多少可在 Settings 中按 lead 配置。
| projectId required | string |
| sessionId required | string |
| module required | string 模块名称,与 |
{- "module": "api"
}{- "estado": "aguardando_autorizacao",
- "actionId": "01JC4Z8QK3M7YV2N5T9B0PXHRA",
- "ativosNaSessao": 2,
- "maxParallel": 2,
- "motivo": "string"
}最新的在前,每个都已携带与上一版本的差异,由服务端计算。
| projectId required | string |
| agent required | string 示例: dev-api |
[- {
- "id": "01JC4Z0000VERSAO00000000001",
- "version": 4,
- "content": "# dev-api\n\nAlways run the suite before opening the PR.",
- "createdBy": null,
- "sourceActionId": null,
- "sourceHypothesisId": "01JC4Z0000HIPOTESE000000001",
- "note": "Explicit definition-of-done criterion",
- "createdAt": "2026-07-26T14:00:00.000Z",
- "isCurrent": true,
- "diff": {
- "lines": [
- {
- "kind": "add",
- "content": "Always run the suite before opening the PR.",
- "lineNo": 42
}
], - "additions": 3,
- "deletions": 1
}
}
]历史不可变:恢复不会删除任何东西,而是以旧内容创建一个新版本。需要 maintainer,因为它会从此改变代理的行为——与补丁的校准相同。
| projectId required | string |
| agent required | string 示例: dev-api |
| version required | string 示例: 2 要恢复的版本。 |
{- "agent": "dev-api",
- "restoredFrom": 2,
- "toVersion": 5,
- "cacheInvalidated": true
}与按会话查找事件的路由不同:这里不知道会话。正是这一点让画像的证据标签落在正确的事件上,因为 Anamnese 的窗口跨越多个会话。
| projectId required | string |
| eventId required | string |
{- "id": "01JC4Z8QK3M7YV2N5T9B0PXHRB",
- "sessionId": "01JC4Z8QK3M7YV2N5T9B0PXHRA",
- "seq": 41,
- "type": "chat.message",
- "actor": {
- "kind": "agent",
- "id": "dev-api"
}, - "payload": {
- "role": "assistant",
- "content": "I'll open the epic first."
}, - "createdAt": "2026-07-27T14:30:58.512Z"
}从在项目中拥有版本的代理出发,而不是从静态名单出发——正是这一点让按模块划分的 dev agent(dev-api)显示出来。
| projectId required | string |
[- {
- "agent": "dev-api",
- "versions": [
- {
- "id": "01JC4Z0000VERSAO00000000001",
- "version": 4,
- "content": "# dev-api\n\nAlways run the suite before opening the PR.",
- "createdBy": null,
- "sourceActionId": null,
- "sourceHypothesisId": "01JC4Z0000HIPOTESE000000001",
- "note": "Explicit definition-of-done criterion",
- "createdAt": "2026-07-26T14:00:00.000Z",
- "isCurrent": true,
- "diff": {
- "lines": [
- {
- "kind": "add",
- "content": "Always run the suite before opening the PR.",
- "lineNo": 42
}
], - "additions": 3,
- "deletions": 1
}
}
]
}
]默认是用户自己的画像;团队汇总视图只对管理该项目的人开放。能力画像是关于这个人的数据,最不令人意外的默认是看到你自己的。
| projectId required | string |
[- {
- "id": "01JC4Z0000PERFIL00000000001",
- "projectId": "01JC4Z0000PROJETO0000000001",
- "userId": "01JC4Z0000USUARIO0000000002",
- "competency": "TypeScript",
- "level": "avancado",
- "rationale": "Fixed three generic typing errors without help.",
- "evidenceEventIds": [
- "01JC4Z8QK3M7YV2N5T9B0PXHRB"
], - "createdAt": "2026-07-26T12:00:00.000Z",
- "updatedAt": "2026-07-26T12:00:00.000Z",
- "userName": "Senior Dev",
- "userEmail": "dev@brabo.dev"
}
]当前映射是 version 最高的那个(历史不可变)。待办项是 story↔映射的交叉校验:没有模块的 story,或指向不存在模块的 story。
| projectId required | string |
{- "moduleMap": {
- "id": "01JC4Z0000MODULEMAP00000001",
- "projectId": "01JC4Z0000PROJETO0000000001",
- "sessionId": "01JC4Z8QK3M7YV2N5T9B0PXHRA",
- "modules": [
- {
- "name": "api",
- "stack": "NestJS + Drizzle",
- "responsibility": "Business rules and HTTP surface.",
- "dependsOn": [
- "db"
], - "resources": {
- "cpus": 2,
- "memoryMb": 4096,
- "pidsLimit": 512
}
}
], - "version": 3,
- "createdAt": "2026-07-24T16:00:00.000Z"
}, - "adrs": [
- {
- "actionId": "01JC4Z8QK3M7YV2N5T9B0PXHRC",
- "title": "0007 — Postgres queue instead of Redis",
- "status": "executed",
}
], - "pendencies": [
- {
- "storyId": "01JC4Z0000HISTORIA000000001",
- "title": "Add item to cart",
- "status": "ready",
- "reason": "missing_module",
- "missing": [
- "pagamentos"
]
}
], - "c4Diagram": {
- "status": "sem_diagrama",
- "diagrama": {
- "systemName": "Brabo",
- "systemDescription": "Agent-orchestrated engineering platform.",
- "actors": [
- {
- "name": "User",
- "type": "person",
- "description": "Whoever operates the product through the web."
}
], - "contextDiagram": "C4Context\n title Context Diagram -- Brabo\n ...",
- "containerDiagram": "C4Container\n title Container Diagram -- Brabo\n ..."
}, - "version": 0,
- "eventId": "01JC4Z0000EVENTO000000002",
- "createdAt": { }
}
}嵌套且完整,一次调用即可。已归档的 story(RN-727)不会列出。backlog 由代理通过 /internal/* 路由写入。
| projectId required | string |
[- {
- "id": "01JC4Z0000EPICO000000000001",
- "projectId": "01JC4Z0000PROJETO0000000001",
- "sessionId": "01JC4Z8QK3M7YV2N5T9B0PXHRA",
- "title": "Shopping cart",
- "description": "Everything the buyer does before paying.",
- "createdAt": "2026-07-25T08:30:00.000Z",
- "updatedAt": "2026-07-25T08:30:00.000Z",
- "stories": [
- {
- "id": "01JC4Z0000HISTORIA000000001",
- "epicId": "01JC4Z0000EPICO000000000001",
- "projectId": "01JC4Z0000PROJETO0000000001",
- "sessionId": "01JC4Z8QK3M7YV2N5T9B0PXHRA",
- "title": "Add item to cart",
- "description": "As a buyer, I want to gather items before paying.",
- "rf": [
- "The cart accepts up to 50 items"
], - "rnf": [
- "The response stays under 200 ms at p95"
], - "businessRuleIds": [
- "RN-014"
], - "dod": [
- "Unit tests green"
], - "dor": [
- "Module defined"
], - "moduleIds": [
- "api"
], - "status": "ready",
- "proposedReady": true,
- "returnedReason": "DoD too generic — spell out the acceptance criteria.",
- "returnedAt": "2026-08-02T14:00:00.000Z",
- "archivedAt": null,
- "archivedReason": null,
- "createdAt": "2026-07-25T09:00:00.000Z",
- "updatedAt": "2026-07-25T09:40:00.000Z",
- "tasks": [
- {
- "id": "01JC4Z0000TAREFA00000000001",
- "storyId": "01JC4Z0000HISTORIA000000001",
- "title": "POST /cart/items endpoint",
- "description": "Accepts SKU and quantity; validates stock.",
- "status": "in_progress",
- "assignedTo": "dev-api",
- "blocked": false,
- "blockedReason": null,
- "blockedOrigin": null,
- "gateStatus": null,
- "gateCorrectionCount": 0,
- "module": "api",
- "createdAt": "2026-07-25T10:00:00.000Z",
- "updatedAt": "2026-07-25T12:30:00.000Z"
}
]
}
]
}
]将项目的规则与 story 的 businessRuleIds 交叉比对。完全没有 story 的规则是一项发现——是 PO 的待办项,而不是错误。
| projectId required | string |
{- "rules": [
- {
- "ruleId": "RN-014",
- "title": "Cart accepts at most 50 items",
- "coveredByStoryIds": [
- "01JC4Z0000HISTORIA000000001"
], - "covered": true
}
], - "uncoveredCount": 2
}InfraAgent 的制品(Dockerfile、compose、CI)。它们经过与 dev PR 相同的 QA 和 SecOps gate,背后没有任务或 worktree。
| projectId required | string |
[- {
- "id": "01JC4Z0000INFRAARTEFATO00001",
- "projectId": "01JC4Z0000PROJETO0000000001",
- "sessionId": "01JC4Z8QK3M7YV2N5T9B0PXHRA",
- "title": "api's production Dockerfile",
- "prActionId": "01JC4Z8QK3M7YV2N5T9B0PXHRD",
- "gateStatus": "awaiting_qa",
- "gateCorrectionCount": 0,
- "blocked": false,
- "blockedReason": null,
- "createdAt": "2026-07-26T09:00:00.000Z",
- "updatedAt": "2026-07-26T09:20:00.000Z"
}
]状态来自事件日志:未决即存在 artifact.open_question 但没有 open_question.answered。最新的在前,上限 50 条并附 total。
| projectId required | string |
| status | string 默认值: "open" 枚举: "open" "answered" "all" |
{- "itens": [
- {
- "questionId": "ffa0420e-f70b-4e5b-99a0-434bff860467",
- "eventId": "string",
- "sessionId": "string",
- "pergunta": "string",
- "sugestao": "string",
- "afeta": {
- "regras": [
- "string"
], - "historias": [
- "string"
], - "tarefas": [
- "string"
]
}, - "autor": "criativo",
- "criadaEm": "2019-08-24T14:15:22Z",
- "status": "open",
- "autorDaSessao": "string",
- "resposta": {
- "texto": "string",
- "aceitouSugestao": true,
- "decisionEventId": "string",
- "respondidaPor": "string",
- "respondidaEm": "2019-08-24T14:15:22Z"
}
}
], - "total": 0
}只有问题所在会话的作者才能回答。写入一条 decision_record(参与者:该用户,附 questionId)和 open_question.answered,并尽力请求 engine 恢复每个受影响任务被暂停的 QA gate(RN-860)。
| projectId required | string |
| questionId required | string |
| resposta | string 自由文本回答。发送这个或者 |
| aceitarSugestao | boolean 接受代理的建议作为回答。 |
{- "resposta": "404 for a code that does not exist.",
- "aceitarSugestao": true
}{- "questionId": "ffa0420e-f70b-4e5b-99a0-434bff860467",
- "eventId": "string",
- "sessionId": "string",
- "pergunta": "string",
- "sugestao": "string",
- "afeta": {
- "regras": [
- "string"
], - "historias": [
- "string"
], - "tarefas": [
- "string"
]
}, - "autor": "criativo",
- "criadaEm": "2019-08-24T14:15:22Z",
- "status": "open",
- "autorDaSessao": "string",
- "resposta": {
- "texto": "string",
- "aceitouSugestao": true,
- "decisionEventId": "string",
- "respondidaPor": "string",
- "respondidaEm": "2019-08-24T14:15:22Z"
}
}RN-852(ADR 0213):当前的 artifact.run_instructions,在 open_infra_pr 携带 run 执行时发出。由 Infra 代理声明,从未在读者的机器上验证。没有时为 null。
| projectId required | string |
{- "command": "cp .env.example .env && docker compose up --build",
- "envExample": true,
- "app": {
- "command": "npm start",
- "port": 3000
}, - "qa": { },
- "version": 1,
- "eventId": "01JC4Z0000EVENTORUNINSTRUC01",
- "declaredAt": "2026-10-11T12:00:00.000Z"
}该 story 离开 backlog、覆盖、提升队列、Dev Lead 的计划和认领——其任务也一样。不会删除任何内容:记录保留并带有 archivedAt,backlog.story_archived 记录由谁、为何以及哪些任务。仅限没有执行中任务的 draft story。
| projectId required | string |
| storyId required | string |
| reason | string 可选;记录在 |
{- "reason": "Duplicate of \"Register a user\"."
}{- "id": "01JC4Z0000HISTORIA000000001",
- "epicId": "01JC4Z0000EPICO000000000001",
- "projectId": "01JC4Z0000PROJETO0000000001",
- "sessionId": "01JC4Z8QK3M7YV2N5T9B0PXHRA",
- "title": "Add item to cart",
- "description": "As a buyer, I want to gather items before paying.",
- "rf": [
- "The cart accepts up to 50 items"
], - "rnf": [
- "The response stays under 200 ms at p95"
], - "businessRuleIds": [
- "RN-014"
], - "dod": [
- "Unit tests green"
], - "dor": [
- "Module defined"
], - "moduleIds": [
- "api"
], - "status": "ready",
- "proposedReady": true,
- "returnedReason": "DoD too generic — spell out the acceptance criteria.",
- "returnedAt": "2026-08-02T14:00:00.000Z",
- "archivedAt": null,
- "archivedReason": null,
- "createdAt": "2026-07-25T09:00:00.000Z",
- "updatedAt": "2026-07-25T09:40:00.000Z"
}该 story 离开提议队列,理由会变成固定在 PO 会话中的一条消息——与把 gate 退回给 dev 的模式相同。如果该会话的 PO 已不在运行,退回仍会被记录:这是用户的决定,不依赖于有代理在听。
| projectId required | string |
| storyId required | string |
| reason required | string 理由是必需的:它会变成固定在 PO 会话中的消息,没有理由的退回等于交还了没有任何信息的工作。 |
{- "reason": "The acceptance criteria don't cover the payment refusal case."
}{- "ok": true
}仅限 draft story,且没有执行中的任务(in_progress/in_review)。写入 backlog.story_updated,附带旧标题和新标题。所需角色与其他 backlog 写入操作(提升/退回)相同。
| projectId required | string |
| storyId required | string |
| title required | string 已去除首尾空白;1 到 200 个字符。 |
{- "title": "Register a user"
}{- "id": "01JC4Z0000HISTORIA000000001",
- "epicId": "01JC4Z0000EPICO000000000001",
- "projectId": "01JC4Z0000PROJETO0000000001",
- "sessionId": "01JC4Z8QK3M7YV2N5T9B0PXHRA",
- "title": "Add item to cart",
- "description": "As a buyer, I want to gather items before paying.",
- "rf": [
- "The cart accepts up to 50 items"
], - "rnf": [
- "The response stays under 200 ms at p95"
], - "businessRuleIds": [
- "RN-014"
], - "dod": [
- "Unit tests green"
], - "dor": [
- "Module defined"
], - "moduleIds": [
- "api"
], - "status": "ready",
- "proposedReady": true,
- "returnedReason": "DoD too generic — spell out the acceptance criteria.",
- "returnedAt": "2026-08-02T14:00:00.000Z",
- "archivedAt": null,
- "archivedReason": null,
- "createdAt": "2026-07-25T09:00:00.000Z",
- "updatedAt": "2026-07-25T09:40:00.000Z"
}manual 模式(自 Phase 12c 起为默认)交还给用户的人工步骤:在提升之前,该 story 的任何任务都不能被任何 dev agent 认领。提升会一次性放出这批任务,并唤醒该模块中空闲的代理(Phase 12b)。这一批不是全有或全无——未通过的会以 failed 返回并附上原因,通过的则被提升。
| projectId required | string |
| storyIds required | 数组:strings 要提升为 |
{- "storyIds": [
- "01JC2XK9Q7",
- "01JC2XKB4M"
]
}{- "promoted": [
- "string"
], - "failed": [
- {
- "storyId": "01JC2XK9Q7",
- "reason": "Story is not ready: missing dod, dor"
}
]
}每条假设都引用支持它的事件(evidenceEventIds)——正是这一点让它可审计,而不是凭信任接受。
| projectId required | string |
[- {
- "id": "01JC4Z0000HIPOTESE000000001",
- "projectId": "01JC4Z0000PROJETO0000000001",
- "sessionId": "01JC4Z8QK3M7YV2N5T9B0PXHRA",
- "analysisId": "01JC4Z0000ANALISE0000000001",
- "agenteAlvo": "dev-api",
- "observacao": "The agent reopened the same task three times.",
- "hipotese": "The instructions don't say when to consider the task done.",
- "sugestao": "Add an explicit definition-of-done criterion to the instruction.",
- "confiancaPercent": 72,
- "evidenceEventIds": [
- "01JC4Z8QK3M7YV2N5T9B0PXHRB"
], - "terminationAnalysis": {
- "causa": "heartbeat_timeout",
- "estadoDaSessao": "closed_abnormally",
- "analise": "The agent stopped responding after a 90s tool call."
}, - "status": "proposed",
- "decidedBy": null,
- "decidedAt": null,
- "createdAt": "2026-07-26T18:00:00.000Z",
- "updatedAt": "2026-07-26T18:00:00.000Z"
}
]将 proposed → accepted。不是 proposed_action:Psychologist 是只读的,这里没有任何外部效果。
| projectId required | string |
| hypothesisId required | string |
{- "id": "01JC4Z0000HIPOTESE000000001",
- "projectId": "01JC4Z0000PROJETO0000000001",
- "sessionId": "01JC4Z8QK3M7YV2N5T9B0PXHRA",
- "analysisId": "01JC4Z0000ANALISE0000000001",
- "agenteAlvo": "dev-api",
- "observacao": "The agent reopened the same task three times.",
- "hipotese": "The instructions don't say when to consider the task done.",
- "sugestao": "Add an explicit definition-of-done criterion to the instruction.",
- "confiancaPercent": 72,
- "evidenceEventIds": [
- "01JC4Z8QK3M7YV2N5T9B0PXHRB"
], - "terminationAnalysis": {
- "causa": "heartbeat_timeout",
- "estadoDaSessao": "closed_abnormally",
- "analise": "The agent stopped responding after a 90s tool call."
}, - "status": "proposed",
- "decidedBy": null,
- "decidedAt": null,
- "createdAt": "2026-07-26T18:00:00.000Z",
- "updatedAt": "2026-07-26T18:00:00.000Z"
}将 proposed → dismissed。理由与接受相同。
| projectId required | string |
| hypothesisId required | string |
{- "id": "01JC4Z0000HIPOTESE000000001",
- "projectId": "01JC4Z0000PROJETO0000000001",
- "sessionId": "01JC4Z8QK3M7YV2N5T9B0PXHRA",
- "analysisId": "01JC4Z0000ANALISE0000000001",
- "agenteAlvo": "dev-api",
- "observacao": "The agent reopened the same task three times.",
- "hipotese": "The instructions don't say when to consider the task done.",
- "sugestao": "Add an explicit definition-of-done criterion to the instruction.",
- "confiancaPercent": 72,
- "evidenceEventIds": [
- "01JC4Z8QK3M7YV2N5T9B0PXHRB"
], - "terminationAnalysis": {
- "causa": "heartbeat_timeout",
- "estadoDaSessao": "closed_abnormally",
- "analise": "The agent stopped responding after a 90s tool call."
}, - "status": "proposed",
- "decidedBy": null,
- "decidedAt": null,
- "createdAt": "2026-07-26T18:00:00.000Z",
- "updatedAt": "2026-07-26T18:00:00.000Z"
}costMicros 由该轮的 token_usage 求和而得。正是这一点让轻量与重量分诊之间的成本差异可见,而不仅仅是断言——两个层级使用的确实是不同的模型。
| projectId required | string |
[- {
- "id": "01JC4Z0000ANALISE0000000001",
- "projectId": "01JC4Z0000PROJETO0000000001",
- "sessionId": "01JC4Z8QK3M7YV2N5T9B0PXHRA",
- "tier": "leve",
- "triggeredBy": "auto",
- "supersedes": null,
- "superseded": false,
- "supersededAt": null,
- "eventCountAtAnalysis": 128,
- "createdAt": "2026-07-26T18:00:00.000Z",
- "costMicros": 4200,
- "hypothesisCount": 3
}
]真实计数(从不估算):.md 文件(位于项目仓库的 docs/docs/adr 下)与其中有分块的数量对比,项目的会话数与其中有分块的数量对比。这三个范围不包含“N 分钟前”——没有按范围的索引时间戳列,猜测的数字会说谎(与 ADR 0042 对模型评级的规则相同)。local(RN-455)是唯一的例外:lastAttachedAt 是该范围上真实的 MAX(created_at),因为浏览器上传没有可供比较的仓库状态。
| projectId required | string |
{- "docs": {
- "filesInRepo": 0,
- "filesIndexed": 0,
- "truncated": true
}, - "adr": {
- "filesInRepo": 0,
- "filesIndexed": 0,
- "truncated": true
}, - "session": {
- "sessionsInProject": 0,
- "sessionsIndexed": 0
}, - "local": {
- "filesIndexed": 0,
- "folderName": "string",
- "lastAttachedAt": "string"
}, - "chunksTotal": 0,
- "chunksWithoutVector": 0
}RAG 测量唯一的真实性信号:延迟和降级率说明搜索是否运行了,却从不说明它是否正确。角色与 search 相同——能读取结果的人就能评判它。对同一搜索的同一分块重新投票会覆盖你自己的投票,而不是新增一票(每个参与者唯一),因此该指标衡量的是准确性而不是热情。
| projectId required | string |
| searchId required | string <uuid> 产生此命中的搜索所返回的 |
| chunkId required | string <uuid> 被评判的分块。必须是那次搜索返回的命中之一,否则返回 400。 |
| verdict required | string 枚举: "util" "irrelevante" 两个值,而不是 1-5 分制:更细的刻度会招致每个投票者标尺不同,之后任何聚合都无法还原。 |
{- "searchId": "9b1a67f9-9477-48e5-8a6c-11b70245e1d9",
- "chunkId": "90e1f3df-a8a1-438e-8dd5-10e45398d051",
- "verdict": "util"
}{- "searchId": "9b1a67f9-9477-48e5-8a6c-11b70245e1d9",
- "chunkId": "90e1f3df-a8a1-438e-8dd5-10e45398d051",
- "verdict": "util",
- "rank": 0
}浏览器已读取的文本内容(<input webkitdirectory>),绝不是宿主机路径——这一区别正是 ADR 0113 的全部要点。local 范围的幂等完全重建:每次调用都会删除并根据本次发送的文件重新创建,因此重新上传就是重新同步的机制。超过文件数/总字节数上限时拒绝(400),而不是悄悄截断——这是用户在文件夹选择器前的一次性操作,不是后台扫描。单个过大或扩展名无法识别的文件只会被跳过,绝不会让整批失败。
| projectId required | string |
| folderName required | string 在浏览器中选择的文件夹名称——显示在覆盖率面板中。 |
required | 数组:objects (LocalFolderFileDto) 最多 500 个文件。精确的字节总和上限(RAG_LOCAL_TOTAL_BYTES_LIMIT)和逐文件的扩展名允许列表由用例而不是此 DTO 强制执行——由各自有效的字符串组成的数组加起来仍可能是过大的载荷。 |
{- "folderName": "brabo",
- "files": [
- {
- "path": "src/domain/rag/chunking.ts",
- "content": "string"
}
]
}{- "folderName": "string",
- "filesIndexed": 0,
- "filesSkipped": 0,
- "chunksCreated": 0,
- "embedding": {
- "available": true,
- "embedded": 0,
- "skipped": 0,
- "reason": "string"
}
}来自 Chat RAG 面板的“立即重建索引”。根据当前状态删除并重建三个可信范围(RN-219)的分块——推送/会话关闭时没有自动监视器(决定记录在 ADR 0079/0080 中)。在会话很多的项目上可能需要一段时间:它对每个会话运行一次索引,每次都可能批量调用嵌入提供商。
| projectId required | string |
{- "docs": {
- "filesScanned": 0,
- "docsChunks": 0,
- "adrChunks": 0,
- "truncated": true,
- "embedding": {
- "available": true,
- "embedded": 0,
- "skipped": 0,
- "reason": "string"
}
}, - "sessions": {
- "total": 0,
- "indexed": 0,
- "chunksCreated": 0
}, - "embeddingAvailable": true,
- "embeddingReason": "string"
}将余弦相似度(pgvector)与词法 ts_rank(tsvector)结合,二者来自独立的查询,通过加权求和合并并按阈值截断(ADR 0080)。当嵌入提供商没有响应、搜索只用词法信号运行时,vectorAvailable: false 会发出提示——它从不假装运行了完整的混合搜索。
| projectId required | string |
| query required | string 2 到 500 个字符之间——与 Code 标签页搜索的规则相同。 |
| scopes | 数组:strings 项 枚举: "docs" "adr" "session" "local" 缺省时搜索全部四个范围(RN-219/454)。 |
| limit | number 1 到 10——缺省时使用上限。 |
{- "query": "how does the PR gate work",
- "scopes": [
- "docs"
], - "limit": 10
}{- "query": "string",
- "searchId": "9b1a67f9-9477-48e5-8a6c-11b70245e1d9",
- "hits": [
- {
- "chunkId": "string",
- "scope": "docs",
- "content": "string",
- "score": 0,
- "vectorScore": 0,
- "lexicalScore": 0,
- "origin": { }
}
], - "vectorAvailable": true,
- "vectorUnavailableReason": "string"
}每个条目都带有 technical:仅当会话由仓库置备打开时为 true(从 repo_bootstraps 关联)。
| projectId required | string |
[- {
- "id": "01JC4Z8QK3M7YV2N5T9B0PXHRA",
- "projectId": "01JC4Z0000PROJETO0000000001",
- "createdBy": "01JC4Z0000USUARIO0000000001",
- "status": "active",
- "kind": "criativa",
- "name": "Cart checkout",
- "nextSeq": 42,
- "createdAt": "2026-07-27T14:03:22.187Z",
- "updatedAt": "2026-07-27T14:31:09.004Z",
- "closedAt": null,
- "terminationReason": null,
- "traceParent": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01",
- "technical": false
}
]会话以 created 状态诞生,是代理所做一切的容器:事件日志、提议的操作和 token 预算都挂在它上面。kind 是必需的并会被记录:consultiva(咨询式)只对话,criativa(创作式)产出内容,并且是唯一接受 execution.activated 的。
| projectId required | string |
| kind required | string 枚举: "consultiva" "criativa" 会话的意图。 |
| name | string <= 80 characters 友好名称,可选。不会取代 id 的井号标签——界面会把两者组合起来。空白视为缺省。 |
{- "kind": "criativa",
- "name": "Cart checkout"
}{- "id": "01JC4Z8QK3M7YV2N5T9B0PXHRA",
- "projectId": "01JC4Z0000PROJETO0000000001",
- "createdBy": "01JC4Z0000USUARIO0000000001",
- "status": "active",
- "kind": "criativa",
- "name": "Cart checkout",
- "nextSeq": 42,
- "createdAt": "2026-07-27T14:03:22.187Z",
- "updatedAt": "2026-07-27T14:31:09.004Z",
- "closedAt": null,
- "terminationReason": null,
- "traceParent": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
}{- "id": "01JC4Z8QK3M7YV2N5T9B0PXHRA",
- "projectId": "01JC4Z0000PROJETO0000000001",
- "createdBy": "01JC4Z0000USUARIO0000000001",
- "status": "active",
- "kind": "criativa",
- "name": "Cart checkout",
- "nextSeq": 42,
- "createdAt": "2026-07-27T14:03:22.187Z",
- "updatedAt": "2026-07-27T14:31:09.004Z",
- "closedAt": null,
- "terminationReason": null,
- "traceParent": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
}名称是导航标签,不会取代 id 的井号标签——界面会把两者组合起来,没有名称时退化为只显示井号标签。null 或空白字符串会清除名称。无法修改 kind:它是创建时的意图,之后修改会把它变成状态。
| projectId required | string |
| sessionId required | string |
| name required | object or null <= 80 characters 新名称。 |
{- "name": "Cart checkout"
}{- "id": "01JC4Z8QK3M7YV2N5T9B0PXHRA",
- "projectId": "01JC4Z0000PROJETO0000000001",
- "createdBy": "01JC4Z0000USUARIO0000000001",
- "status": "active",
- "kind": "criativa",
- "name": "Cart checkout",
- "nextSeq": 42,
- "createdAt": "2026-07-27T14:03:22.187Z",
- "updatedAt": "2026-07-27T14:31:09.004Z",
- "closedAt": null,
- "terminationReason": null,
- "traceParent": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
}Human gesture, with no cap: the automatic resume happens once at boot (ADR 0226) and this is the button for the rest. The reply is the ACCEPT (the turn goes up in the engine); progress arrives on the session channel. Requires developer.
| projectId required | string |
| sessionId required | string |
| agent required | string |
{- "ok": true
}在整个会话日志上统计的业务规则、决策记录和每个代理的制品(界面读取的是 200 个事件的尾部),以及此会话和其已批准的 Dev Lead 计划所激活的执行会话中被 git_commit/git_push 触及的文件(RN-828)。自 RN-844 起,它还返回规则和决策的条目(各最新 50 条;总数仍在 regras/decisoes 中)以及这些会话中打开的 PR 的文件,读取自 PR 标签页所读的同一 diff,每次调用最多 20 个 PR;prsNaoLidas 说明有多少被略过或失败。只读;没有新表。
| projectId required | string |
| sessionId required | string |
{- "sessoesLidas": [
- "string"
], - "regras": 11,
- "decisoes": 6,
- "itensDeRegras": [
- {
- "eventId": "evt_01JC4Z",
- "seq": 42,
- "actorId": "po",
- "titulo": "Login exige e-mail",
- "descricao": "string",
- "origens": 2
}
], - "itensDeDecisoes": [
- {
- "eventId": "evt_01JC4Z",
- "seq": 43,
- "actorId": "criativo",
- "escolha": "SQLite em arquivo",
- "contexto": "string"
}
], - "artefatosPorAgente": [
- {
- "actorId": "po",
- "quantos": 7
}
], - "arquivosTocados": {
- "itens": [
- {
- "actionId": "01JC4Z0000ACAO00000000000001",
- "path": "src/app.ts",
- "additions": 12,
- "deletions": 3
}
], - "total": 140,
- "prsNaoLidas": 0
}
}日志不可变,按 seq 排序。常规分页是增量式的:把上一个响应的 nextCursor 作为 afterSeq 传入。latest=true 则相反——它获取尾部并忽略 afterSeq,供那些在末尾打开而不是从头重建整个会话的界面使用(ADR 0021)。
| projectId required | string |
| sessionId required | string |
| afterSeq | string 示例: afterSeq=40 返回 |
| limit | string 示例: limit=50 |
| latest | string 示例: latest=true 获取日志尾部;忽略 |
| actionId | string 示例: actionId=01JC4Z0000ACAO00000000000001 只返回 |
{- "items": [
- {
- "id": "01JC4Z8QK3M7YV2N5T9B0PXHRB",
- "sessionId": "01JC4Z8QK3M7YV2N5T9B0PXHRA",
- "seq": 41,
- "type": "chat.message",
- "actor": {
- "kind": "agent",
- "id": "dev-api"
}, - "payload": {
- "role": "assistant",
- "content": "I'll open the epic first."
}, - "createdAt": "2026-07-27T14:30:58.512Z"
}
], - "nextCursor": 41
}仅追加:seq 由服务器分配,日志中的任何内容之后都不会被更新或删除。按设计没有编辑端点。
| projectId required | string |
| sessionId required | string |
| type required | string 事件类型。完整清单见 |
required | object (ActorDto) |
required | object 自由形态,因 |
{- "type": "chat.message",
- "actor": {
- "kind": "agent",
- "id": "dev-api"
}, - "payload": {
- "role": "user",
- "content": "I want a new checkout."
}
}{- "id": "01JC4Z8QK3M7YV2N5T9B0PXHRB",
- "sessionId": "01JC4Z8QK3M7YV2N5T9B0PXHRA",
- "seq": 41,
- "type": "chat.message",
- "actor": {
- "kind": "agent",
- "id": "dev-api"
}, - "payload": {
- "role": "assistant",
- "content": "I'll open the epic first."
}, - "createdAt": "2026-07-27T14:30:58.512Z"
}它的存在是为了让 Psychologist 的假设证据无论落在哪一页、无论信息流的过滤条件如何,都能找到所引用的事件。
| projectId required | string |
| sessionId required | string |
| eventId required | string |
{- "id": "01JC4Z8QK3M7YV2N5T9B0PXHRB",
- "sessionId": "01JC4Z8QK3M7YV2N5T9B0PXHRA",
- "seq": 41,
- "type": "chat.message",
- "actor": {
- "kind": "agent",
- "id": "dev-api"
}, - "payload": {
- "role": "assistant",
- "content": "I'll open the epic first."
}, - "createdAt": "2026-07-27T14:30:58.512Z"
}只有 closed/closed_abnormally 可以重新打开,并且只能回到 active——closing 绝不回退(ADR 0183)。会话保留其事件日志、制品、已回答的问题和 handoff;kind 不变。一个新事件 session.reopened 记录之前的 closedAt 和 terminationReason,记录中的这两列会被清空。携带 execution.activated 的会话会被拒绝(sessao_com_execucao):请打开新会话并在那里激活执行。没有时间限制。需要 developer(ADR 0184)。
| projectId required | string |
| sessionId required | string |
{- "id": "01JC4Z8QK3M7YV2N5T9B0PXHRA",
- "projectId": "01JC4Z0000PROJETO0000000001",
- "createdBy": "01JC4Z0000USUARIO0000000001",
- "status": "active",
- "kind": "criativa",
- "name": "Cart checkout",
- "nextSeq": 42,
- "createdAt": "2026-07-27T14:03:22.187Z",
- "updatedAt": "2026-07-27T14:31:09.004Z",
- "closedAt": null,
- "terminationReason": null,
- "traceParent": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
}为调用方解析,绝不为会话解析:会话覆盖 > 账户选择 > 检测并确认的 > 界面语言(RN-618)。返回胜出者、其来源以及链条的每一环——并且当你最近的消息指向另一种语言、界面应当就此询问时,返回 detectionQuestion(RN-624)。
| projectId required | string |
| sessionId required | string |
{- "language": "pt-BR",
- "origin": "interface",
- "sessionOverride": null,
- "account": "automatico",
- "detected": null,
- "interfaceLocale": "pt-BR",
- "detectionQuestion": "es"
}只针对调用方,只在此会话中(RN-618)——其他参与者和你的账户选择不受影响。null 恢复为从账户继承。
| projectId required | string |
| sessionId required | string |
| language required | string or null <= 35 characters 代理仅在此会话中回答你所使用的语言(RN-618)——其他参与者绝不受影响。服务器能识别的任何 BCP-47 代码,以规范形式存储。发送 |
{- "language": "en"
}{- "language": "pt-BR",
- "origin": "interface",
- "sessionOverride": null,
- "account": "automatico",
- "detected": null,
- "interfaceLocale": "pt-BR",
- "detectionQuestion": "es"
}该票据用于 engine 上会话 Phoenix 套接字 session:<id> 的 connect/3 认证——不是复用的 JWT。TTL 30 秒,一次性使用:每次重连(包括自动重连)都会请求新票据。scope: "heartbeat" 需要 viewer 角色;scope: "terminal" 需要 developer,与终端操作的最低角色相同。
| projectId required | string |
| sessionId required | string |
| scope required | string 枚举: "heartbeat" "terminal"
|
{- "scope": "heartbeat"
}{- "ticket": "string",
- "expiresAt": "2026-08-09T12:00:30.000Z"
}状态转换由领域的状态机校验,而不是由客户端:created → active → closing → closed | closed_abnormally。无效的跳转返回 409。
| projectId required | string |
| sessionId required | string |
| status required | string 枚举: "created" "active" "closing" "closed" "closed_abnormally" 目标状态。状态机会以 409 拒绝无效的跳转——从 |
{- "status": "closing"
}{- "id": "01JC4Z8QK3M7YV2N5T9B0PXHRA",
- "projectId": "01JC4Z0000PROJETO0000000001",
- "createdBy": "01JC4Z0000USUARIO0000000001",
- "status": "active",
- "kind": "criativa",
- "name": "Cart checkout",
- "nextSeq": 42,
- "createdAt": "2026-07-27T14:03:22.187Z",
- "updatedAt": "2026-07-27T14:31:09.004Z",
- "closedAt": null,
- "terminationReason": null,
- "traceParent": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
}响应只是一个确认,并在接受时返回——在代理的回合结束之前(ADR 0163)。代理的回复通过会话的事件日志和通道到达——而不是通过这次调用。如果代理正处于回合中,消息不会被拒绝:它会进入代理的队列(entrega: "enfileirada"),在当前回合结束时与其他排队的消息在一个回合中一起被读取;等待期间可以取消(RN-673,ADR 0191)。
| projectId required | string |
| sessionId required | string |
| agent required | string 示例: po 读取该消息的对话式代理的 slug:criativo、po、arquiteto、dev-lead、ux-designer 或 staff。任何其他 slug——包括 infra——都会以 422 拒绝;绝不会投递给默认代理(RN-584)。 |
| text required | string [ 1 .. 10000 ] characters 用户发给会话活跃代理的消息。 |
{- "text": "I want to allow discount coupons at checkout."
}{- "ok": true,
- "mensagemId": "01JC4Z0000EVENTO000000000001",
- "entrega": "enfileirada",
- "posicao": 2
}在代理处于回合中时发送的消息会在队列中等待,并在回合结束时被读取(RN-673)。等待期间,发送者可以取消它:engine 将其从队列中移除并记录 chat.message_cancelled;它绝不会展示给模型。取消别人的消息返回 403。
| projectId required | string |
| sessionId required | string |
| agent required | string 示例: po 队列中持有该消息的代理的 slug。 |
| messageId required | string 示例: 01JC4Z0000EVENTO000000000001
|
{- "ok": true
}记录 chat.structured_question_answered,并把答案作为普通消息重新发送给代理。一组问题只能回答一次。在接受时返回,在代理的回合结束之前(ADR 0163):回合继续在 engine 中运行,其叙述、结束和失败通过会话通道和事件日志到达(agent.status、agent.response、agent.error)——绝不通过这个响应。
| projectId required | string |
| sessionId required | string |
| agent required | string 示例: criativo 提出问题(并将读取答案)的代理的 slug。 |
| questionSetId required | string 示例: 01JC4Z0000EVENTO000000000001 已回答的 |
required | object 以问题 | ||
| |||
{- "answers": {
- "name": "Easy Checkout",
- "users": "Small business owners",
- "platform": "Web"
}
}{- "ok": true
}专用端点,而不是复用属于 Criativo 的 readiness:它们是会话中两个不同的里程碑,混为一谈会让事件日志含糊不清。在接受时返回,在代理的回合结束之前(ADR 0163):回合继续在 engine 中运行,其叙述、结束和失败通过会话通道和事件日志到达(agent.status、agent.response、agent.error)——绝不通过这个响应。到 Dev Lead 的 handoff 仍在到 Infra 的之后:engine 会一直持有它,直到收尾回合结束。幂等(ADR 0182,RN-635):已有待处理提议或已在项目中活跃的目标不会被再次触发,两者都如此时不记录任何内容。
| projectId required | string |
| sessionId required | string |
{- "ok": true,
- "desfecho": "confirmado",
- "jaAtendidos": [
- {
- "toAgent": "infra",
- "motivo": "oferta_pendente"
}
]
}记录 necessity.validated。要求 Criativo 已在此会话中整合出一份 product_brief(RN-406)——没有它会被拒绝:没有可验证的内容。自 ADR 0185(RN-657)起,“我准备好了——需求已验证”的点击(POST .../readiness)会自行记录此事件,Web 端不再调用此路由;保留它是为了那些就绪点击早于该 ADR 的会话。
| projectId required | string |
| sessionId required | string |
{- "ok": true
}| projectId required | string |
| sessionId required | string |
[- {
- "id": "01JC4Z0000HANDOFF00000000001",
- "sessionId": "01JC4Z8QK3M7YV2N5T9B0PXHRA",
- "projectId": "01JC4Z0000PROJETO0000000001",
- "fromAgent": "criativo",
- "toAgent": "po",
- "artifactId": "01JC4Z0000ARTEFATO000000001",
- "status": "offered",
- "createdAt": "2026-07-24T10:00:00.000Z",
- "updatedAt": "2026-07-24T10:05:00.000Z"
}
]以 offered 状态诞生,与代理自己的 offer_handoff 完全一样——唯一的区别是由谁决定。toAgent 必须在可寻址目录中(区域 lead 或没有区域的代理);子代理或未知 slug 会以 400 拒绝。每个(项目,目标)最多一个待处理提议:见 desfecho(ADR 0182,RN-635)。
| projectId required | string |
| sessionId required | string |
| toAgent required | string 目标代理的 slug。必须是 lead 或没有区域的代理——绝不能是子代理。 |
{- "toAgent": "staff"
}{- "id": "01JC4Z0000HANDOFF00000000001",
- "sessionId": "01JC4Z8QK3M7YV2N5T9B0PXHRA",
- "projectId": "01JC4Z0000PROJETO0000000001",
- "fromAgent": "criativo",
- "toAgent": "po",
- "artifactId": "01JC4Z0000ARTEFATO000000001",
- "status": "offered",
- "createdAt": "2026-07-24T10:00:00.000Z",
- "updatedAt": "2026-07-24T10:05:00.000Z",
- "desfecho": "criado"
}只做一件事:转换为 accepted 并启动目标代理。这是除 Criativo 之外每个代理的常规激活路径。
| projectId required | string |
| sessionId required | string |
| handoffId required | string |
{- "id": "01JC4Z0000HANDOFF00000000001",
- "sessionId": "01JC4Z8QK3M7YV2N5T9B0PXHRA",
- "projectId": "01JC4Z0000PROJETO0000000001",
- "fromAgent": "criativo",
- "toAgent": "po",
- "artifactId": "01JC4Z0000ARTEFATO000000001",
- "status": "offered",
- "createdAt": "2026-07-24T10:00:00.000Z",
- "updatedAt": "2026-07-24T10:05:00.000Z"
}这是触发 product_brief 和到 PO 的 handoff 的按钮。在事件日志中记录 readiness.confirmed。在接受时返回,在代理的回合结束之前(ADR 0163):回合继续在 engine 中运行,其叙述、结束和失败通过会话通道和事件日志到达(agent.status、agent.response、agent.error)——绝不通过这个响应。
| projectId required | string |
| sessionId required | string |
{- "ok": true
}Authenticated by a MACHINE device key (never a session JWT, never a project-bound credential). Same envelope encryption as POST /users/me/credentials. 409 credencial_existente when the user already has this provider and substituir is not true. The key is never logged nor returned.
| provider required | string 枚举: "anthropic" "openai" "openrouter" "nvidia-nim" "together" "deepinfra" "bitdeer" "vultr" "9router" "opencode-zen" |
| apiKey required | string <= 512 characters The API key in plain text. Encrypted before touching the database; NEVER logged and NEVER returned. |
| origem required | string <= 200 characters Tool and path the key was read from. Audit log only; never a value. |
| substituir | boolean Must be |
{- "provider": "anthropic",
- "apiKey": "string",
- "origem": "codex: ~/.codex/auth.json",
- "substituir": true
}{- "provider": "anthropic",
- "substituida": true
}按(用户,提供商)upsert。密钥在接触数据库之前通过信封加密进行加密,响应只携带投影——绝不包含机密,即使是加密后的也不包含。
| provider required | string 枚举: "anthropic" "openai" "openrouter" "nvidia-nim" "together" "deepinfra" "bitdeer" "vultr" "9router" "opencode-zen" |
| apiKey required | string <= 512 characters 明文密钥。它在接触数据库之前通过信封加密进行加密,绝不会出现在任何响应中——即使是加密后的也不会。512 个字符的上限是防止荒谬载荷的保护,而不是格式校验:密钥是否有效由提供商在 |
{- "provider": "anthropic",
- "apiKey": "sk-ant-api03-…"
}{- "id": "01JC4Z0000CREDENCIAL00000001",
- "provider": "anthropic",
- "createdAt": "2026-07-10T10:00:00.000Z",
- "updatedAt": "2026-07-10T10:00:00.000Z"
}检查是一个显式操作,与登记分开(ADR 0050):api 解密机密、调用提供商,只返回裁决——密钥绝不会返回,哪怕是一部分。三种结果都返回 200,因为请求已被处理;refused 是一种结果,不是协议错误。当(用户,提供商)没有凭据时返回 404——那时没有可测试的东西。
| provider required | string 示例: openrouter |
{- "resultado": "recusado",
- "motivo": "connection test failed for openrouter: openrouter responded 401"
}使用自己的路径,而不是 users/me/credentials,因为请求体不同(token,且提供商枚举为 github/gitlab)。令牌经过加密并在未事先验证的情况下存储(ADR 0050);如需验证,请调用 POST /users/me/credentials/{provider}/test。读取、移除和测试复用 users/me/credentials 路由——它们是同一张表。
| provider required | string 枚举: "github" "gitlab" |
| token required | string <= 512 characters 个人访问令牌。在接触数据库之前通过信封加密进行加密,绝不返回,即使是加密后的也不返回。登记时不会测试令牌(ADR 0050)——如需验证,请使用 |
{- "provider": "github",
- "token": "ghp_…"
}{- "id": "01JC4Z0000CREDENCIAL00000001",
- "provider": "anthropic",
- "createdAt": "2026-07-10T10:00:00.000Z",
- "updatedAt": "2026-07-10T10:00:00.000Z"
}账户级别,路径中没有项目(RN-611):一行命令安装会在任何项目存在之前创建机器密钥,而其他所有列表都是按项目的。只返回机器密钥(especie: "maquina",projectId: null),且只返回调用方的——项目密钥保留在其项目的列表中,任何人都看不到其他用户的密钥(RN-519)。已撤销的密钥也包含在内:登记新的机器密钥会撤销上一把(RN-552),而这个列表就是体现这一点的地方。lastUsedAt 是一次记录的使用,绝不是活动连接;为空表示该密钥从未被使用。
[- {
- "id": "01JC4Z0000CHAVE000000000001",
- "name": "laptop",
- "projectId": "01JC4Z0000PROJETO000000001",
- "especie": "projeto",
- "createdAt": "2026-08-27T12:00:00.000Z",
- "revokedAt": null,
- "lastUsedAt": null
}
]幂等——再次撤销不是错误。与 DELETE /projects/{projectId}/runner-device-keys/{deviceKeyId} 是同一种撤销:它还会断开用此密钥打开的活动连接,遍及所有项目(RN-520/RN-543)。自 RN-685(ADR 0201)起,目标是密钥本身:同一用户用 PAT 或其他密钥连接的另一个 runner 不受影响。该密钥尚未使用的票据会被作废。还没有项目时,就没有可断开的连接。项目密钥、不存在的密钥和其他用户的密钥都返回同样的 404。
| deviceKeyId required | string |
两个字段都是可选且相互独立的——发送其中一个绝不会影响另一个,两者都没有的请求体返回 400。locale(界面)仍限定在 pt-BR/en 列表中。responseLanguage(RN-618)为 automatico 或任何可识别的 BCP-47 代码,以规范形式存储;无法识别的代码返回 400,请求体中的任何内容都不会被写入。
| locale | string 枚举: "pt-BR" "en" "ru" "zh-Hans" 界面语言。限定在列表中——不是自由形式的 BCP-47。 |
| responseLanguage | string <= 35 characters 代理回答所用的语言(RN-618): |
{- "locale": "en",
- "responseLanguage": "es"
}{- "locale": "pt-BR",
- "responseLanguage": "automatico",
- "detectedLanguage": null,
- "detectedLanguageConfirmedAt": "2019-08-24T14:15:22Z",
- "effectiveResponseLanguage": {
- "language": "pt-BR",
- "origin": "interface"
}
}检测到的语言成为偏好的唯一途径(RN-624):仅凭检测绝不会改变任何东西。confirm 写入 detectedLanguage + detectedLanguageConfirmedAt;decline 记录拒绝,以免再次询问同一种语言。返回回答之后的偏好设置。
| language required | string <= 35 characters 问题所涉及的语言——即 |
| answer required | string 枚举: "confirm" "decline"
|
{- "language": "es",
- "answer": "confirm"
}{- "locale": "pt-BR",
- "responseLanguage": "automatico",
- "detectedLanguage": null,
- "detectedLanguageConfirmedAt": "2019-08-24T14:15:22Z",
- "effectiveResponseLanguage": {
- "language": "pt-BR",
- "origin": "interface"
}
}[- {
- "workspace": {
- "id": "01JC4Z0000WORKSPACE00000001",
- "name": "Acme Corp",
- "slug": "acme-corp",
- "createdBy": "01JC4Z0000USUARIO0000000001",
- "toolRouterEnabled": true,
- "createdAt": "2026-07-20T09:12:00.000Z",
- "updatedAt": "2026-07-20T09:12:00.000Z"
}, - "role": "maintainer"
}
]不需要任何角色:创建者成为 owner。这是尚不属于任何 workspace 的人的唯一入口。
| name required | string >= 2 characters |
| slug required | string^[a-z0-9]+(-[a-z0-9]+)*$ kebab-case。在系统中唯一,并在 URL 中可见。 |
{- "name": "Acme Corp",
- "slug": "acme-corp"
}{- "id": "01JC4Z0000WORKSPACE00000001",
- "name": "Acme Corp",
- "slug": "acme-corp",
- "createdBy": "01JC4Z0000USUARIO0000000001",
- "toolRouterEnabled": true,
- "createdAt": "2026-07-20T09:12:00.000Z",
- "updatedAt": "2026-07-20T09:12:00.000Z"
}{- "id": "01JC4Z0000WORKSPACE00000001",
- "name": "Acme Corp",
- "slug": "acme-corp",
- "createdBy": "01JC4Z0000USUARIO0000000001",
- "toolRouterEnabled": true,
- "createdAt": "2026-07-20T09:12:00.000Z",
- "updatedAt": "2026-07-20T09:12:00.000Z"
}| workspaceId required | string |
| name | string >= 2 characters |
| slug | string^[a-z0-9]+(-[a-z0-9]+)*$ kebab-case。在系统中唯一,并在 URL 中可见。 |
{- "name": "Acme Corp",
- "slug": "acme-corp"
}{- "id": "01JC4Z0000WORKSPACE00000001",
- "name": "Acme Corp",
- "slug": "acme-corp",
- "createdBy": "01JC4Z0000USUARIO0000000001",
- "toolRouterEnabled": true,
- "createdAt": "2026-07-20T09:12:00.000Z",
- "updatedAt": "2026-07-20T09:12:00.000Z"
}需要 owner。会连同其项目、会话及其历史一起删除。
| workspaceId required | string |
{- "id": "01JC4Z0000WORKSPACE00000001",
- "name": "Acme Corp",
- "slug": "acme-corp",
- "createdBy": "01JC4Z0000USUARIO0000000001",
- "toolRouterEnabled": true,
- "createdAt": "2026-07-20T09:12:00.000Z",
- "updatedAt": "2026-07-20T09:12:00.000Z"
}谁与该 workspace 关联,以及其 workspace 角色——除非项目记录覆盖它,每个项目都会继承这个角色。补充 GET projects/:projectId/members,后者只列出项目记录:仅通过 workspace 进入项目的人出现在这里而不是那里。只包含 id、姓名、邮箱和角色。
| workspaceId required | string |
[- {
- "userId": "01JC4Z0000USUARIO0000000002",
- "role": "maintainer",
- "name": "Senior Dev",
- "email": "dev@brabo.dev"
}
]只有 owner 可以改动成员名单。这里的角色会被该 workspace 的所有项目继承。这个角色是必要的但不是充分的:在这里修改你自己的角色会以 403 拒绝,两个方向都是,且在任何地方都无法启用。上面没有更高层级来兜底,因此自我降级将无法通过界面恢复。降级另一个 owner 仍然允许——这是撤销所有权的唯一方式。
| workspaceId required | string |
| userId required | string <uuid> 要关联的用户 id。 |
| role required | string 枚举: "viewer" "developer" "maintainer" "owner" 此关联中的角色。在项目上,它在两个方向上都覆盖 workspace 角色——只要关联存在,有效角色就是这个,无论更高还是更低——并且有两种变动会以 403 拒绝:降级 workspace |
{- "userId": "3f1b2c8e-5a4d-4b7e-9c10-2d6f8a1b4c33",
- "role": "developer"
}{- "workspaceId": "01JC4Z0000WORKSPACE00000001",
- "userId": "01JC4Z0000USUARIO0000000002",
- "role": "maintainer",
- "createdAt": "2026-07-22T08:00:00.000Z"
}只有 owner 可以调用,与 upsert 相同。移除 workspace 记录,并在同一事务中移除该用户在此 workspace 每个项目中的记录(项目角色覆盖 workspace 角色,保留它们会让该用户留在那些项目中),以及该用户在这些项目中的项目设备密钥和个人访问令牌。之后会尽力断开该用户在每个项目中的活动 runner 连接。机器设备密钥和已打开的会话不受影响——由于角色解析为无,它们不再能触及此 workspace。
移除你自己会以 403 拒绝,且在任何地方都无法启用:上面没有更高层级来兜底。正是这一条款保护了最后一个 owner——只有 owner 可以调用此路由且没有人能移除自己,因此每次成功调用后至少还有调用方是 owner。移除另一个 owner 是允许的——这是撤销所有权的方式——但记录所有者(createdBy)除外,它会以 409 criador_do_workspace 拒绝,直到 PUT :workspaceId/owner-of-record 把它转给另一个所有者:代理花费的 LLM 密钥和花费报告都是他们的。幂等:没有 workspace 记录的目标仍会清除其在此 workspace 中的项目记录,并返回 204。
| workspaceId required | string |
| userId required | string |
记录所有者是 createdBy:代理花费的是谁的 LLM 凭据、代理操作使用的是谁的 git 凭据(RN-058),以及花费报告归谁所有(RN-060)。它不授予授权——授权由角色决定。目标必须已经是该 workspace 的 owner(否则返回 409 titular_precisa_ser_owner)。从提交那一刻起,代理回合会查找新记录所有者的凭据;如果他们没有该模型提供商的凭据,回合会以该提供商现有的“没有登记凭据”结果结束——此路由不会事先检查。任何 owner 都可以调用,包括为另一个所有者调用。转给当前的记录所有者是空操作。
| workspaceId required | string |
| userId required | string <uuid> 将成为记录所有者的用户 id。必须已经是此 workspace 的 |
{- "userId": "3f1b2c8e-5a4d-4b7e-9c10-2d6f8a1b4c33"
}{- "id": "01JC4Z0000WORKSPACE00000001",
- "name": "Acme Corp",
- "slug": "acme-corp",
- "createdBy": "01JC4Z0000USUARIO0000000001",
- "toolRouterEnabled": true,
- "createdAt": "2026-07-20T09:12:00.000Z",
- "updatedAt": "2026-07-20T09:12:00.000Z"
}Mounted 模式的文件夹选择器,由 api 而不是 runner 提供(RN-504)。严格限定在 BRABO_PROJECTS_BASE 内(ADR 0141):path 是可选的,默认为基目录本身,任何在其之外的 path 都返回 400——这是格式错误的请求,而不是权限问题,因为没有任何角色能浏览基目录之外的内容。
刻意设限:只列目录,最多 500 个(在截断之前排序,并用 truncado 说明),绝不递归,排除以点开头的条目,符号链接只报告而绝不跟随。被略去的内容会被计数(arquivos、simbolicos),这样满是代码的文件夹绝不会看起来是空的。
没有配套的 POST:创建文件夹属于物化挂载 workspace 的过程,绝不属于选择器。
| path | string 要列出的绝对路径。省略即基目录本身。必须位于 |
{- "base": "/home/voce/brabo",
- "path": "/home/voce/brabo/clientes",
- "entries": [
- "api",
- "loja",
- "website"
], - "truncado": false,
- "arquivos": 12,
- "simbolicos": 1
}[- {
- "id": "01JC4Z0000PROJETO0000000001",
- "workspaceId": "01JC4Z0000WORKSPACE00000001",
- "name": "Checkout",
- "slug": "checkout",
- "workspaceDirName": "checkout-3f2b1c8e",
- "executionMode": "container",
- "workspacePath": null,
- "workspaceVerifiedAt": null,
- "mirrorPath": null,
- "language": "pt-BR",
- "createdBy": "01JC4Z0000USUARIO0000000001",
- "taskBudgetMicros": 500000,
- "maxConsecutiveBlocked": 3,
- "storyPromotion": "auto",
- "createdAt": "2026-07-21T11:00:00.000Z",
- "updatedAt": "2026-07-21T11:00:00.000Z"
}
]| workspaceId required | string |
| name required | string >= 2 characters |
| slug required | string^[a-z0-9]+(-[a-z0-9]+)*$ kebab-case。在 workspace 内唯一。 |
| maxConsecutiveBlocked | number 熔断器(Phase 12b——RN-047):连续多少个任务以阻塞结束会让该模块的 dev agent 停在 idle_tripped。省略时使用领域默认值(DEFAULT_MAX_CONSECUTIVE_BLOCKED)。从下一次激活执行起生效——不影响已在运行的代理。 |
| storyPromotion | string 枚举: "manual" "auto" 谁把 story 从 |
| executionMode | string 枚举: "container" "mounted" "runner" 此项目的命令在哪里执行(RN-169/RN-421——ADR 0072/0104)。 |
| workspacePath | string 文件夹的绝对路径,当 |
| language | string <= 35 characters 项目的语言(RN-619)——共享制品和没有人类作者的回合使用它。服务器能识别的任何 BCP-47 代码,以规范形式存储;无法识别的返回 400。省略时,项目以其创建者的有效回复语言开始(RN-618:账户选择 > 已确认的检测 > 界面语言)。 |
{- "name": "Checkout",
- "slug": "checkout",
- "maxConsecutiveBlocked": 3,
- "storyPromotion": "auto",
- "executionMode": "container",
- "workspacePath": "/home/you/projects/store",
- "language": "pt-BR"
}{- "id": "01JC4Z0000PROJETO0000000001",
- "workspaceId": "01JC4Z0000WORKSPACE00000001",
- "name": "Checkout",
- "slug": "checkout",
- "workspaceDirName": "checkout-3f2b1c8e",
- "executionMode": "container",
- "workspacePath": null,
- "workspaceVerifiedAt": null,
- "mirrorPath": null,
- "language": "pt-BR",
- "createdBy": "01JC4Z0000USUARIO0000000001",
- "taskBudgetMicros": 500000,
- "maxConsecutiveBlocked": 3,
- "storyPromotion": "auto",
- "createdAt": "2026-07-21T11:00:00.000Z",
- "updatedAt": "2026-07-21T11:00:00.000Z"
}运营者机器上 api 和 engine 容器都能看到的那个唯一文件夹,按身份挂载(ADR 0141)。null——一种正常状态,绝不是错误——表示此安装没有 BRABO_PROJECTS_BASE,因此项目向导完全不能提供 Mounted 模式。每个 workspace 的值都相同:它是安装配置,workspaceId 只用于限定授权范围。
brokerConfigurado 回答同一问题的另一半:container 和 mounted 只通过 broker 启动容器(ADR 0144),因此没有它时向导不会预选 Mounted,而是提供 Runner(ADR 0161,RN-573)。
{- "projectsBase": "/home/voce/brabo",
- "brokerConfigurado": false
}workspace 中每个项目一行,包含 git 提供商、置备状态、预算、最近活动、等待提升的 story,以及决定代理名单的事实(RN-090)。
与 projects-status 同样的思路,只是高了一层:卡片网格是一次 workspace 读取,而不是 N 次项目读取。之前的做法每张卡片要发起七个轮询查询,有 23 个项目时仅仪表盘就突破了 300 次/分钟的速率限制。
| workspaceId required | string |
[- {
- "projectId": "01JC4Z0000PROJETO0000000001",
- "provider": "github",
- "provisioningStatus": "provisioned",
- "budget": {
- "limitMicros": 50000000,
- "spentMicros": 12500000
}, - "latestSessionId": "01JC4Z0000SESSAO00000000001",
- "latestSeq": 41,
- "lastEvent": {
- "id": "01JC4Z8QK3M7YV2N5T9B0PXHRB",
- "sessionId": "01JC4Z8QK3M7YV2N5T9B0PXHRA",
- "seq": 41,
- "type": "chat.message",
- "actor": {
- "kind": "agent",
- "id": "dev-api"
}, - "payload": {
- "role": "assistant",
- "content": "I'll open the epic first."
}, - "createdAt": "2026-07-27T14:30:58.512Z"
}, - "storiesAwaitingPromotion": 2,
- "pendingApprovalsCount": 8,
- "onlineAgentCount": 2,
- "roster": {
- "executionActivated": true,
- "moduleNames": [
- "api",
- "web"
], - "gatesEverOpened": true,
- "delegatedSubagents": [
- "qa-automacao"
], - "activatedAgents": [
- "arquiteto",
- "infra"
], - "infraActive": false,
- "uxDesignerActive": false,
- "staffActive": false
}
}
]默认开启。开启且回合模型来自 OpenRouter 时,拥有两个或更多工具的代理步骤会先询问 Jev 哪个工具合适,聊天模型只会被提供该工具和上一个工具。Jev 从不批准或拒绝任何东西:需要批准的操作仍然需要批准。Jev 的任何失败都会回落到整个目录。关闭它会停止对 Jev 的所有调用,其成本也随之停止。
| workspaceId required | string |
| enabled required | boolean 是否由 Jev 为代理的每一步选择工具。默认开启;仅当回合模型来自 OpenRouter 时才起作用。 |
{- "enabled": false
}{- "id": "01JC4Z0000WORKSPACE00000001",
- "name": "Acme Corp",
- "slug": "acme-corp",
- "createdBy": "01JC4Z0000USUARIO0000000001",
- "toolRouterEnabled": true,
- "createdAt": "2026-07-20T09:12:00.000Z",
- "updatedAt": "2026-07-20T09:12:00.000Z"
}铃铛的抽屉,面向整个 workspace(RN-091)。每个游标说明该项目已读到哪个 seq,响应携带每个项目最近会话中在此之后的内容。
之所以用 POST,是因为它是唯一带请求体的动词,而不是因为它改变状态。 服务器不知道每个读者停在哪里:“读到这里”是存储在浏览器中的每个项目一个 seq,并且(刻意地)没有标记为已读的端点。因此截止点需要随请求传递,而这样的对有几十个——放在查询字符串中会变成很长的 URL,代理会截断它,还会把用户的项目 id 写进访问日志。该路由是幂等的且没有副作用:它返回 200,绝不返回 201。
取代了抽屉打开时每个项目一次的请求——23 个项目要花 286 次/分钟,而限制是 300。
| workspaceId required | string |
required | 数组:objects (UnreadCursorDto) <= 200 items 每个要查询的项目一个游标。空列表返回空列表——“我什么都没要”不等于“全都给我”。来自其他 workspace 的项目会被静默忽略,因为游标来自调用方的本地存储,可能带有旧 workspace 的残留。 | ||||
Array (<= 200 items)
| |||||
{- "cursors": [
- {
- "projectId": "01JC4Z0000PROJETO0000000001",
- "afterSeq": 41
}
]
}[- {
- "projectId": "01JC4Z0000PROJETO0000000001",
- "sessionId": "01JC4Z0000SESSAO00000000001",
- "events": [
- {
- "id": "01JC4Z8QK3M7YV2N5T9B0PXHRB",
- "sessionId": "01JC4Z8QK3M7YV2N5T9B0PXHRA",
- "seq": 41,
- "type": "chat.message",
- "actor": {
- "kind": "agent",
- "id": "dev-api"
}, - "payload": {
- "role": "assistant",
- "content": "I'll open the epic first."
}, - "createdAt": "2026-07-27T14:30:58.512Z"
}
]
}
]