fix(core): serialize Trace appends; tolerate malformed middle lines on read (#234)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -54,6 +54,7 @@ A Trace is an append-only JSON Lines file; each line is one OmniMessage envelope
|
||||
- Recorded: `session_meta`, complete `model_msg`, and all `event_msg`.
|
||||
- Not recorded: streaming `partial_*` fragments (the producer appends the complete message once the segment ends), and nested messages tagged with `origin` — a subagent's messages go to the child Session's own Trace, while the parent Trace keeps a single `subagent` pointer event at the spawn site recording the child Session id.
|
||||
- `request_begin` and `request_end(status)` come in pairs delimiting one Request; replay uses `request_end.status === "completed"` as the commit criterion for that turn.
|
||||
- Appends are serialized inside the writer: records from concurrent producers (the model stream, parallel tool executions) land strictly one after another, each as a single uninterrupted line — a multi-megabyte record such as a base64 image Data URL can never be torn apart by a concurrent append, and file rotation never splits a record.
|
||||
|
||||
See `packages/core/src/trace/writer.ts` for the implementation.
|
||||
|
||||
@@ -82,7 +83,7 @@ The Trace is the single source of truth for recovery — there is no separate se
|
||||
4. Reconstruct the carry-over (undelivered tool outputs, interruption markers) plus turn and Token counters;
|
||||
5. Continue appending to the same Trace file.
|
||||
|
||||
Recovery requires that the Workspace and the model still exist. What recovery guarantees is structural legality: only committed turns are replayed, with `tool_call` / `tool_call_output` pairing intact; incomplete model output (thinking, text) is allowed to be lost. A truncated last line left by an abnormal process exit is tolerated and ignored. See `packages/core/src/trace/resume.ts`.
|
||||
Recovery requires that the Workspace and the model still exist. What recovery guarantees is structural legality: only committed turns are replayed, with `tool_call` / `tool_call_output` pairing intact; incomplete model output (thinking, text) is allowed to be lost. A truncated last line left by an abnormal process exit is tolerated and ignored; a malformed line in the middle of a file (e.g. in a file damaged before writer appends were serialized) is skipped with a diagnostic on stderr, and every parseable record is kept. See `packages/core/src/trace/resume.ts`.
|
||||
|
||||
Special case: if the latest Trace file ends with a completed compaction, that context is closed as a whole — resume starts from an empty context; in summarize mode the `[context_summary]` is reconstructed and prepended to the first input after resume (old Traces using the earlier angle-bracket `<summary>` form are still understood).
|
||||
|
||||
|
||||
@@ -52,6 +52,7 @@ Trace 是 append-only 的 JSON Lines 文件,每行一个 OmniMessage 信封(
|
||||
- 记录的消息:`session_meta`、完整的 `model_msg`、全部 `event_msg`。
|
||||
- 不记录的消息:流式 `partial_*` 分片(片段结束后由生产方补写完整消息);带 `origin` 标记的嵌套消息——子 Agent 的消息写入子 Session 自己的 Trace,父 Trace 只在派生位置保留一个 `subagent` 指针事件,记录子 Session id。
|
||||
- `request_begin` 与 `request_end(status)` 成对出现,界定一轮 Request;回放以 `request_end.status === "completed"` 作为该轮已提交的判据。
|
||||
- 追加在写入器内部串行执行:并发生产者(模型流、并行工具执行)的记录严格逐条落盘,每条都是一行完整内容——base64 图片 Data URL 之类的多 MB 大记录不会被并发追加撕裂,文件轮转也不会切断任何记录。
|
||||
|
||||
实现见 `packages/core/src/trace/writer.ts`。
|
||||
|
||||
@@ -80,7 +81,7 @@ Trace 是恢复的唯一事实来源,没有独立的会话数据库需要与
|
||||
4. 重建 carry-over(未送达的工具输出、中断标记)与轮数、Token 计数器;
|
||||
5. 继续追加写入同一个 Trace 文件。
|
||||
|
||||
恢复的前提是 Workspace 与模型仍然存在。恢复保证的是结构合法性:只回放已提交的轮次,`tool_call` 与 `tool_call_output` 配对完整;未完成的模型输出(thinking、文本)允许丢失。异常退出留下的截断末行会被容忍并忽略。实现见 `packages/core/src/trace/resume.ts`。
|
||||
恢复的前提是 Workspace 与模型仍然存在。恢复保证的是结构合法性:只回放已提交的轮次,`tool_call` 与 `tool_call_output` 配对完整;未完成的模型输出(thinking、文本)允许丢失。异常退出留下的截断末行会被容忍并忽略;文件中间的损坏行(如写入器追加串行化之前受损的存量文件)会被跳过并在 stderr 给出诊断,其余可解析的记录全部保留。实现见 `packages/core/src/trace/resume.ts`。
|
||||
|
||||
特殊情形:若最新 Trace 文件以一次完成的压缩收尾,则该上下文已整体关闭——恢复从空上下文开始;summarize 模式下会重建 `[context_summary]` 摘要,前置到恢复后第一轮输入中(旧 Trace 中早期的尖括号 `<summary>` 形式仍可识别)。
|
||||
|
||||
|
||||
Reference in New Issue
Block a user