运行记录
查询文档运行的历史记录。
源码: apps/backend/src/routes/runs.ts
selectAuth 在凭据缺少或无效时返回 401,在账户准入、Token 路由权限或 OAuth scope 不满足时返回 403;OAuth scope 拒绝的错误为 insufficient_scope。这些拒绝响应会在业务处理前直接返回。
认证通过后,各端点继续执行原有的认证方式限制、项目权限与运行归属校验。下文列出各端点支持的身份与业务错误;本规则不扩大 OAuth scope 或运行记录访问范围。
GET /runs
查询项目下的运行记录列表。
认证方式
JWT Token 或 OAuth Token(selectAuth)。OAuth 需要 runs:read scope,且只返回当前授权用户通过当前 App 触发、source_type=oauth 的运行;同一 App 的其他授权用户也不可见。JWT 保持项目级视图。
请求参数
Query 参数
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
projectId | string | 是 | - | 项目 UUID |
documentId | string | 否 | - | 文档 UUID,按文档筛选 |
deploymentId | string | 否 | - | 部署 UUID,按部署筛选 |
sourceType | string | 否 | - | 来源类型:editor、api_key、schedule、oauth、webhook、connector |
page | number | 否 | 1 | 页码(最小 1) |
pageSize | number | 否 | 20 | 每页数量(最大 100) |
响应格式
状态码: 200。下例为 JWT 项目视图;OAuth 响应只包含对第三方调用方开放的运行字段,不返回内部执行快照(如 envelope、contentSnapshot、toolManifest、flowDocuments)。
{
"runs": [
{
"id": "uuid",
"owner_user_id": "uuid",
"project_id": "uuid",
"document_id": "uuid",
"deployment_id": "uuid",
"source_type": "editor",
"triggered_by_user_id": "uuid",
"api_key_id": null,
"status": "success",
"data": {
"inputs": {},
"globalCtx": [...],
"duration": 1234,
"completedBlocks": 3,
"timedOut": false
},
"created_at": "2026-03-07T12:00:00.000Z",
"updated_at": "2026-03-07T12:00:01.234Z",
"deleted_at": null,
"api_key": {
"team": { "name": "我的团队" }
},
"deployment": {
"version_major": 1,
"version_minor": 2
}
}
],
"total": 100,
"page": 1,
"pageSize": 20
}| 字段 | 类型 | 说明 |
|---|---|---|
runs | RunRecord[] | 运行记录数组;OAuth 仅返回当前用户与当前 App 的记录及下述安全字段 |
total | number | 总记录数 |
page | number | 当前页码 |
pageSize | number | 每页数量 |
记录按 created_at 降序排列。
OAuth 列表与详情使用同一安全字段集合:id、project_id、document_id、deployment_id、source_type、triggered_by_user_id、status、request_id、session_id、causation_id、parent_run_id、created_at、updated_at 、失败时的脱敏 error 和 data。其中 data 仅保留 duration、completedBlocks、timedOut、timeoutMs、cancelledReason;配置了 Output 的运行,详情接口另带最终结果 data.output = { endpointId, endpointName, value }(列表不带)。不返回原始 inputs、outputs、errorMessage、envelope、data.globalCtx、owner/worker 信息、OAuth 内部标识或关联对象;需要执行输出时使用运行接口响应。事件/追踪读取另受项目读权限约束,见下文。
请求示例
# 查询项目所有运行记录
curl -H "Authorization: Bearer YOUR_JWT" \
"https://block2-api.wainao.chat/runs?projectId=PROJECT_UUID"
# 按来源类型筛选
curl -H "Authorization: Bearer YOUR_JWT" \
"https://block2-api.wainao.chat/runs?projectId=PROJECT_UUID&sourceType=api_key&page=1&pageSize=10"错误码
| 状态码 | 错误 | 说明 |
|---|---|---|
| 400 | projectId is required | 缺少 projectId |
| 400 | Invalid sourceType: xxx | sourceType 无效 |
| 403 | 仅支持 JWT / OAuth 认证 | 使用了其他认证方式 |
| 403 | 无权访问该项目 | 非项目所有者且非团队成员 |
| 404 | 项目不存在 | 项目不存在或已删除 |
GET /runs/:runId
获取单条运行记录详情。
认证方式
JWT Token 或 OAuth Token(selectAuth)。OAuth 需要 runs:read scope,且 run 必须由当前授权用户通过当前 App 触发;历史未记录 App 归属的旧 OAuth run 不开放 OAuth 读取。JWT 保持项目级视图。
请求参数
Path 参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
runId | string | 是 | 运行记录 UUID |
Query 参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
trace | string | 否 | 传 skip 进入「骨架秒开」模式:后端不重建 globalCtx,并从响应中剥离旧 run 已持久化的 data.globalCtx(大 JSON),只返回基础字段。追踪明细改由 GET /runs/:runId/globalctx-events?format=timeline 按需拉取。不传时行为不变(向后兼容)。仅影响响应,不改数据库。 |
响应格式
状态码: 200
JWT 返回完整的运行记录对象。OAuth 返回与列表相同的安全字段集合,不包含内部执行快照(如 envelope、contentSnapshot、toolManifest、flowDocuments),也不返回下列关联信息。JWT 详情除关联的 api_key、deployment 外,还会组装:
triggered_by_user:用triggered_by_user_id二次查profiles得到的展示名(profiles外键已删除,无法 JOIN)。project.team:项目所属团队名,用于组织名兜底(editor/schedule/oauth 触发的 run 无api_key时使用)。
{
"id": "uuid",
"owner_user_id": "uuid",
"project_id": "uuid",
"document_id": "uuid",
"deployment_id": "uuid",
"source_type": "api_key",
"triggered_by_user_id": "uuid",
"api_key_id": "uuid",
"status": "success",
"data": {
"inputs": { "text": "hello" },
"globalCtx": [...],
"duration": 2345,
"completedBlocks": 5,
"timedOut": false
},
"created_at": "2026-03-07T12:00:00.000Z",
"triggered_by_user": { "display_name": "张三" },
"api_key": { "team": { "name": "我的团队" } },
"project": { "team": { "name": "我的团队" } },
"deployment": { "version_major": 1, "version_minor": 0 }
}上例为 JWT 项目视图。
trace=skip模式下,响应中的data不含globalCtx;OAuth 的安全视图不因省略trace=skip而返回内部执行快照。
错误码
| 状态码 | 错误 | 说明 |
|---|---|---|
| 403 | 仅支持 JWT / OAuth 认证 | 使用了其他认证方式 |
| 403 | 无权访问该运行记录 | 非项目所有者且非团队成员,或 OAuth App 归属不匹配 |
| 404 | 运行记录不存在 | 记录不存在或已删除 |
| 404 | 项目不存在 | 记录关联的项目不存在 |
补充端点
本节补充 plans/need-update-api.md 中尚未覆盖到 docs-site 的接口。端点标题保持严格的 METHOD /path 格式,便于后台文档覆盖率服务识别。
POST /runs
从编辑器创建一次工作流运行。
认证:编辑器 JWT Bearer Token,或 OAuth Token(flows:execute scope,项目 execute 权限仍按授权用户判定)。API Key 请使用 /api/released-app/:deploymentId/run。
请求:请求体为 JSON,主要字段如下:
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
documentId | string | 是 | 文档 UUID |
inputs | object | 否 | Input Block 的输入值,默认 {} |
executeMode | inline | worker | 否 | 默认 inline |
maxLifetimeMs | number | 否 | 本次 run 的最长生命周期覆盖值 |
requestId | string | 否 | OAuth 的调用方幂等键(≤128 字符);新版本受理且请求指纹可核验的相同请求返回原 runId/status 与 idempotentReplay: true |
sessionId | string | 否 | 外部系统会话归组键(≤128 字符) |
causationId | string | 否 | 外部系统因果链定位键(≤128 字符) |
parentRunId | string | 否 | 关联的父运行 UUID(必须已存在,否则 404 parent_run_not_found) |
后端会先取得协作网关的权威 checkpoint,检查活动变量引用,再按工作流实际消费的 Input 做必填与类型校验。正文/代码/条件提示词使用公开名称与稳定 mention 绑定;ValueSource、Structure/Output 等内部结构化引用继续使用 Block ID。输入值的 key 始终是 Input 的公开 name,裸内部 ID 不会作为正文变量名称兜底。输入校验失败返回 400:
{
"error": "输入参数验证失败",
"details": [{ "field": "customer", "message": "\"customer\" 是必填字段" }]
}校验失败时不会写入 runs、发布事件或启动执行器。未知 input key 保持宽容并被忽略。
活动引用无效时同样在受理前返回 400,响应为 { "error": "workflow_reference_invalid", "message": "...", "details": [...] }。details 项包含引用诊断 code/reference/path,适用时带 blockId/nearBlockId;例如内部 ID 别名是 internal-id-reference,绑定与名称冲突是 binding-name-mismatch。修正引用再提交;inline 与 worker 使用相同检查,历史异常文档仍可读取。
已验证调用方也可传 Idempotency-Key 请求头(≤200 字符),它优先于 body requestId。同一身份与键对应相同、由新版本受理并保存请求指纹的 run 时,断线重试返回 200 { "runId": "...", "status": "...", "idempotentReplay": true } 和 X-Wainao-Request-Id,不重复执行或扣费;同键但请求语义不同,或旧 run 缺少指纹无法核验时,返回 409 idempotency_conflict,不重新执行。OAuth 的恢复范围包含授权用户和 App;JWT 按其已验证身份隔离。
OAuth 列表、详情和事件均要求 run 的 oauth_app_id 精确匹配当前 App;历史未记录 App 归属的 run 不开放 OAuth 读取。
历史数据库唯一索引仍按 (oauth_app_id, request_id) 约束:同一 App 的不同用户若复用 body requestId,后来的创建可能返回 409。调用方应为每次新运行生成全局唯一的 requestId;断线重试沿用原值。
带请求键的草稿试跑在计算指纹前检查 inputs 和 flowDocuments 的结构深度与节点数;超限返回 400 input_structure_too_deep 或 400 input_structure_too_large,不会创建 run。
响应:成功时返回任务触发结果、运行状态或同步摘要;长任务可能只表示已入队。
常见错误:400 参数非法,401 未认证,403 权限不足,409 idempotency_conflict(同键不同请求),404 资源不存在,503 run_replay_lookup_unavailable(无法安全确认原运行);其他服务端或上游异常返回 500。
POST /runs/:runId/cancel
取消正在执行的运行。
认证:JWT / OAuth / API Key(selectAuth,按 run 归属判定;发布 run 与草稿 run 均适用)。 路径参数
| 参数 | 说明 |
|---|---|
runId | 运行记录 ID |
请求:请求体为 JSON,包含创建、提交、安装、发布或业务动作所需字段。
响应:成功时返回任务触发结果、运行状态或同步摘要;长任务可能只表示已入队。
常见错误:400 参数非法,401 未认证,403 权限不足,404 资源不存在;服务端或上游异常返回 500。
GET /runs/:runId/events
订阅指定运行的 SSE 事件;event 为事件类型,id 为断点续读 ID,data 保持原有 JSON 形态。
认证:JWT / OAuth / API Key(selectAuth,按 run 归属判定;发布 run 与草稿 run 均适用)。OAuth 读取事件还要求是运行所属项目 owner 或在该项目具有 read 权限;仅能调用公开 Flow 不等于能读取发布者的追踪。 路径参数
| 参数 | 说明 |
|---|---|
runId | 运行记录 ID |
请求:无请求体。可选 after=<Stream ID> 从该 ID 之后读取,格式为两段 1–20 位非负整数(例如 1700000000000-1);非法格式返回 400 invalid_event_cursor。未传时从 0-0 开始。
响应:text/event-stream,15 秒空闲时发送保活注释。新受管运行的正文引用由服务端还原,客户端不需要解析 Blob ref。终态且语义事件已落库后,近期缓存可过期;连接可从持久化历史恢复语义事件并结束。已淘汰的非语义 chunk/通知不能重建,最终结果与详情仍从 GET /runs/:runId 读取。
常见错误:400 参数非法,401 未认证,403 oauth_run_trace_requires_project_read(OAuth 缺少运行所属项目读权限),404 资源不存在;服务端或上游异常返回 500。
GET /runs/:runId/globalctx-events
读取指定运行的 global context 事件历史。
认证:JWT / OAuth / API Key(selectAuth,按 run 归属判定;发布 run 与草稿 run 均适用)。OAuth 读取追踪还要求是运行所属项目 owner 或在该项目具有 read 权限;仅能调用公开 Flow 不等于能读取发布者的追踪。 路径参数
| 参数 | 说明 |
|---|---|
runId | 运行记录 ID |
Query 参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
sinceSequence | number | 否 | 断点续读:仅返回 sequence 严格大于该值的事件 |
limit | number | 否 | 单次返回事件数上限。默认 200,最大 500(超出会被夹到 500)。issue #561 紧急止血新增 |
format | string | 否 | 传 timeline 返回 GlobalCtxRecord[];传单值小写 diagnostics 返回下述安全诊断;不传时返回原始热表事件(向后兼容) |
includeOutput | boolean | 否 | 仅在 format=timeline 下生效。默认(或 false)只返回时间摘要(blockName/blockType/startedAt/completedAt),剥离可能很大的 block_complete.output(block_error 的小体量 error 摘要仍保留);传 true 才带完整 output |
请求:无请求体。
响应:
- 默认(无
format):{ events, maxSequence, truncated, oversizedFirstEvent },events为原始热表事件。 format=timeline:{ globalCtx, maxSequence, truncated, oversizedFirstEvent },globalCtx为 hydrate 后的GlobalCtxRecord[]。旧 run(热表无事件)回退读runs.data.globalCtx,并同样按includeOutput契约裁剪 output。
字节上限保护(issue #561 紧急止血新增):
服务端累计响应字节,超过 2MB 后截断剩余事件(按 sequence ASC 优先保留前序事件)。响应字段:
truncated: boolean— 本页是否因字节上限截断。true时客户端应用maxSequence作为sinceSequence继续拉下一批。oversizedFirstEvent: boolean— 首条事件本身就超过2MB。此时仍强制返回该事件避免 cursor 死循环,但客户端应当感知到本页含异常大事件,必要时降级渲染。该字段仅在历史超大 run(PR-1 时代的 200MB 级 payload)上才会出现,后续主体改造(plan v5 §4.3 blob 外置)落地后将不再触发。
常见错误:400 参数非法(如 sinceSequence 非非负整数),401 未认证,403 oauth_run_trace_requires_project_read(OAuth 缺少运行所属项目读权限),404 资源不存在;服务端或上游异常返回 500。
安全诊断:format=diagnostics(#1575 阶段 B)
GET /runs/:runId/globalctx-events?format=diagnostics&limit=200&sinceSequence=0身份、Token 路由权限、Run 归属和 OAuth 项目读门禁沿用该端点。OAuth 必须是本人、同 App、已记录非空 App 归属的运行,且具备既有 runs:read 与项目读资格;公开 Flow 调用权不等于内部追踪读取权。不新增 scope。未认证返回 401,拒绝路径不读取事件或诊断子树。
诊断成功响应使用 Content-Type: application/vnd.wainao.run-diagnostics+json(可带 charset)。客户端先确认这个头,再读取有界 body 和检查 diagnosticsVersion:1;旧节点没有专用头时取消 body,显示“不支持诊断”,不显示或导出旧响应。混部署时旧节点仍可能按原同权限路径读取原事件,客户端拒收不证明旧服务端没有读取正文。
| Query 参数 | 默认值 | 诊断格式规则 |
|---|---|---|
format | — | 必须是单值、精确小写 diagnostics;重复、混合或大小写变体拒绝 400 |
sinceSequence | 0 | 原始序号下界,不是页码;无符号十进制安全整数,不接受前导零、负数、小数或科学计数 |
limit | 200 | 1..500 个可消费 index 行;超出拒绝,不夹值;实际源读行另计 |
snapshotMaxSequence | 首次当前可见最大值 | 后页沿用首个水位;不能大于当前可见最大值或小于 since;水位不是原子快照 |
blockId | — | 最多 128 字符,首字符字母/数字,之后仅字母、数字、下划线、点、冒号、连字符 |
traceKey | — | 40 位小写十六进制 |
failureKind | — | none / compile_failed / security_rejected / runtime_unavailable / timeout / job_queue_limit / unknown |
from / to | — | UTC ISO 日期时间,YYYY-MM-DDTHH:mm:ss[.SSS]Z;过滤事件 timestamp,按毫秒比较(合法小数向下取整);from ≤ to |
诊断 query 总长度最多 2048 字符,只允许上表键,每个键单值;includeOutput 不适用。过滤发生在验证和消费 index 之后,所以没有命中也可能有续读游标和源读行预算。
{
"diagnosticsVersion": 1,
"runId": "a0cfe1f0-1307-4a99-a124-2e0a4d22649b",
"source": "hot_events",
"events": [],
"executionEvidence": "unknown",
"retention": "policy_unknown",
"nonAtomicRead": true,
"lateWriterPossible": true,
"writing": "not_observed",
"readCoverage": "page_covered",
"reasons": ["hot_events_absent"],
"page": {
"sinceSequence": 0,
"snapshotMaxSequence": 0,
"consumedThrough": 0,
"observedSequences": [],
"nextSinceSequence": null,
"hasMore": false,
"sourceRowsKnown": 0,
"sourceRowsUpperBound": 3,
"sourceCountStatus": "known",
"budgetCharge": 0,
"sourceBytes": 6,
"returnedBytes": 550
}
}新水位只从 readCoverage=page_covered 页确认;未传水位且首页探测失败时的 snapshotMaxSequence=0 是占位值,不能当作真实水位。客户端保留以前已确认/明确请求的水位;否则保持未知,手动导出重新探测。读取与导出的错误各自呈现,互不清除。
示例数字仅展示字段,sourceBytes / returnedBytes / sourceRowsUpperBound 由实际请求计算。每个事件只含原 sequence、摘要 eventKeyDigest、固定类型 eventType、可用的时间/blockId/traceKey、可选 V1 codeDiagnostics 和 missing 枚举。原 event_key 不返回;诊断不读取或导出代码、prompt、output、错误全文/stack、runs.data、envelope 或旧 blob。
observedSequences 最多 500 项,只含本页实际验证且消费的未过滤原序号。客户端据此观察连续前缀和最多 64 个空洞区间,不根据过滤后的 events 猜空洞。原 JSON 序号必须是正安全整数,并且与物理列文本全等;历史缺失、NULL、字符串、错值或重复序号标 legacy_sequence_unpageable / partial,不重建历史序号或回读正文。
codeDiagnostics 仅接受版本 1 的已有记录,最多 8 KiB;缺失为 not_recorded,未知版本为 unsupported_diagnostics_version,损坏字段为 redacted。读取后再次核实际 UTF-8 8 KiB 上限,超限对象整项省略;这是读侧兜底,不证明旧写侧预算已闭环。appVersionFingerprint 只是应用版本指纹,不能当作精确 HEAD/镜像证明;engineBuild / safeLocation 未记录。缺字段不解释历史失败原因。
范围与预算:单页 metadata 源 512 KiB,诊断源 6 MiB,累计源 8 MiB,完整返回 DTO 2 MiB UTF-8,读取期限 10 秒。源超限、超时、取消或不可用时返回已确认的小记录及 partial / unavailable,不自动重试或回读旧正文。HTTP 取消不保证数据库查询已停止。
sourceRowsKnown 是完整解码确认的源读行数,包含最大/最小/NULL 探测、index lookahead 和诊断子树;sourceRowsUpperBound 是已发查询 limit 之和。中断时 sourceCountStatus:unknown,budgetCharge 使用保守上界,不伪记实际值。UI/导出在请求前独立预留 2*limit+5 源行预算,不能因返回计数较低而超读。
正常页以 nextSinceSequence 续读,同一轮固定水位;partial 页停止自动续读。遍历当前可见区间结束只表示 visible_range_exhausted,不是完整执行日志。writer 会消耗未写入序号,空洞不能证明丢日志;迟到写入可能落在已过游标以下。executionEvidence 始终 unknown,Run 终态也不证明 flush 完成;热表空不证明过期或历史不存在。
Run 详情的独立诊断区可手动读取、筛选和导出安全 JSON。导出和 UI 共用分页器,最多 10000 源读行的保守预算、16 MiB 整个 UTF-8 文件、60 秒(含认证头等待、网络、慢 body、校验和序列化)。带失败类型筛选时诊断未知,或带时间筛选时 timestamp 无效,将 filter_indeterminate 停在该项前,不能把未知当不匹配跳过。取消相关筛选可继续浏览安全 missing 项;固定坏字段/未知版本重读不保证改变。触限或未知失败只保留截止前已完整校验的页和受控原因,处理中超期的页整页丢弃;这不是承诺 10000 个命中事件。请求拒绝重定向。导出失败的已确认部分只属于该次导出,不替换面板读取状态或游标。取消、切 Run/筛选、关闭或卸载会使旧请求/下载失效,不执行工作流或自动重试。旧时间线保留原行为,并提示其读取范围不等于完整执行日志。
历史两次 #1575 超时的真实 worker/build/限额/最终 eval 字节仍未知;新增记录和查询不能替代历史根因确认。部署、新认证 Run、跨节点和真实浏览器验收是后续独立门槛。
失败摘要与实际步骤输出(#1575)
失败 Run 的列表 OAuth 摘要和详情根节点可包含 error,内容来自已有运行错误并按有界规则脱敏;旧失败记录缺少错误内容时用 execution_failed,不重新执行流程。OAuth 仍只读取本人、同 App 的运行摘要,不因此得到发布者的快照或内部过程。
旧原始事件/时间线响应根节点也包含失败 error(来自已有运行摘要,无额外 trace 查询)。format=timeline 中,同一 traceKey 的错误与完成事件合并为一条记录:保持 status: "error" 与独立 error,includeOutput=true 同时保留该 traceKey 最后实际保存的 output。循环和嵌套实例按既有 traceKey 区分,错误优先规则和重放身份保持不变。没有实际输出时沿用 { "error": "..." } 占位;includeOutput=false 仅保留小摘要。V2 blob 超预算仍截断 output,错误、位置和状态不受输出预算影响。
查询只还原已有事件,不枚举未执行步骤。现有分页、响应字节上限和 blob 预算保持不变;本次不增加历史导出或新诊断 API。
POST /runs/:runId/resume
用同一 runId 启动新的执行段:回答 ask 提问(ask block 或 Agent 的 AskUserQuestion 工具),或重启意外中断的运行。仅 status 为 success / interrupted 的运行可 resume。
认证:JWT / OAuth / API Key(selectAuth,按 run 归属判定;发布 run 与草稿 run 均适用)。 路径参数
| 参数 | 说明 |
|---|---|
runId | 运行记录 ID |
请求体(判别联合,按 mode 区分)
回答提问(mode: "answer"):
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
mode | "answer" | 是 | 回答 ask 提问 |
askEventKey | string | 推荐 | ask_pending 事件的 event_key(40 位 hex)。缺省时走 blockId fallback(仅唯一未应答 ask 时允许) |
answer | string | 是 | 回答文本(≤ 64KB)。ask block 为用户输入原文;AskUserQuestion 为可读摘要 |
answers | array | 否 | 仅 AskUserQuestion(kind=ask_user):结构化答案,逐题 { question, header?, selected: string[], notes? },与提问行的问题组逐题校验 |
blockId | string | 否 | 提问 block ID(展示 + fallback 用) |
重启中断运行(mode: "retry"):
{ "mode": "retry" }响应 200
{
"ok": true,
"acceptedAt": "2026-07-12T08:00:00.000Z",
"initialSequence": 42,
"askAnswerEventKey": "<40位hex>"
}常见错误:400 参数非法(缺 askEventKey、answers 与问题组不匹配等),401 未认证,403 权限不足,404 ask_pending_not_found,409 当前状态不可 resume,413 answer/answers 超 64KB;服务端或上游异常返回 500。
可选代码执行诊断记录
新CodeBlock的内部持久事件可以带codeDiagnostics(diagnosticsVersion:1),与事件PayloadSlot版本独立。单对象上限为8KiB;写侧在白名单裁剪与结构降级后复验JSON UTF-8字节数,阶段 B 读取后还会独立核限并省略超限对象。它只含受控阶段、实际预算、沙箱尝试、程序摘要和来源/缺失标记;不含代码、prompt、环境、secret、业务正文或原始异常。公开执行响应与业务变量不新增该字段,已有Run读取权限与App/用户/项目隔离保持。
结构降级后仍复验JSON UTF-8字节数,超限摘要不写入。阶段细节裁剪标记为partial/structure_limit。Node/Bun版本限定为三个安全整数数字段(每段最多16位),可选原有最多32字符的ASCII后缀,总长最多83字符。总长超限的版本被移除并标partial/size_limit;其余失真版本被移除并标partial/redacted。短正常版本保持,缺失版本不补猜测值。详情和安全导出按相同的83字符/三段安全整数/后缀限制校验,不能仅按宽正则接受越界数值。未清洗的非法版本响应判为protocol;写侧清洗后省略非法版本的受控partial摘要仍可读取。
status:observed表示这些观察通过白名单,并不保证完整执行日志或全部落库。旧Run不补写,缺字段为未记录;GIT_SHA来源仅是进程版本指纹,不能推断历史构建。阶段 B 在同一 globalctx-events 路径提供下述 format=diagnostics;旧格式与权限合同继续适用。通用TTL与可靠完成水位未提供。
创建运行时的容量拒绝(2026-10-07)
POST /runs 启用受管回放时,缓存注册或初始事件无法安全写入返回 503 replay_capacity_unavailable,服务端不会继续入队/执行。调度队列满而旧任务尚未被所有消费者确认时返回 503 enqueue_failed。失败运行会保留在历史记录中;HTTP 错误不能视为执行已开始。准入开关默认关闭,正在运行的旧格式任务保持兼容。
耗时诊断(2026-10-20)
POST /runs 的响应带 Server-Timing 头,列出响应前各阶段相对请求开始的毫秒偏移(doc、permission、balance、checkpoint、prepared、accepted、launched 等)和本次请求前台发生的数据库请求数(db;desc="requests")。inline 运行结束后,GET /runs/:runId 返回的 data.phases 记录完整阶段(含 executorReady、executed、finished)与前台 / 后台数据库请求数及按表计数。这些字段只用于性能诊断,不含业务数据,字段名可能随实现调整,不应作为业务判断依据。