交付运行时
Delivery runtime 会把输出请求转换为持久化的 session records、实时 events、artifacts 和 channel items。
每次 output send_* 调用也会向 SQLite runtime outbox projection 写入一个
delivery intent,以及匹配的 delivery.send durable work item。Outbox rows 通过
owner_turn_id 归属于 foreground turn;delivery work 不会把 foreground turn id
当作 task id 或 parent work id。DeliveryRuntime
通过 SessionInteractionRouter dispatch;router 只按 InteractionOutbound.session_id
查找当前活跃 route。Channel adapters 绑定到 session,并把 payload 适配为平台 API,
但不拥有 durable delivery state。
内存中的 InteractionItem.dispatch_status 生命周期是
pending -> scheduled -> delivered/failed/unrouted。持久化 outbox 生命周期是
queued -> sending -> sent/failed/unknown/unrouted。unrouted 表示该 outbound
session 当前没有绑定 route;它不同于 failed,后者表示 route 存在但 adapter
delivery 抛错。
来源
Delivery requests 可以来自:
- output modules
- authored tools
- schedule runs
- channel adapter logic
历史策略
持久化的 delivery 会成为持久的 assistant history。临时 delivery 适 合进度、 notice 和仅实时输出。
Artifacts
Artifacts 由 host-owned records 表示。Output modules 可以请求 artifact delivery,
但路径、metadata 和持久化都由 host 负责。
Foreground delivery artifacts 通过 owner_turn_id 归属于 session turn;除非未来
由明确的 task-artifact seam 支持真实 detached task 产物,否则它们不是 task
artifacts。
Session Routes
InteractionOutbound.session_id 是必填字段。channel 字段只是 adapter metadata,
不再决定 route ownership。普通 live delivery 只按 outbound.session_id 路由。
SessionInteractionRouter 拥有 live route table:
bind(session_id, route)把 TUI、Telegram 或其他 adapter route 绑定到一个 session,并返回 token。unbind(token)移除这个 live route。deliver(outbound)只发送到outbound.session_id绑定的 route。prompt_user(prompt)和request_approval(request)使用单独的 session-aware route lookup 处理交互 prompt 和 approval。
Route 会防御性拒绝任何非自身绑定 session 的 outbound payload。
InteractionRuntime.handle() 会在 runner 解析出最终 session 后绑定 inbound route。
/new、/resume 和 session switch 路径必须重新绑定到新的 session。
Durable channel ownership 与 live routes 分开记录。Runtime 会保存
(core_id, channel, conversation_key) -> session_id,其中 conversation_key
是 host-owned external route key,例如 telegram:dm:123、
slack:channel:T1:C1:thread:123.4 或
matrix:room:%21room%3Aexample。Channel adapters 必须通过共享 runtime helper
构造这些 key,不要手写字符串。Canonical 格式有意不兼容旧 binding,例如
telegram:123;旧 channel conversation 会重新开始,除非用户显式 resume。
Channel /resume 同时是 live route switch 和 durable conversation rebind。
用户从 external channel 恢复某个 session 后,同一 conversation_key 的后续
inbound messages 会继续进入该 resumed session。
Channels
Channel adapters 会把 delivery 适配为平台特定消息。它们不再作为嵌套 work
继承的 ambient bridge。若某个 session 没有 active route,普通 delivery 和 tool
lifecycle item 会被标记为 unrouted,不会作为 InteractionRuntime.handle() 的
pending fallback output 返回。
如果 bridge delivery 在 history 已写入后失败,history row 会保持 durable。
Non-text delivery 使用 write_history=True 时必须提供显式 history_text;host
不会发明 artifact placeholder text。可选的 failure_history_text 可以在首次失败时
替换 history row。后续 retry status update 不能重写该 body。
Host 会在平台 I/O 开始前 claim delivery。若进程在 sending 之后、平台结果持久化
之前崩溃,recovery 会把该 delivery 标记为 unknown,而不是自动重放。能与平台状态
对账的 channel 可以解析 unknown;否则它保持为 operator-visible state。
Subagents
Child agent runs 使用独立的 session_child_* sessions。Router 不知道 parent/child
lineage。Child ordinary output 和 tool lifecycle delivery 只进入显式绑定到 child
session 的 route。没有这样的 route 时,它们是 unrouted;parent 只能通过显式
AgentRunResult、task completion 或未来 observability events 看到 child。
边界
不要让 output modules 直接写 session history 或 channel state。 不要按 parent/child relationship、conversation key 或 ambient adapter state 路由 delivery。