20 KiB
TauriTavern Agent Run Event Journal
本文档定义 Agent Run 的事件日志、状态机、订阅、恢复、取消与审批语义。
Run Journal 是 Agent 系统的真相源。没有 journal 的 Agent 只是一个难以调试的异步流程。
1. 原则
- Append-only:事件只能追加,不能原地修改。
- Ordered:每个 run 内 event seq 单调递增。
- Durable:关键副作用前后必须落 journal。
- Replayable:UI timeline、debug、resume 应尽量从 journal 重建。
- User-visible:用户能看到工具调用、审批、workspace mutation、commit 与错误。
2. 文件格式
第一期建议 JSONL:
events.jsonl
每行一个 event envelope:
{
"seq": 12,
"id": "evt_...",
"runId": "run_...",
"timestamp": "2026-04-26T00:00:00Z",
"level": "info",
"type": "tool_call_completed",
"payload": {},
"causality": {
"parentEventId": "evt_...",
"requestId": "model_req_..."
}
}
要求:
seq由 repository 分配。id全局唯一或 run 内唯一均可,但必须稳定。type使用 snake_case。- payload 必须可反序列化为 tagged enum。
- 大文本、二进制、长 tool result 不直接塞进 event,使用 resource ref。
3. Run Status
当前已落地状态:
Created
InitializingWorkspace
AssemblingContext
CallingModel
DispatchingTool
ApplyingWorkspacePatch
AwaitingHostCommit
Finishing
Completed
Cancelling
Cancelled
Failed
后续规划状态:
Created
InitializingWorkspace
AssemblingContext
Planning
Running
CallingModel
AwaitingApproval
DispatchingTool
ApplyingWorkspacePatch
AwaitingHostCommit
Finishing
Completed
Cancelling
Cancelled
Failed
终态:
Completed
Cancelled
Failed
状态迁移必须通过 event 记录,例如:
status_changed { from, to, reason }
4. Event 类型
当前实际写入的主要事件:
run_created
generation_intent_recorded
status_changed
workspace_initialized
persistent_projection_initialized
context_assembled
agent_invocation_created
agent_invocation_started
agent_invocation_completed
agent_invocation_failed
agent_invocation_cancelled
agent_invocation_transferred
agent_task_registered
agent_task_queued
agent_task_started
agent_task_completed
agent_task_failed
agent_task_cancelled
agent_delegate_started
agent_handoff_requested
agent_handoff_accepted
agent_await_started
agent_await_completed
task_return_completed
model_request_created
model_call_attempt_started
model_call_attempt_failed
model_call_retry_scheduled
model_response_stored
provider_state_updated
model_completed
direct_output_captured
tool_call_requested
tool_call_started
tool_result_stored
tool_call_completed
tool_call_failed
workspace_file_written
workspace_patch_applied
agent_loop_finished
artifact_assembled
commit_started
commit_draft_created
persistent_changes_committed
persistent_changes_commit_failed
persistent_state_metadata_update_requested
persistent_state_metadata_updated
persistent_state_metadata_update_failed
run_committed
run_completed
run_partial_success
run_cancel_requested
run_cancelled
drift_recovery_attempted
run_failed
多 invocation 事件必须写入 canonical event scope:
{
"eventScope": {
"invocationId": "inv_root",
"relatedInvocationIds": ["inv_child"]
}
}
eventScope.invocationId 是事件主归属 invocation;eventScope.relatedInvocationIds 是同一事件关联的其它 invocation。readEvents({ invocationId }) 先按该 event scope 过滤,再应用 seq 分页。旧 journal 没有 canonical scope 时,后端可以用历史 payload 字段兼容读取,但新事件不应依赖字段名推断作为主契约。
以下小节同时包含当前已落地事件和后续阶段设计事件;实现新事件时必须更新 docs/CurrentState/AgentFramework.md。
Handoff event sequence
当前 agent.handoff 的典型事件序列:
agent_handoff_requested
agent_invocation_created # kind = handoff
agent_task_registered # continuation = transfer_control
agent_handoff_accepted
agent_invocation_transferred # source invocation
agent_loop_finished
agent_task_started # handoff task starts when target invocation is prepared
agent_invocation_started # target invocation
context_assembled
skill_scopes_resolved
...
agent_task_completed # incoming handoff task completes when target finishes or hands off again
Handoff 的 task / invocation 结构、prompt brief 与失败边界见 docs/Agent/Handoff.md。
4.1 Run Lifecycle
run_started
status_changed
run_completed
run_partial_success
run_cancel_requested
run_cancelled
drift_recovery_attempted
run_failed
run_failed payload:
{
"code": "tool_policy_denied",
"message": "Tool mcp.foo is not allowed by current plan node",
"technicalMessage": "Validation error: tool_policy_denied: ...",
"retryable": false,
"userRetryable": false,
"details": {}
}
retryable:宿主可不询问用户、安全地自动重试。RateLimited、Transient与结构化 networkUpstreamFailure等暂态错误为true,并消费同一 ProfilemodelRetry预算。例如model.upstream_invalid_response表示上游响应体本应承载 provider JSON 契约但不可读或不是合法 JSON,属于可重试的 transient invalid response;它不应用于 request build、provider 明确拒绝、tool call / native metadata / response schema 等本地契约错误。userRetryable:用户可通过 UI 手动重试。retryable=true时一定为true;此外model.tool_call_required、agent.max_tool_rounds_exceeded等轮次耗尽后的指令漂移错误也是userRetryable=true,但 禁止 自动重试。若 run 已经有任意 host-confirmed 自动或显式 chat commit,终态会改为run_partial_success,不会复用run_failed.userRetryable,避免用户在已保留输出上直接重试造成重复消息。
run_partial_success 是一个独立终态:当一次 run 已经通过自动或显式 Committer 向 chat 发布过至少一次 host-confirmed 结果,但后续仍因 drift、dispatch、workspace.finish、persistent commit 或 persistent metadata 写回错误失败时,executor 会保留这些 chat commit,并写 run_partial_success。它不是 clean success:错误仍在 payload 中暴露,retryable / userRetryable 固定为 false,宿主 UI 应以 warning 展示“已保留部分结果”。只有 persistent_state_metadata_updated 成功后,chat message 才能带有可作为下一轮 base 的 persistStateId。
{
"code": "model.tool_call_required",
"message": "model must use Agent tools and finish through workspace_finish",
"technicalMessage": "Validation error: model.tool_call_required: ...",
"retryable": false,
"userRetryable": false,
"details": {},
"preservedCommitCount": 1,
"preservedCommits": [
{
"path": "output/main.md",
"mode": "replace",
"messageId": "10",
"round": 4
}
]
}
run_rollback_targets 作为显式丢弃 / 旧版本清理事件形状保留。它不再是 committed drift 的默认终态路径:自动 rollback 会浪费模型已经通过 host 确认的输出。宿主收到该事件时,rollback 必须 fail-fast:目标缺失、runId 不匹配、rollback strategy 缺失、swipe 状态不安全或宿主删除 API 不可用都必须报错,不能静默跳过或扩大为整条消息删除。若之后暴露 retry 动作,也必须使用 run handle / generation_intent_recorded 的 typed generation intent,不得触发 DOM regenerate 按钮。
{
"reasonCode": "model.tool_call_required",
"round": 5,
"targetCount": 1,
"targets": [
{
"path": "output/main.md",
"mode": "replace",
"messageId": "10",
"round": 4
}
]
}
Soft drift recovery:在直接 fail-fast 之前,loop runner 会做"软纠正":当模型返回 0 tool_calls 且包含纯文本时,runtime 会把该文本捕获到当前 profile messageBody artifact 所在 root 的 direct_output.md(默认 output/direct_output.md)并写入 direct_output_captured;随后把纯文本回复推进 history,再追加一条合成的 user 消息提醒它必须通过 Agent 工具继续。root run 如果直接输出就是目标回复,应显式 workspace_commit 该捕获文件,再调用 workspace_finish;如果需要修订已提交内容,必须先 workspace_apply_patch / workspace_write_file,再 workspace_commit,最后 workspace_finish。return-mode child 则必须用 task_return 结束。direct output recovery 没有独立的一次性尝试上限;只要仍有下一轮模型调用预算就继续纠偏。每次尝试都会写一条 drift_recovery_attempted 事件,便于宿主 UI 给用户显示"系统正在纠正…"提示:
{
"attempt": 1,
"maxAttempts": 79,
"maxRounds": 80,
"limitReason": "max_rounds",
"round": 9,
"committedCount": 1,
"reasonCode": "model.tool_call_required"
}
maxAttempts 是兼容字段,表示在当前 maxRounds 下 direct-output recovery 的理论上限;实际停止条件由 maxRounds / cancel 决定,而不是另一套隐藏 retry budget。
- 恢复成功 → run 继续,不会发
run_rollback_targets,也不会写run_failed - 恢复失败(模型再次返回 0 tool_calls 且已没有下一轮预算)→ 若没有成功 chat commit,写
run_failed(userRetryable=true);若已有成功 chat commit,写run_partial_success并保留输出 - 上述 rollback / partial-success 语义与前端入口无关;普通发送、
/trigger、regenerate 与 overswipe 只要进入 Agent run,都必须遵守同一 journal 与 host commit 契约。
4.1.1 User Guidance
user_guidance_submitted
user_guidance_applied
user_guidance_discarded
User guidance 是 active AgentRun 的 run-scoped 用户输入,不是普通聊天消息。提交成功后 runtime 必须先写 user_guidance_submitted,再将 guidance 放入当前 run mailbox;下一次 root / handoff 前台 invocation 创建模型请求前,runtime 将 pending guidance 合并为一条 canonical role=user message,并写 user_guidance_applied。
已经发出的 provider request 不可被热修改。若 guidance 在模型调用、工具调用、host commit 等过程中提交,只能影响后续安全模型请求边界。run cancel、finish、failure / partial success 会关闭 mailbox,并对尚未应用的 guidance 写 user_guidance_discarded。
事件 payload 必须包含 guidanceId 或 guidanceIds、chars、words、preview、status,并带 invocationId / canonical eventScope 以便 Timeline 可以归入前台 Agent 链。user_guidance_submitted 在 V1 直接内联受长度限制的完整 text,便于 Timeline detail 和审计读取;applied / discarded 事件用 ids、统计和 preview 关联,不重复写全文。guidance 文本不应写入普通 chat history。
4.2 Workspace
workspace_initialized
workspace_file_written
workspace_patch_requested
workspace_patch_applied
workspace_patch_failed
workspace_file_deleted
workspace_rollback_completed
Workspace event 不应内联大文件全文。面向 Agent/UI 的 payload 应记录 path、sha256、chars、words、patch ref;字节数仅保留在文件系统与 hash 等内部实现边界。
4.3 Context
context_assembly_started
context_component_added
context_component_skipped
context_assembled
context_assembly_failed
context_component_skipped 必须有 reason,例如 budget、policy hidden、empty。
Policy 拒绝不是 skipped,而是 failure。
当前每个 Agent invocation 的 context_assembled 会记录 invocationId、canonical request summary、toolSnapshotPath、紧凑 toolSnapshot 摘要与完整 toolTurn。冻结 descriptor/schema 只在 input/invocations/<invocation-id>/tool_snapshot.json 写一次;journal 不重复内联大 schema。toolTurn 包含 snapshotId、有序 canonical ToolId 列表与 typed choice,因此可以证明本轮 provider 实际看到的能力边界。
4.4 Model
model_request_created
model_request_sent
model_delta
model_tool_call_delta
model_response_stored
provider_state_updated
model_completed
direct_output_captured
model_failed
当前 model_request_created 记录 canonical request summary(source、custom format、model、message count、tool count)、round、invocationId 与同一 invocation 的 toolTurn,不默认记录完整 prompt。长期应记录 request ref、profile id、provider/source、model、token estimate;完整 prompt 是否保存取决于调试设置与隐私策略。
当前 model_response_stored 会把完整 AgentModelResponse 写入 model-responses/round-XXX.json,event 只记录路径与摘要。provider_state_updated 只记录 provider_state 摘要字段,不记录完整内部 payload。
model_completed 是 UI timeline 的模型回合入口:
{
"round": 1,
"modelResponsePath": "model-responses/round-001.json",
"toolCallCount": 1,
"textChars": 26,
"textWords": 5,
"hasAssistantText": true,
"assistantTextChars": 26,
"assistantTextWords": 5,
"narration": {
"source": "assistantText",
"text": "I will write the artifact.",
"totalChars": 26,
"totalWords": 5,
"truncated": false
},
"hasReasoning": true,
"reasoningChars": 30,
"reasoningWords": 6
}
narration 是可选字段,仅在模型回合包含工具调用且存在可展示 assistant visible text 时写入;它是模型轮次的展示投影,不是新的 run status,也不从 reasoning / thinking / thought 提取。事件中只保存短 preview;完整文本仍通过 readModelTurn({ runId, round }) 的白名单 DTO 读取。
前端读取详情时使用 Host ABI readModelTurn({ runId, round }),不直接解析 modelResponsePath 指向的 raw 文件。
4.5 Tool
tool_call_requested
tool_call_awaiting_approval
tool_call_approved
tool_call_denied
tool_call_started
tool_call_completed
tool_call_failed
tool_call_requested:
{
"round": 1,
"invocationId": "inv_root",
"callId": "call_...",
"toolId": "builtin:workspace.apply_patch",
"snapshotId": "inv_root",
"name": "workspace.apply_patch",
"argumentsRef": "tool-args/call_....json",
"providerMetadata": {}
}
tool_call_completed:
{
"round": 1,
"invocationId": "inv_root",
"callId": "call_...",
"toolId": "builtin:workspace.apply_patch",
"snapshotId": "inv_root",
"name": "workspace.apply_patch",
"isError": false,
"errorCode": null,
"message": null,
"elapsedMs": 120,
"resourceRefs": ["output/main.md"]
}
tool_call_started、tool_call_completed 与 tool_call_failed 保持 tool_call_requested 之后的既有顺序,并携带同一 canonical toolId / snapshotId。预算耗尽属于可恢复的 tool_call_failed;turn/snapshot/choice violation 在 requested 后 fail-fast,不会发出 started。tool_result_stored 会携带同一 round、toolId 与 path,用于 UI 读取工具结果详情。
4.6 Plan / Profile
plan_created
plan_updated
plan_node_started
plan_node_completed
plan_policy_violation
profile_selected
profile_switch_requested
profile_switched
profile_switch_denied
Locked plan violation 必须失败或等待用户决策,不能静默忽略。
4.7 Artifact / Commit
artifact_assembly_started
artifact_assembled
artifact_assembly_failed
chat_commit_requested
chat_commit_completed
chat_commit_failed
committed_message_rollback_completed
自动与显式 commit 共用同一事件序列。自动 commit 在一轮 tool calls 全部处理后最多发起一次。chat_commit_requested 记录 chat ref、artifact path 与预期 SHA;Host 重读当前 workspace,SHA 不一致时拒绝提交,并在确认后记录 message id。chat_commit_completed.messageId 是提交时结果,不是对当前聊天消息位置的反向查询。Host 返回未确认时写 warning 级 chat_commit_failed;它不会终止 run,也不会产生 chat_commit_recorded。自动提交跳过本次,显式提交另写模型可见的 tool_call_failed。
5. 事件与副作用的顺序
推荐顺序:
意图事件
-> 执行副作用
-> 结果事件
例如 tool call:
tool_call_requested
tool_call_started
tool dispatch
tool_call_completed / tool_call_failed
例如 workspace patch:
workspace_patch_requested
apply patch
workspace_patch_applied
当前 workspace_file_written 同时覆盖 workspace.write_file 的 replace 与 append 成功结果,payload 必须携带 mode,便于 timeline 与恢复逻辑区分完整替换和原样追加。
如果副作用前需要保证恢复后不会重复执行,可以先写 pending event,再由恢复逻辑检查 pending 状态。这一点对 MCP/外部副作用尤其重要。
6. 实时订阅
前端订阅 API:
const unsubscribe = await window.__TAURITAVERN__.api.agent.subscribe(runId, event => {});
要求:
- subscribe 不复播全部历史,除非 options 指定。
- UI 首次进入 run 页面可以读取最新页
readEvents(runId, { beforeSeq, limit }),再通过 subscribe /afterSeq追新;需要回看更早轮次时继续用beforeSeq分页补拉。 - unsubscribe 必须幂等。
- 事件丢失时,UI 可通过
afterSeq补拉。
7. 分页读取
Journal 读取需要支持:
readEvents(runId, { afterSeq, limit })
readEvents(runId, { beforeSeq, limit })
移动端 timeline 不应该一次读取巨大 journal。 默认 timeline UI 应把 raw journal 投影成用户可见操作流,并使用窗口化渲染;状态机、provider state 等审计事件仍保留在 journal 中,但不应因为 UI 容量限制挤掉早期操作轮次。
8. Cancel
Cancel 是用户意图,不是 failure。
流程:
cancel_agent_run(runId)
-> run_cancel_requested
-> signal cancellation token
-> 当前可取消操作停止
-> run_cancelled
约束:
- LLM call 必须尽量复用现有 cancellation registry 或等价 watch channel。
- Tool dispatch 需要声明是否 cancellable。
- Cancel 后不能 commit。
- Cancel 后 workspace 保留。
9. Approval
危险工具、MCP tool、commit、profile switch 可以要求审批。
流程:
tool_call_requested
tool_call_awaiting_approval
approveToolCall({ approved: true/false })
tool_call_approved / tool_call_denied
审批必须记录:
- requested tool
- arguments summary/ref
- policy reason
- user decision
- decision timestamp
审批拒绝不是系统错误。它可以进入模型可见 tool error,或让 run 暂停/失败,取决于 plan policy。
10. Resume
第一期可以不实现自动 resume,但 journal 设计必须支持。
Resume 的基本策略:
- 读取最后状态。
- 如果终态,拒绝 resume。
- 如果 pending model call,没有 result event,标记 previous attempt interrupted,重新发起或让用户选择。
- 如果 pending external tool call,默认不重复执行,要求人工确认。
- 如果 pending workspace patch,检查 patch result/ref 决定是否重放。
外部副作用必须谨慎,不能因为恢复而重复调用付费 API 或危险工具。
11. Error 分类
建议错误 code 分层:
agent.invalid_intent
agent.invalid_profile
agent.policy_violation
agent.cancelled
workspace.path_denied
workspace.required_artifact_missing
workspace.patch_failed
context.budget_exceeded
model.provider_denied
model.request_failed
tool.not_found
tool.policy_denied
tool.execution_failed
mcp.server_denied
mcp.tool_denied
commit.cursor_integrity
commit.save_failed
journal.append_failed
journal.append_failed 是严重错误。没有 journal 就不能继续执行副作用。
12. 当前核心 Event Set
当前至少应保持:
run_created
generation_intent_recorded
status_changed
workspace_initialized
persistent_projection_initialized
context_assembled
model_request_created
model_response_stored
provider_state_updated
model_completed
direct_output_captured
tool_call_requested
tool_call_started
tool_result_stored
tool_call_completed
tool_call_failed
workspace_file_written
workspace_patch_applied
agent_loop_finished
artifact_assembled
commit_started
commit_draft_created
persistent_changes_committed
persistent_state_metadata_update_requested
persistent_state_metadata_updated
persistent_state_metadata_update_failed
run_committed
run_completed
run_partial_success
run_cancel_requested
run_cancelled
run_failed
这套事件已经足够支撑当前 tool loop、timeline、cancel、debug、commit、partial-success 和 diff 基础。run_rollback_targets 作为显式丢弃 / 旧版本清理事件形状保留,但不再是 committed drift 的默认终态路径。