跳到主要内容

Runtime Control Plane v2

本文记录破坏性 runtime 重构的实现契约。Host 拥有 harness。Agent Core 拥有 agent/ 下的 authored files。

Module Shape

新的 deep modules 是:

  • RuntimeStore:SQLite event store 和 projection surface。
  • RuntimeControlPlane:host-owned detached task seam。
  • SessionRuntime:session admission 和 session/turn/message projections。
  • TurnEngine:一个 Agent Core foreground turn 的 provider/tool loop。
  • SlotRuntime:按 phase 执行 authored slot callable。

detached-work task ledger 模型是:

TaskSpec -> Task -> Event -> Projection

可作为 task 观察的 detached host work 通过 RuntimeControlPlane.submit_task() 进入。当前 task-ledger kinds 是 agent.spawnterminal.execevolver.runschedule.fire。Foreground Agent Core turn 不会成为 task row;它们通过 SessionRuntime 投影为 turns 和 messages。Delivery、approval、tool-call、MCP、state 和 artifact facts 应该使用 自己的 projections 或 runtime events,而不是伪装成 task submission。

Storage

runtime database 是 ~/.demiurge/runtime/runtime.sqlite3。它使用 Python stdlib sqlite3 和 WAL mode。旧的 JSON/JSONL session、scheduler 和 background-task state 不迁移。restart 后发现的 in-progress subprocess work 必须标记为 lostinterrupted;host 不能在 crash 后 replay dangerous effects。

Agent Slot Layout

每个 bootstrap、input 和 output slot 在自己的 slot directory 中拥有一个 slot.yaml manifest。agent/pipelines.yaml 是唯一的 phase ordering graph:

schema_version: 1
bootstrap:
serial: []
input:
serial: [base_input]
parallel: []
output:
serial: [base_output]
parallel: []

Slot code 和 metadata 保存在 typed folders 中:

agent/bootstrap/<slot_id>/module.py
agent/bootstrap/<slot_id>/slot.yaml
agent/input/<slot_id>/module.py
agent/input/<slot_id>/slot.yaml
agent/output/<slot_id>/module.py
agent/output/<slot_id>/slot.yaml

base_inputbase_output 是普通可编辑 seed slots。Host 不把它们当作 built-ins,loader 也不要求这些 id 存在。

Input slots 构建当前 model context。ctx.input.raw_text 是 read-only。 Slots 使用 ctx.input.add_context(text, role="user"|"system", write_history=...)。Output slots 读取 ctx.output.response_text 并使用 ctx.output.send_*。面向作者的 delivery timing 参数已移除:每次 send 都会 立即记录一个 delivery intent。

Serial slots 可以影响主流程。Parallel slots 是 non-blocking background side-effect lanes,不能修改 prompt、assistant response 或 session history。

Current Implementation Slice

runtime store 现在是 sessions、turns、messages、foreground tool-call records、 task status、task logs、scheduler instances、artifacts、delivery outbox rows、 runtime work items 和 unique channel conversation bindings 的 hot-path source of truth。Foreground tool-call records 由当前 turn_id 和 model-loop step_id 标识;它们不是 task facts。旧安装留下的 JSON session 和 scheduler files 可能仍在磁盘上,但 runtime code 不读取、不迁移,也不 dual-write 它们。

RuntimeTaskWorker 是 active subprocess、terminal、evolver 和 child-agent work 的 live worker。它只在内存中保存 non-durable process handles、cancel callbacks 和 live completion subscribers。Public task reads、lists、logs、 waits、cancellation results 和 pending completion notifications 都从 RuntimeControlPlane / SQLite projections 与 runtime events 重建。

Terminal task handle 还携带 Host-issued process spawn_id 与 OS process-start marker;durable metadata 包含 PID/process-group/platform identity,但绝不会单独用这些 metadata 杀进程。并发 cancellation 是 single-flight:一个 cleanup callback 会先把 return code/exit reason 提交到唯一 terminal event,再发布 completion-ready,所有 caller 观察同一结果。DemiurgeApp.close() 会在 关闭 tool resource 前 cancel/drain active runtime task 与已登记的 foreground terminal owner。 Task admission 会在 shutdown snapshot active work 之前关闭,因此 late approval 不能在 drain 期间启动新的 background process。Output-persistence failure 也会先清理 process tree,再提交 task.failed。完整 terminal stream 会持久化为 artifact row/file;task log 与 model result 仍是有界 projection。

BackgroundWorkRuntime 跟踪 parallel slots 和 delivery dispatch 创建的 in-process background coroutines。它把这些 local tasks 与 durable RuntimeTaskWorker 组合起来提供 drain 和 active-count 行为;foreground runner 不再拥有单独的 background-task ledger。

DeliveryRuntime claim 匹配的 durable work item 后,通过 session-scoped interaction router dispatch queued delivery intents。Outbox lifecycle 是 queued -> sending -> sent/failed/unknown/unroutedunrouted 表示该 delivery session 没有绑定 live route;failed 表示 route 存在但 adapter delivery 抛错。 Delivery failure 可以用显式 failure history text 更新此前已持久化的 history row, 但 retries 不能重写原始 history body。

SessionTurnStepRunner 现在委托:

  • session creation、update、turn lifecycle 和 message persistence 给 SessionRuntime
  • foreground turn admission,包括 session/core resolution、route binding、 revision/capability pinning 与 turn begin,给 TurnAdmissionRuntime
  • authored input -> model/tool -> output execution、captured-route context、 bootstrap、owner-checked cancellation、delivery drain 与最终 cleanup 给 TurnExecution
  • foreground input record、assistant output record、display turn、completion 与 interruption 给 TurnPersistenceRuntime
  • provider/tool loop execution 给 TurnEngine
  • authored bootstrap/input/output slot callable loading 和 invocation 给 SlotRuntime

面向 model 的 delegation tools 是:

  • delegate_task(goal, core_id=None, context_mode="isolated", notify_policy="return_to_parent", tool_policy=None, max_depth=None)
  • task_list(kind=None),限定当前 session;
  • task_status(task_id, view="model")
  • task_control(task_id, command="cancel")
  • yield_until(task_id, timeout_seconds=30)

delegate_task 当前支持 isolatedfork context modes,执行默认 depth 和 child-count limits,并在 visible-tool construction 和 dispatch 阶段应用 child tool_policy filters。notify_policy 只接受 return_to_parentsilent; 前者会发出 completion event,后者会抑制它。Child output 默认作为 parent 的 evidence。

Foreground turn 不能通过 control-plane projection 按 task id 读取。它们通过 session、turn、message、event-log 和 runtime-event projections 保持可追踪。 面向 model 的 task tools 只操作 detached background task kinds,所以普通 turn 不会出现在 task_list 中,也不支持 task_statustask_controlyield_until