JSONL 会话存储器。 设计要点(来自 Reference EP09): 1. 仅追加写入 — 不修改已有内容 2. 崩溃安全 — 追加写入是原子操作(配合 O_APPEND) 3. 每条消息独立 — 一行坏了不影响其他行 对应 Reference: architecture/zh-CN/09-session-persistence.md - sessionStorage.ts:5,106 行 - JSONL 仅追加存储
| 278 | # 这是 Reference EP09 描述的生产级方案 |
| 279 | |
| 280 | class JsonlSessionStorage: |
| 281 | """ |
| 282 | JSONL 会话存储器。 |
| 283 | |
| 284 | 设计要点(来自 Reference EP09): |
| 285 | 1. 仅追加写入 — 不修改已有内容 |
| 286 | 2. 崩溃安全 — 追加写入是原子操作(配合 O_APPEND) |
| 287 | 3. 每条消息独立 — 一行坏了不影响其他行 |
| 288 | |
| 289 | 对应 Reference: architecture/zh-CN/09-session-persistence.md |
| 290 | - sessionStorage.ts:5,106 行 |
| 291 | - JSONL 仅追加存储 |
| 292 | """ |
| 293 | |
| 294 | def __init__(self, base_dir: str): |
| 295 | self.base_dir = base_dir |
| 296 | os.makedirs(base_dir, exist_ok=True) |
| 297 | |
| 298 | def _session_path(self, session_id: str) -> str: |
| 299 | """ |
| 300 | 生成会话文件路径。 |
| 301 | |
| 302 | 对应 Reference: EP09 §1 |
| 303 | ~/.claude/projects/{sanitized_cwd}/{session-id}.jsonl |
| 304 | """ |
| 305 | return os.path.join(self.base_dir, f"{session_id}.jsonl") |
| 306 | |
| 307 | def append_message(self, session_id: str, message: ConversationMessage): |
| 308 | """ |
| 309 | 追加一条消息到 JSONL 文件。 |
| 310 | |
| 311 | 这是最核心的写入操作。每条消息一行,追加到文件末尾。 |
| 312 | |
| 313 | 对应 Reference: EP09 §2 |
| 314 | Project.appendEntry() → enqueueWrite(filePath, entry) |
| 315 | """ |
| 316 | path = self._session_path(session_id) |
| 317 | entry = { |
| 318 | "type": message.role, |
| 319 | **message.to_dict(), |
| 320 | } |
| 321 | line = json.dumps(entry, ensure_ascii=False) + "\n" |
| 322 | with open(path, "a") as f: |
| 323 | f.write(line) |
| 324 | |
| 325 | def append_metadata(self, session_id: str, meta_type: str, data: dict): |
| 326 | """ |
| 327 | 追加元数据条目(标题、标签等)。 |
| 328 | |
| 329 | 对应 Reference: EP09 §1 条目类型 |
| 330 | custom-title, ai-title, last-prompt, tag 等 |
| 331 | """ |
| 332 | entry = {"type": meta_type, **data} |
| 333 | path = self._session_path(session_id) |
| 334 | line = json.dumps(entry, ensure_ascii=False) + "\n" |
| 335 | with open(path, "a") as f: |
| 336 | f.write(line) |
| 337 |