Sessions

Message 与 Part

读取 Active 与 Segment 历史,理解 Message、Part 和 sequence

Message 与 Part

session.messages() 返回当前 Active 的全部持久化 Message,不需要 limit。实时变化由 subscribe() 补充。

const page = await session.messages();

for (const message of page.items) {
  render_message(message);
}

返回结果为:

interface SessionMessagePage {
  items: SessionMessage[];
  total: number;
  source: "active" | "segment";
  start_sequence?: number;
  end_sequence?: number;
  next_before_sequence?: number;
  has_more: boolean;
}

total 是 Session 已创建的真实 Message 总数,不是当前 items.lengthsource 说明本次结果来自当前 Active 还是一个已关闭 Segment。

四类顶层 Message

type表示什么常见展示
user用户输入或运行中 steering用户气泡、附件
assistant模型生成的一次连续回复文本、推理、工具、文件
action模型切换、压缩等运行时操作状态通知
errorSession 或 Turn 的可展示错误错误提示与重试入口

Session 内只有一条按 sequence 排序的顶层 Message 序列。工具调用不是另一条 Timeline,也不是顶层 Message;它属于 Assistant Message 的 parts,因此 text -> tool -> text 会以真实生成顺序保存。

普通 Tool Loop、Provider continuation 和恢复重试不会创建新的 Assistant Message;它们继续向当前 Assistant 追加有序 Part。只有新的 User Message(包括运行中 steer)真正进入消息序列时,才结束之前的 Assistant Message,后续输出再创建下一条 Assistant Message。模型 step 的边界由 step-start Part 表达,不需要额外的 segment 字段。

SessionMessage 是持久化数据结构,AI SDK UIMessage 是 Executor 与 UI 边界格式。User / Assistant 的可序列化 UI parts 可以转换成 Session parts,再恢复为等价的 UIMessageactionerror 是 Downcity Session 自己的顶层类型,不会送入模型。

Turn 在产生任何 Assistant 内容前失败时,只写入一条 error Message,不会用错误文本伪造 Assistant 回复。若失败前已经产生部分内容,该 Assistant 会以 status: "failed" 保留,随后再写入结构化 error Message。

Assistant Part

type关键字段说明
texttext, state用户可见输出,streaming -> done
reasoningtext, state推理输出,是否展示由产品决定
tooltool_call_id, tool_name, state, metadata工具输入、等待、运行和结果
interactioninteraction_id, interaction_type, status, request, response审批、问题等用户异步交互
fileurl, media_type, provider_metadataAssistant 生成或引用的文件
sourcesource_type, source_id, URL 或文档字段AI SDK URL / document source
datadata_type, data, data_id可持久化的结构化 UI 数据
step-start无业务字段多 step 回复的边界

每个 Assistant Part 有稳定的 part_id 和创建后不可变的 sequence。每个模型 step 的 UI chunks 是 canonical 顺序的唯一来源;step 最终快照只校验顺序并补充 metadata,不会创建、删除或重排 Part。工具状态变化更新同一个 Part,不应在 UI 中创建“请求”和“结果”两个重复行。

Tool Provider metadata

Tool Part 会保留 AI SDK Provider 附着在工具调用和工具结果上的 metadata:

interface SessionAssistantToolPart {
  title?: string;
  tool_metadata?: JsonObject;
  dynamic?: boolean;
  call_provider_metadata?: ProviderMetadata;
  result_provider_metadata?: ProviderMetadata;
  provider_executed?: boolean;
  preliminary?: boolean;
}

这些字段是 Provider continuation 等能力所需的透明数据。应用可以读取并原样转存,但不应修改其中的 Provider 专属字段。旧 Session 没有这些可选字段时仍可正常读取。

文本、推理、文件和 source 同样保留各自的 provider_metadata。相同 data_idsource_id 的流式更新会替换原 Part 并保留 sequence;带 transient: true 的 AI SDK data chunk 只用于实时 UI,不写入 Session。

Tool 状态不保存审批副本。用户参与使用独立 Interaction Part:

interface SessionAssistantInteractionPart {
  interaction_id: string;
  interaction_type: "approval" | "question";
  status: "pending" | "resolved" | "expired" | "cancelled";
  request: SessionInteractionRequest;
  response?: SessionInteractionResponse;
}

关联 Tool 使用 ready -> waiting-user -> running -> completed,拒绝、超时或取消进入 failed。Tool 与 Interaction 在同一 Assistant revision 中提交;同一 revision 出现两条 Part Mutation 表示一次原子快照中的两个变更,不是两个独立事务。

顺序、版本与可见性

每条 Message 都有稳定 message_id、创建后不变的 sequence,以及随完整快照更新递增的 revision。客户端不能用较低 revision 覆盖较高 revision。

SDK 在每个模型 step 完成时校验最终 UIMessage 与 canonical chunks 的 Part 数量、类型、顺序和稳定标识。AI SDK 可能为只有 start/end、没有 delta 的 Text 或 Reasoning 生成空占位 Part;这类没有用户可见内容的 Part 会在对账前忽略,不进入 canonical history。其余最终快照不一致或 canonical part 写入失败都会使 Turn 失败,不会通过正文内容猜测 identity,也不会发布历史不完整的 completed Turn。客户端收到更高 revision 的完整 message Mutation 时,应替换整条 Message,而不是保留旧 revision 的 parts 数组。

默认 messages() 只返回 visibility: "visible" 的 Message。调试或审计时可传 include_internal: true。Compact Summary 不是 Message,不占用 sequence,也不会出现在 messages() 结果中。

加载更早历史

第一次调用不传参数,得到 Active 全量。若 has_moretrue,把 next_before_sequence 原样传回,会得到紧邻的前一个完整 Segment:

let page = await session.messages();
render_messages(page.items);

while (page.has_more && page.next_before_sequence !== undefined) {
  page = await session.messages({
    before_sequence: page.next_before_sequence,
  });
  prepend_messages(page.items);
}

before_sequence 必须是正整数。每次历史读取返回一个完整 Segment,不支持 limitcursor、offset 或 Segment 内切片。这样 UI 边界与物理 Compact 边界一致,也不会重复读取累计 Summary。

与实时 Mutation 配合

  1. messages() 建立 Active 初始状态。
  2. message / part Mutation 替换对应快照。
  3. delta 追加当前文本 Part 的新文本或 Tool Part 的输入原文。
  4. 向上滚动时按 next_before_sequence 逐个加载 Segment。
  5. 断线后重新读取 Active,并按 message_id + revision 合并。

完整同步算法见 重连与同步,存储细节见 元数据与落盘