元数据与落盘
Session SQLite、canonical Message 与 Composer 派生上下文
SDK Session 保存在 City 提供的 Agent 存储作用域中;没有 City 时默认使用内存存储。应用应通过 Session API 读写,不要把这些文件当作自己的数据库。
~/.downcity/agents/<agent_id>/sessions/<origin_type>/<session_id>/
├── session.db
└── attachments/一个 Session 对应一个 session.db。核心表为:
session_state:Session 身份、当前目录、配置、标题、预览和 system snapshot。messages:User/Agent Message 的 envelope。message_parts:每个 Message 的独立 Part;一个 content 对应一个 Part。
读取 API 返回聚合后的 SessionMessage,数据库表结构不会泄漏到 UI 或模型 Provider。Message 是
canonical history,永远不会因为上下文压缩而删除或改写。
session_state.workspace_id 记录的是该会话的当前目录,不是身份:它可以在会话中改变,
改变不影响 session_id、历史与附件归属。Agent 自己的那段对话位于固定的 main 分区,
路径为 sessions/main/main/。
Composer 与派生上下文
SessionComposer 负责生成最终送给模型的 system、messages 和 tools。调用方把 canonical history 与带
命名空间的派生存储传入每次调用,Composer 不会自己去打开 Session 存储。
默认 DefaultSessionComposer 以 Part 为上下文处理边界,并在自己的
composer_adaptive_part_checkpoints 派生表中保存累计摘要边界。它的 compose() 返回:
显式 <session-context-summary> system block
+ 边界之后的 canonical Parts摘要与其他投影出的上下文都是可重建的派生数据,不是 Message。模型返回上下文超限,或真实 usage 超过
压力阈值时,Session 通过 advance_context() 请求 Composer 推进派生状态;不会写入 Active、Segment 或第二份消息文件。
中断恢复与事务
Message envelope 与 Parts 在同一个 SQLite 事务中提交,只有提交成功后才发布 Mutation。启动时
SessionMessages 会把 Storage 查询到的 streaming Agent Message 和 running Action 收口为可恢复状态;Storage 不解释业务终态。附件仍放在
attachments/,Message Part 只保存附件引用。
模型流遵循“事件负责实时展示、内存负责流式组装、数据库只保存稳定语义检查点”:普通 chunk 不写 SQLite;完整模型 Step、Tool 执行前后、Interaction、Action/Error 和 Message 收口才提交事务。
旧版本 JSONL 数据不由 Desktop 或 Runtime 自动迁移。需要迁移时,在应用外执行仓库提供的一次性脚本
scripts/migrate-session-storage-to-sqlite.mjs。已经生成旧 SQLite 的开发期数据使用
scripts/migrate-session-sqlite-v3.mjs 升级;Runtime 只检查版本,不会隐式修改磁盘。Interaction 并入
Tool Part 之前生成的数据库仍保留独立的 Interaction 行,读取时会被拒绝;执行
scripts/migrate-session-interaction-into-tool.mjs 把它们移回所属 Tool Part。