【deepseek-harness】DeepSeek Harness Agent Loop 模块深度架构分析之一

DeepSeek Harness Agent Loop 模块深度架构分析

代码仓库 :github.com/deepseek-harness

分析范围 :packages/core/agent-loop/ + packages/core/agent/(Agent 接口与驱动器)

分析维度 :设计实现 · 循环机制 · Turn/Step/Round 三层嵌套 · 事件分发 · 工具调度 · 数据流

文档版本 :v1.0

分析日期:2025-09-03


目录

  • [1. 概述与定位](#1. 概述与定位)
  • [2. 包结构与源码索引](#2. 包结构与源码索引)
  • [3. Agent 接口设计(core/agent)](#3. Agent 接口设计(core/agent))
    • [3.1 Agent 接口定义](#3.1 Agent 接口定义)
    • [3.2 三种输入投递方式](#3.2 三种输入投递方式)
    • [3.3 Inbox 消息队列系统](#3.3 Inbox 消息队列系统)
    • [3.4 Agent 事件体系](#3.4 Agent 事件体系)
    • [3.5 Dispatch 融合分发器](#3.5 Dispatch 融合分发器)
    • [3.6 ConsumedWork 工作审计](#3.6 ConsumedWork 工作审计)
  • [4. ReactLoopAgent 驱动器实现(core/agent-loop/agent.ts)](#4. ReactLoopAgent 驱动器实现(core/agent-loop/agent.ts))
    • [4.1 类结构与状态机](#4.1 类结构与状态机)
    • [4.2 Phase 三态状态机](#4.2 Phase 三态状态机)
    • [4.3 wakeDriver 驱动唤醒](#4.3 wakeDriver 驱动唤醒)
    • [4.4 kick 驱动主循环](#4.4 kick 驱动主循环)
    • [4.5 turn 方法------Turn 级循环](#4.5 turn 方法——Turn 级循环)
    • [4.6 preStep 方法------Step 前置处理](#4.6 preStep 方法——Step 前置处理)
    • [4.7 step 方法------Step 级行执行](#4.7 step 方法——Step 级行执行)
    • [4.8 buildRequest 方法------请求构建](#4.8 buildRequest 方法——请求构建)
    • [4.9 RuntimeContextProjection 运行时上下文投影](#4.9 RuntimeContextProjection 运行时上下文投影)
  • [5. 工具调用调度(tool-calls.ts)](#5. 工具调用调度(tool-calls.ts))
    • [5.1 调度模型:独占屏障 + 并行池](#5.1 调度模型:独占屏障 + 并行池)
    • [5.2 executeToolCalls 主入口](#5.2 executeToolCalls 主入口)
    • [5.3 runGroup 组调度核心](#5.3 runGroup 组调度核心)
    • [5.4 有序提交与模型顺序保证](#5.4 有序提交与模型顺序保证)
    • [5.5 中断与失败处理](#5.5 中断与失败处理)
  • [6. AgentLoop 工厂服务(index.ts)](#6. AgentLoop 工厂服务(index.ts))
    • [6.1 AgentLoop 服务类](#6.1 AgentLoop 服务类)
    • [6.2 FactoryOwnership 工厂级生命周期管理](#6.2 FactoryOwnership 工厂级生命周期管理)
    • [6.3 prepare 方法------Agent 准备](#6.3 prepare 方法——Agent 准备)
    • [6.4 create / createAgent / resume 三种创建路径](#6.4 create / createAgent / resume 三种创建路径)
    • [6.5 配置体系与声明式 Agent](#6.5 配置体系与声明式 Agent)
  • [7. Invariant 请求重建不变量](#7. Invariant 请求重建不变量)
  • [8. 循环机制全景](#8. 循环机制全景)
    • [8.1 三层嵌套循环模型](#8.1 三层嵌套循环模型)
    • [8.2 Turn 完整流程时序图](#8.2 Turn 完整流程时序图)
    • [8.3 Step 完整流程时序图](#8.3 Step 完整流程时序图)
    • [8.4 Turn 结束决策逻辑](#8.4 Turn 结束决策逻辑)
    • [8.5 连续 Turn 驱动机制](#8.5 连续 Turn 驱动机制)
  • [9. 数据流分析](#9. 数据流分析)
  • [10. 模块间调用关系](#10. 模块间调用关系)
  • [11. 避坑指南](#11. 避坑指南)

1. 概述与定位

1.1 什么是 Agent Loop

Agent Loop 是 DeepSeek Harness 的运行时心脏------它驱动 Agent 与 LLM 的完整交互循环。每一次用户输入、每一次模型请求、每一次工具调用,都由 Agent Loop 编排。

Agent Loop 实现了三层嵌套循环模型:

层级 名称 含义 持久事件
Turn 一次输入排空 从 claim 首个输入到模型和工具停止 turn/start、turn/end
Step 一次模型请求 + 工具调用 Turn 内的一次完整 LLM 交互 step/start、step/end、user/message、assistant/*、tool/*
Round 外层策略迭代 如 goal round(跨 Turn 的自动续行) 由 goal-round-driver 管理,非 Loop 内置

1.2 核心设计原则

#mermaid-svg-5yQlcVX3g4s58oTn{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-5yQlcVX3g4s58oTn .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-5yQlcVX3g4s58oTn .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-5yQlcVX3g4s58oTn .error-icon{fill:#552222;}#mermaid-svg-5yQlcVX3g4s58oTn .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-5yQlcVX3g4s58oTn .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-5yQlcVX3g4s58oTn .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-5yQlcVX3g4s58oTn .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-5yQlcVX3g4s58oTn .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-5yQlcVX3g4s58oTn .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-5yQlcVX3g4s58oTn .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-5yQlcVX3g4s58oTn .marker{fill:#333333;stroke:#333333;}#mermaid-svg-5yQlcVX3g4s58oTn .marker.cross{stroke:#333333;}#mermaid-svg-5yQlcVX3g4s58oTn svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-5yQlcVX3g4s58oTn p{margin:0;}#mermaid-svg-5yQlcVX3g4s58oTn .edge{stroke-width:3;}#mermaid-svg-5yQlcVX3g4s58oTn .section--1 rect,#mermaid-svg-5yQlcVX3g4s58oTn .section--1 path,#mermaid-svg-5yQlcVX3g4s58oTn .section--1 circle,#mermaid-svg-5yQlcVX3g4s58oTn .section--1 polygon,#mermaid-svg-5yQlcVX3g4s58oTn .section--1 path{fill:hsl(240, 100%, 76.2745098039%);}#mermaid-svg-5yQlcVX3g4s58oTn .section--1 text{fill:#ffffff;}#mermaid-svg-5yQlcVX3g4s58oTn .node-icon--1{font-size:40px;color:#ffffff;}#mermaid-svg-5yQlcVX3g4s58oTn .section-edge--1{stroke:hsl(240, 100%, 76.2745098039%);}#mermaid-svg-5yQlcVX3g4s58oTn .edge-depth--1{stroke-width:17;}#mermaid-svg-5yQlcVX3g4s58oTn .section--1 line{stroke:hsl(60, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-5yQlcVX3g4s58oTn .disabled,#mermaid-svg-5yQlcVX3g4s58oTn .disabled circle,#mermaid-svg-5yQlcVX3g4s58oTn .disabled text{fill:lightgray;}#mermaid-svg-5yQlcVX3g4s58oTn .disabled text{fill:#efefef;}#mermaid-svg-5yQlcVX3g4s58oTn .section-0 rect,#mermaid-svg-5yQlcVX3g4s58oTn .section-0 path,#mermaid-svg-5yQlcVX3g4s58oTn .section-0 circle,#mermaid-svg-5yQlcVX3g4s58oTn .section-0 polygon,#mermaid-svg-5yQlcVX3g4s58oTn .section-0 path{fill:hsl(60, 100%, 73.5294117647%);}#mermaid-svg-5yQlcVX3g4s58oTn .section-0 text{fill:black;}#mermaid-svg-5yQlcVX3g4s58oTn .node-icon-0{font-size:40px;color:black;}#mermaid-svg-5yQlcVX3g4s58oTn .section-edge-0{stroke:hsl(60, 100%, 73.5294117647%);}#mermaid-svg-5yQlcVX3g4s58oTn .edge-depth-0{stroke-width:14;}#mermaid-svg-5yQlcVX3g4s58oTn .section-0 line{stroke:hsl(240, 100%, 83.5294117647%);stroke-width:3;}#mermaid-svg-5yQlcVX3g4s58oTn .disabled,#mermaid-svg-5yQlcVX3g4s58oTn .disabled circle,#mermaid-svg-5yQlcVX3g4s58oTn .disabled text{fill:lightgray;}#mermaid-svg-5yQlcVX3g4s58oTn .disabled text{fill:#efefef;}#mermaid-svg-5yQlcVX3g4s58oTn .section-1 rect,#mermaid-svg-5yQlcVX3g4s58oTn .section-1 path,#mermaid-svg-5yQlcVX3g4s58oTn .section-1 circle,#mermaid-svg-5yQlcVX3g4s58oTn .section-1 polygon,#mermaid-svg-5yQlcVX3g4s58oTn .section-1 path{fill:hsl(80, 100%, 76.2745098039%);}#mermaid-svg-5yQlcVX3g4s58oTn .section-1 text{fill:black;}#mermaid-svg-5yQlcVX3g4s58oTn .node-icon-1{font-size:40px;color:black;}#mermaid-svg-5yQlcVX3g4s58oTn .section-edge-1{stroke:hsl(80, 100%, 76.2745098039%);}#mermaid-svg-5yQlcVX3g4s58oTn .edge-depth-1{stroke-width:11;}#mermaid-svg-5yQlcVX3g4s58oTn .section-1 line{stroke:hsl(260, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-5yQlcVX3g4s58oTn .disabled,#mermaid-svg-5yQlcVX3g4s58oTn .disabled circle,#mermaid-svg-5yQlcVX3g4s58oTn .disabled text{fill:lightgray;}#mermaid-svg-5yQlcVX3g4s58oTn .disabled text{fill:#efefef;}#mermaid-svg-5yQlcVX3g4s58oTn .section-2 rect,#mermaid-svg-5yQlcVX3g4s58oTn .section-2 path,#mermaid-svg-5yQlcVX3g4s58oTn .section-2 circle,#mermaid-svg-5yQlcVX3g4s58oTn .section-2 polygon,#mermaid-svg-5yQlcVX3g4s58oTn .section-2 path{fill:hsl(270, 100%, 76.2745098039%);}#mermaid-svg-5yQlcVX3g4s58oTn .section-2 text{fill:#ffffff;}#mermaid-svg-5yQlcVX3g4s58oTn .node-icon-2{font-size:40px;color:#ffffff;}#mermaid-svg-5yQlcVX3g4s58oTn .section-edge-2{stroke:hsl(270, 100%, 76.2745098039%);}#mermaid-svg-5yQlcVX3g4s58oTn .edge-depth-2{stroke-width:8;}#mermaid-svg-5yQlcVX3g4s58oTn .section-2 line{stroke:hsl(90, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-5yQlcVX3g4s58oTn .disabled,#mermaid-svg-5yQlcVX3g4s58oTn .disabled circle,#mermaid-svg-5yQlcVX3g4s58oTn .disabled text{fill:lightgray;}#mermaid-svg-5yQlcVX3g4s58oTn .disabled text{fill:#efefef;}#mermaid-svg-5yQlcVX3g4s58oTn .section-3 rect,#mermaid-svg-5yQlcVX3g4s58oTn .section-3 path,#mermaid-svg-5yQlcVX3g4s58oTn .section-3 circle,#mermaid-svg-5yQlcVX3g4s58oTn .section-3 polygon,#mermaid-svg-5yQlcVX3g4s58oTn .section-3 path{fill:hsl(300, 100%, 76.2745098039%);}#mermaid-svg-5yQlcVX3g4s58oTn .section-3 text{fill:black;}#mermaid-svg-5yQlcVX3g4s58oTn .node-icon-3{font-size:40px;color:black;}#mermaid-svg-5yQlcVX3g4s58oTn .section-edge-3{stroke:hsl(300, 100%, 76.2745098039%);}#mermaid-svg-5yQlcVX3g4s58oTn .edge-depth-3{stroke-width:5;}#mermaid-svg-5yQlcVX3g4s58oTn .section-3 line{stroke:hsl(120, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-5yQlcVX3g4s58oTn .disabled,#mermaid-svg-5yQlcVX3g4s58oTn .disabled circle,#mermaid-svg-5yQlcVX3g4s58oTn .disabled text{fill:lightgray;}#mermaid-svg-5yQlcVX3g4s58oTn .disabled text{fill:#efefef;}#mermaid-svg-5yQlcVX3g4s58oTn .section-4 rect,#mermaid-svg-5yQlcVX3g4s58oTn .section-4 path,#mermaid-svg-5yQlcVX3g4s58oTn .section-4 circle,#mermaid-svg-5yQlcVX3g4s58oTn .section-4 polygon,#mermaid-svg-5yQlcVX3g4s58oTn .section-4 path{fill:hsl(330, 100%, 76.2745098039%);}#mermaid-svg-5yQlcVX3g4s58oTn .section-4 text{fill:black;}#mermaid-svg-5yQlcVX3g4s58oTn .node-icon-4{font-size:40px;color:black;}#mermaid-svg-5yQlcVX3g4s58oTn .section-edge-4{stroke:hsl(330, 100%, 76.2745098039%);}#mermaid-svg-5yQlcVX3g4s58oTn .edge-depth-4{stroke-width:2;}#mermaid-svg-5yQlcVX3g4s58oTn .section-4 line{stroke:hsl(150, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-5yQlcVX3g4s58oTn .disabled,#mermaid-svg-5yQlcVX3g4s58oTn .disabled circle,#mermaid-svg-5yQlcVX3g4s58oTn .disabled text{fill:lightgray;}#mermaid-svg-5yQlcVX3g4s58oTn .disabled text{fill:#efefef;}#mermaid-svg-5yQlcVX3g4s58oTn .section-5 rect,#mermaid-svg-5yQlcVX3g4s58oTn .section-5 path,#mermaid-svg-5yQlcVX3g4s58oTn .section-5 circle,#mermaid-svg-5yQlcVX3g4s58oTn .section-5 polygon,#mermaid-svg-5yQlcVX3g4s58oTn .section-5 path{fill:hsl(0, 100%, 76.2745098039%);}#mermaid-svg-5yQlcVX3g4s58oTn .section-5 text{fill:black;}#mermaid-svg-5yQlcVX3g4s58oTn .node-icon-5{font-size:40px;color:black;}#mermaid-svg-5yQlcVX3g4s58oTn .section-edge-5{stroke:hsl(0, 100%, 76.2745098039%);}#mermaid-svg-5yQlcVX3g4s58oTn .edge-depth-5{stroke-width:-1;}#mermaid-svg-5yQlcVX3g4s58oTn .section-5 line{stroke:hsl(180, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-5yQlcVX3g4s58oTn .disabled,#mermaid-svg-5yQlcVX3g4s58oTn .disabled circle,#mermaid-svg-5yQlcVX3g4s58oTn .disabled text{fill:lightgray;}#mermaid-svg-5yQlcVX3g4s58oTn .disabled text{fill:#efefef;}#mermaid-svg-5yQlcVX3g4s58oTn .section-6 rect,#mermaid-svg-5yQlcVX3g4s58oTn .section-6 path,#mermaid-svg-5yQlcVX3g4s58oTn .section-6 circle,#mermaid-svg-5yQlcVX3g4s58oTn .section-6 polygon,#mermaid-svg-5yQlcVX3g4s58oTn .section-6 path{fill:hsl(30, 100%, 76.2745098039%);}#mermaid-svg-5yQlcVX3g4s58oTn .section-6 text{fill:black;}#mermaid-svg-5yQlcVX3g4s58oTn .node-icon-6{font-size:40px;color:black;}#mermaid-svg-5yQlcVX3g4s58oTn .section-edge-6{stroke:hsl(30, 100%, 76.2745098039%);}#mermaid-svg-5yQlcVX3g4s58oTn .edge-depth-6{stroke-width:-4;}#mermaid-svg-5yQlcVX3g4s58oTn .section-6 line{stroke:hsl(210, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-5yQlcVX3g4s58oTn .disabled,#mermaid-svg-5yQlcVX3g4s58oTn .disabled circle,#mermaid-svg-5yQlcVX3g4s58oTn .disabled text{fill:lightgray;}#mermaid-svg-5yQlcVX3g4s58oTn .disabled text{fill:#efefef;}#mermaid-svg-5yQlcVX3g4s58oTn .section-7 rect,#mermaid-svg-5yQlcVX3g4s58oTn .section-7 path,#mermaid-svg-5yQlcVX3g4s58oTn .section-7 circle,#mermaid-svg-5yQlcVX3g4s58oTn .section-7 polygon,#mermaid-svg-5yQlcVX3g4s58oTn .section-7 path{fill:hsl(90, 100%, 76.2745098039%);}#mermaid-svg-5yQlcVX3g4s58oTn .section-7 text{fill:black;}#mermaid-svg-5yQlcVX3g4s58oTn .node-icon-7{font-size:40px;color:black;}#mermaid-svg-5yQlcVX3g4s58oTn .section-edge-7{stroke:hsl(90, 100%, 76.2745098039%);}#mermaid-svg-5yQlcVX3g4s58oTn .edge-depth-7{stroke-width:-7;}#mermaid-svg-5yQlcVX3g4s58oTn .section-7 line{stroke:hsl(270, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-5yQlcVX3g4s58oTn .disabled,#mermaid-svg-5yQlcVX3g4s58oTn .disabled circle,#mermaid-svg-5yQlcVX3g4s58oTn .disabled text{fill:lightgray;}#mermaid-svg-5yQlcVX3g4s58oTn .disabled text{fill:#efefef;}#mermaid-svg-5yQlcVX3g4s58oTn .section-8 rect,#mermaid-svg-5yQlcVX3g4s58oTn .section-8 path,#mermaid-svg-5yQlcVX3g4s58oTn .section-8 circle,#mermaid-svg-5yQlcVX3g4s58oTn .section-8 polygon,#mermaid-svg-5yQlcVX3g4s58oTn .section-8 path{fill:hsl(150, 100%, 76.2745098039%);}#mermaid-svg-5yQlcVX3g4s58oTn .section-8 text{fill:black;}#mermaid-svg-5yQlcVX3g4s58oTn .node-icon-8{font-size:40px;color:black;}#mermaid-svg-5yQlcVX3g4s58oTn .section-edge-8{stroke:hsl(150, 100%, 76.2745098039%);}#mermaid-svg-5yQlcVX3g4s58oTn .edge-depth-8{stroke-width:-10;}#mermaid-svg-5yQlcVX3g4s58oTn .section-8 line{stroke:hsl(330, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-5yQlcVX3g4s58oTn .disabled,#mermaid-svg-5yQlcVX3g4s58oTn .disabled circle,#mermaid-svg-5yQlcVX3g4s58oTn .disabled text{fill:lightgray;}#mermaid-svg-5yQlcVX3g4s58oTn .disabled text{fill:#efefef;}#mermaid-svg-5yQlcVX3g4s58oTn .section-9 rect,#mermaid-svg-5yQlcVX3g4s58oTn .section-9 path,#mermaid-svg-5yQlcVX3g4s58oTn .section-9 circle,#mermaid-svg-5yQlcVX3g4s58oTn .section-9 polygon,#mermaid-svg-5yQlcVX3g4s58oTn .section-9 path{fill:hsl(180, 100%, 76.2745098039%);}#mermaid-svg-5yQlcVX3g4s58oTn .section-9 text{fill:black;}#mermaid-svg-5yQlcVX3g4s58oTn .node-icon-9{font-size:40px;color:black;}#mermaid-svg-5yQlcVX3g4s58oTn .section-edge-9{stroke:hsl(180, 100%, 76.2745098039%);}#mermaid-svg-5yQlcVX3g4s58oTn .edge-depth-9{stroke-width:-13;}#mermaid-svg-5yQlcVX3g4s58oTn .section-9 line{stroke:hsl(0, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-5yQlcVX3g4s58oTn .disabled,#mermaid-svg-5yQlcVX3g4s58oTn .disabled circle,#mermaid-svg-5yQlcVX3g4s58oTn .disabled text{fill:lightgray;}#mermaid-svg-5yQlcVX3g4s58oTn .disabled text{fill:#efefef;}#mermaid-svg-5yQlcVX3g4s58oTn .section-10 rect,#mermaid-svg-5yQlcVX3g4s58oTn .section-10 path,#mermaid-svg-5yQlcVX3g4s58oTn .section-10 circle,#mermaid-svg-5yQlcVX3g4s58oTn .section-10 polygon,#mermaid-svg-5yQlcVX3g4s58oTn .section-10 path{fill:hsl(210, 100%, 76.2745098039%);}#mermaid-svg-5yQlcVX3g4s58oTn .section-10 text{fill:black;}#mermaid-svg-5yQlcVX3g4s58oTn .node-icon-10{font-size:40px;color:black;}#mermaid-svg-5yQlcVX3g4s58oTn .section-edge-10{stroke:hsl(210, 100%, 76.2745098039%);}#mermaid-svg-5yQlcVX3g4s58oTn .edge-depth-10{stroke-width:-16;}#mermaid-svg-5yQlcVX3g4s58oTn .section-10 line{stroke:hsl(30, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-5yQlcVX3g4s58oTn .disabled,#mermaid-svg-5yQlcVX3g4s58oTn .disabled circle,#mermaid-svg-5yQlcVX3g4s58oTn .disabled text{fill:lightgray;}#mermaid-svg-5yQlcVX3g4s58oTn .disabled text{fill:#efefef;}#mermaid-svg-5yQlcVX3g4s58oTn .section-root rect,#mermaid-svg-5yQlcVX3g4s58oTn .section-root path,#mermaid-svg-5yQlcVX3g4s58oTn .section-root circle,#mermaid-svg-5yQlcVX3g4s58oTn .section-root polygon{fill:hsl(240, 100%, 46.2745098039%);}#mermaid-svg-5yQlcVX3g4s58oTn .section-root text{fill:#ffffff;}#mermaid-svg-5yQlcVX3g4s58oTn .section-root span{color:#ffffff;}#mermaid-svg-5yQlcVX3g4s58oTn .section-2 span{color:#ffffff;}#mermaid-svg-5yQlcVX3g4s58oTn .icon-container{height:100%;display:flex;justify-content:center;align-items:center;}#mermaid-svg-5yQlcVX3g4s58oTn .edge{fill:none;}#mermaid-svg-5yQlcVX3g4s58oTn .mindmap-node-label{dy:1em;alignment-baseline:middle;text-anchor:middle;dominant-baseline:middle;text-align:center;}#mermaid-svg-5yQlcVX3g4s58oTn :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Agent Loop

设计原则
日志即真相
每个请求从 Session 日志派生
deriveMessages 投射模型历史
模型可见即已记录
运行时不变量断言
事件驱动扩展
agent/pre-step Waterfall
agent/request Waterfall
agent/request-error Waterfall
agent/turn-stopping Serial
agent/status emit
单一 Inbox
next-turn 等待新 Turn
next-step 等待下一个 Step
claim 时合并两个队列
去重验证
可取消性
每个活动有 AbortController
cancel 清空 inbox + abort
disposed 原因特殊处理
中断后保留已产出内容
有序提交
工具结果按模型顺序提交
独占调用形成屏障
并行调用有界滚动池
额外上下文 FIFO 注入
可重建性
请求 frozen + sessionId
request/header 持久化
llm/stream 不变量校验
派生消息 vs 实际消息比对

1.3 两个包的关系

#mermaid-svg-ulei9Yca4Sy5E81z{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-ulei9Yca4Sy5E81z .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-ulei9Yca4Sy5E81z .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-ulei9Yca4Sy5E81z .error-icon{fill:#552222;}#mermaid-svg-ulei9Yca4Sy5E81z .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-ulei9Yca4Sy5E81z .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-ulei9Yca4Sy5E81z .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-ulei9Yca4Sy5E81z .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-ulei9Yca4Sy5E81z .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-ulei9Yca4Sy5E81z .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-ulei9Yca4Sy5E81z .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-ulei9Yca4Sy5E81z .marker{fill:#333333;stroke:#333333;}#mermaid-svg-ulei9Yca4Sy5E81z .marker.cross{stroke:#333333;}#mermaid-svg-ulei9Yca4Sy5E81z svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-ulei9Yca4Sy5E81z p{margin:0;}#mermaid-svg-ulei9Yca4Sy5E81z .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-ulei9Yca4Sy5E81z .cluster-label text{fill:#333;}#mermaid-svg-ulei9Yca4Sy5E81z .cluster-label span{color:#333;}#mermaid-svg-ulei9Yca4Sy5E81z .cluster-label span p{background-color:transparent;}#mermaid-svg-ulei9Yca4Sy5E81z .label text,#mermaid-svg-ulei9Yca4Sy5E81z span{fill:#333;color:#333;}#mermaid-svg-ulei9Yca4Sy5E81z .node rect,#mermaid-svg-ulei9Yca4Sy5E81z .node circle,#mermaid-svg-ulei9Yca4Sy5E81z .node ellipse,#mermaid-svg-ulei9Yca4Sy5E81z .node polygon,#mermaid-svg-ulei9Yca4Sy5E81z .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-ulei9Yca4Sy5E81z .rough-node .label text,#mermaid-svg-ulei9Yca4Sy5E81z .node .label text,#mermaid-svg-ulei9Yca4Sy5E81z .image-shape .label,#mermaid-svg-ulei9Yca4Sy5E81z .icon-shape .label{text-anchor:middle;}#mermaid-svg-ulei9Yca4Sy5E81z .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-ulei9Yca4Sy5E81z .rough-node .label,#mermaid-svg-ulei9Yca4Sy5E81z .node .label,#mermaid-svg-ulei9Yca4Sy5E81z .image-shape .label,#mermaid-svg-ulei9Yca4Sy5E81z .icon-shape .label{text-align:center;}#mermaid-svg-ulei9Yca4Sy5E81z .node.clickable{cursor:pointer;}#mermaid-svg-ulei9Yca4Sy5E81z .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-ulei9Yca4Sy5E81z .arrowheadPath{fill:#333333;}#mermaid-svg-ulei9Yca4Sy5E81z .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-ulei9Yca4Sy5E81z .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-ulei9Yca4Sy5E81z .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ulei9Yca4Sy5E81z .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-ulei9Yca4Sy5E81z .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ulei9Yca4Sy5E81z .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-ulei9Yca4Sy5E81z .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-ulei9Yca4Sy5E81z .cluster text{fill:#333;}#mermaid-svg-ulei9Yca4Sy5E81z .cluster span{color:#333;}#mermaid-svg-ulei9Yca4Sy5E81z div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-ulei9Yca4Sy5E81z .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-ulei9Yca4Sy5E81z rect.text{fill:none;stroke-width:0;}#mermaid-svg-ulei9Yca4Sy5E81z .icon-shape,#mermaid-svg-ulei9Yca4Sy5E81z .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ulei9Yca4Sy5E81z .icon-shape p,#mermaid-svg-ulei9Yca4Sy5E81z .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-ulei9Yca4Sy5E81z .icon-shape .label rect,#mermaid-svg-ulei9Yca4Sy5E81z .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ulei9Yca4Sy5E81z .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-ulei9Yca4Sy5E81z .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-ulei9Yca4Sy5E81z :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} packages/core/agent-loop/ --- 驱动器实现层
packages/core/agent/ --- Agent 接口层
被实现
setFactory
被使用
被构建
被分发
Agent 接口

followup / steer / inject / cancel
AgentRegistry

Agent 注册表与工厂委托
Inbox

消息队列 + 持久 splice
AgentEventDispatch

融合事件分发器
Agent 事件定义

agent/* 全部事件类型
ConsumedWork

工作审计折叠
ModelSelection

模型切换
ReactLoopAgent

默认 Agent 驱动器
AgentLoop

工厂服务 + 生命周期
tool-calls.ts

工具调用调度器
RuntimeContextProjection

运行时上下文投影
invariant.ts

请求重建不变量
constants.ts

默认值

  • core/agent :定义 Agent 接口、事件类型、Inbox 队列和分发器------是抽象层
  • core/agent-loop :实现 Agent 驱动器 ReactLoopAgent、工厂服务 AgentLoop 和工具调度------是具体层

2. 包结构与源码索引

2.1 core/agent 包

复制代码
packages/core/agent/src/
├── index.ts           (706 行)  AgentRegistry 注册表 + Agent 接口导出 + Context 扩展
├── runtime-types.ts   (292 行)  Agent 接口完整定义 + 全部 agent/* 事件声明
├── inbox.ts           (220 行)  Inbox 消息队列 + 持久 splice + 去重验证
├── dispatch.ts        (176 行)  AgentEventDispatch 融合分发器 + scope carrier
├── consumed-work.ts   (108 行)  foldConsumedWork 工作审计折叠
├── model-selection.ts ( 75 行)  模型选择 + prompt/request 联动
├── types.ts           ( 27 行)  InboxTarget 类型 + AgentCancelCause 重导出
└── invariant.ts       ( 32 行)  空不变量注册(伴生插件占位)

2.2 core/agent-loop 包

复制代码
packages/core/agent-loop/src/
├── index.ts           (713 行)  AgentLoop 工厂服务 + FactoryOwnership + create/resume
├── agent.ts           (515 行)  ReactLoopAgent 驱动器 + turn/step/buildRequest
├── tool-calls.ts      (289 行)  executeToolCalls + runGroup + 有序提交 + 中断处理
├── runtime-context.ts ( 76 行)  RuntimeContextProjection 动态上下文投影
├── invariant.ts       ( 63 行)  请求重建不变量(llm/stream 拦截校验)
└── constants.ts       (  6 行)  DEFAULT_MAX_PARALLEL_TOOL_CALLS = 10

2.3 依赖关系

#mermaid-svg-d78vL2CvLe8o4Kum{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-d78vL2CvLe8o4Kum .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-d78vL2CvLe8o4Kum .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-d78vL2CvLe8o4Kum .error-icon{fill:#552222;}#mermaid-svg-d78vL2CvLe8o4Kum .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-d78vL2CvLe8o4Kum .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-d78vL2CvLe8o4Kum .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-d78vL2CvLe8o4Kum .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-d78vL2CvLe8o4Kum .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-d78vL2CvLe8o4Kum .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-d78vL2CvLe8o4Kum .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-d78vL2CvLe8o4Kum .marker{fill:#333333;stroke:#333333;}#mermaid-svg-d78vL2CvLe8o4Kum .marker.cross{stroke:#333333;}#mermaid-svg-d78vL2CvLe8o4Kum svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-d78vL2CvLe8o4Kum p{margin:0;}#mermaid-svg-d78vL2CvLe8o4Kum .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-d78vL2CvLe8o4Kum .cluster-label text{fill:#333;}#mermaid-svg-d78vL2CvLe8o4Kum .cluster-label span{color:#333;}#mermaid-svg-d78vL2CvLe8o4Kum .cluster-label span p{background-color:transparent;}#mermaid-svg-d78vL2CvLe8o4Kum .label text,#mermaid-svg-d78vL2CvLe8o4Kum span{fill:#333;color:#333;}#mermaid-svg-d78vL2CvLe8o4Kum .node rect,#mermaid-svg-d78vL2CvLe8o4Kum .node circle,#mermaid-svg-d78vL2CvLe8o4Kum .node ellipse,#mermaid-svg-d78vL2CvLe8o4Kum .node polygon,#mermaid-svg-d78vL2CvLe8o4Kum .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-d78vL2CvLe8o4Kum .rough-node .label text,#mermaid-svg-d78vL2CvLe8o4Kum .node .label text,#mermaid-svg-d78vL2CvLe8o4Kum .image-shape .label,#mermaid-svg-d78vL2CvLe8o4Kum .icon-shape .label{text-anchor:middle;}#mermaid-svg-d78vL2CvLe8o4Kum .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-d78vL2CvLe8o4Kum .rough-node .label,#mermaid-svg-d78vL2CvLe8o4Kum .node .label,#mermaid-svg-d78vL2CvLe8o4Kum .image-shape .label,#mermaid-svg-d78vL2CvLe8o4Kum .icon-shape .label{text-align:center;}#mermaid-svg-d78vL2CvLe8o4Kum .node.clickable{cursor:pointer;}#mermaid-svg-d78vL2CvLe8o4Kum .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-d78vL2CvLe8o4Kum .arrowheadPath{fill:#333333;}#mermaid-svg-d78vL2CvLe8o4Kum .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-d78vL2CvLe8o4Kum .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-d78vL2CvLe8o4Kum .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-d78vL2CvLe8o4Kum .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-d78vL2CvLe8o4Kum .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-d78vL2CvLe8o4Kum .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-d78vL2CvLe8o4Kum .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-d78vL2CvLe8o4Kum .cluster text{fill:#333;}#mermaid-svg-d78vL2CvLe8o4Kum .cluster span{color:#333;}#mermaid-svg-d78vL2CvLe8o4Kum div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-d78vL2CvLe8o4Kum .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-d78vL2CvLe8o4Kum rect.text{fill:none;stroke-width:0;}#mermaid-svg-d78vL2CvLe8o4Kum .icon-shape,#mermaid-svg-d78vL2CvLe8o4Kum .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-d78vL2CvLe8o4Kum .icon-shape p,#mermaid-svg-d78vL2CvLe8o4Kum .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-d78vL2CvLe8o4Kum .icon-shape .label rect,#mermaid-svg-d78vL2CvLe8o4Kum .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-d78vL2CvLe8o4Kum .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-d78vL2CvLe8o4Kum .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-d78vL2CvLe8o4Kum :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} dsh-scope

作用域原语
dsh-llm

LLM 适配器 + 消息类型
dsh-session

Session 日志 + 事件类型
dsh-system-prompt

提示组装
dsh-tools

工具注册表 + 执行管道
dsh-settings

设置系统
dsh-invariants

不变量注册
dsh-commands/brand

CommandId
core/agent

Agent 接口层
core/agent-loop

驱动器实现层


3. Agent 接口设计(core/agent)

3.1 Agent 接口定义

Agent 接口定义在 runtime-types.ts 中,是 Agent Loop 的核心抽象:
#mermaid-svg-li44pcflMGgnkFGw{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-li44pcflMGgnkFGw .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-li44pcflMGgnkFGw .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-li44pcflMGgnkFGw .error-icon{fill:#552222;}#mermaid-svg-li44pcflMGgnkFGw .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-li44pcflMGgnkFGw .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-li44pcflMGgnkFGw .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-li44pcflMGgnkFGw .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-li44pcflMGgnkFGw .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-li44pcflMGgnkFGw .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-li44pcflMGgnkFGw .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-li44pcflMGgnkFGw .marker{fill:#333333;stroke:#333333;}#mermaid-svg-li44pcflMGgnkFGw .marker.cross{stroke:#333333;}#mermaid-svg-li44pcflMGgnkFGw svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-li44pcflMGgnkFGw p{margin:0;}#mermaid-svg-li44pcflMGgnkFGw .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-li44pcflMGgnkFGw .cluster-label text{fill:#333;}#mermaid-svg-li44pcflMGgnkFGw .cluster-label span{color:#333;}#mermaid-svg-li44pcflMGgnkFGw .cluster-label span p{background-color:transparent;}#mermaid-svg-li44pcflMGgnkFGw .label text,#mermaid-svg-li44pcflMGgnkFGw span{fill:#333;color:#333;}#mermaid-svg-li44pcflMGgnkFGw .node rect,#mermaid-svg-li44pcflMGgnkFGw .node circle,#mermaid-svg-li44pcflMGgnkFGw .node ellipse,#mermaid-svg-li44pcflMGgnkFGw .node polygon,#mermaid-svg-li44pcflMGgnkFGw .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-li44pcflMGgnkFGw .rough-node .label text,#mermaid-svg-li44pcflMGgnkFGw .node .label text,#mermaid-svg-li44pcflMGgnkFGw .image-shape .label,#mermaid-svg-li44pcflMGgnkFGw .icon-shape .label{text-anchor:middle;}#mermaid-svg-li44pcflMGgnkFGw .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-li44pcflMGgnkFGw .rough-node .label,#mermaid-svg-li44pcflMGgnkFGw .node .label,#mermaid-svg-li44pcflMGgnkFGw .image-shape .label,#mermaid-svg-li44pcflMGgnkFGw .icon-shape .label{text-align:center;}#mermaid-svg-li44pcflMGgnkFGw .node.clickable{cursor:pointer;}#mermaid-svg-li44pcflMGgnkFGw .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-li44pcflMGgnkFGw .arrowheadPath{fill:#333333;}#mermaid-svg-li44pcflMGgnkFGw .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-li44pcflMGgnkFGw .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-li44pcflMGgnkFGw .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-li44pcflMGgnkFGw .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-li44pcflMGgnkFGw .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-li44pcflMGgnkFGw .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-li44pcflMGgnkFGw .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-li44pcflMGgnkFGw .cluster text{fill:#333;}#mermaid-svg-li44pcflMGgnkFGw .cluster span{color:#333;}#mermaid-svg-li44pcflMGgnkFGw div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-li44pcflMGgnkFGw .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-li44pcflMGgnkFGw rect.text{fill:none;stroke-width:0;}#mermaid-svg-li44pcflMGgnkFGw .icon-shape,#mermaid-svg-li44pcflMGgnkFGw .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-li44pcflMGgnkFGw .icon-shape p,#mermaid-svg-li44pcflMGgnkFGw .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-li44pcflMGgnkFGw .icon-shape .label rect,#mermaid-svg-li44pcflMGgnkFGw .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-li44pcflMGgnkFGw .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-li44pcflMGgnkFGw .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-li44pcflMGgnkFGw :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Agent 接口 (runtime-types.ts)
方法
send(message, target, wakeup)

底层投递:指定目标 + 是否唤醒
followup(message)

排队新 Turn + 唤醒驱动器
steer(message)

下一个 Step 边界 + 唤醒
inject(message)

下一个 Step 边界 + 不唤醒
cancel(cause, options?)

取消活跃工作 + 清空 inbox
whenIdle()

等待当前活动静止
runMaintenance(task)

空闲时运行非 Turn 维护任务
id: SessionId

与 session 共享的唯一标识
options: AgentOptions

· provider?: string

· model?: string

· maxTokens?: number
session: Session

活跃会话,日志是持久真相源
inbox: Inbox

Agent 拥有的待处理工作投影
status: AgentStatus

'idle' | 'running'
ctx: Context

Agent 作用域上下文

注册 agent-local,卸载时回退

3.2 三种输入投递方式

Agent 有三种语义不同的输入投递方式,都通过底层 send() 实现:
#mermaid-svg-fMclTYG8d226r68G{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-fMclTYG8d226r68G .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-fMclTYG8d226r68G .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-fMclTYG8d226r68G .error-icon{fill:#552222;}#mermaid-svg-fMclTYG8d226r68G .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-fMclTYG8d226r68G .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-fMclTYG8d226r68G .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-fMclTYG8d226r68G .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-fMclTYG8d226r68G .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-fMclTYG8d226r68G .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-fMclTYG8d226r68G .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-fMclTYG8d226r68G .marker{fill:#333333;stroke:#333333;}#mermaid-svg-fMclTYG8d226r68G .marker.cross{stroke:#333333;}#mermaid-svg-fMclTYG8d226r68G svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-fMclTYG8d226r68G p{margin:0;}#mermaid-svg-fMclTYG8d226r68G .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-fMclTYG8d226r68G .cluster-label text{fill:#333;}#mermaid-svg-fMclTYG8d226r68G .cluster-label span{color:#333;}#mermaid-svg-fMclTYG8d226r68G .cluster-label span p{background-color:transparent;}#mermaid-svg-fMclTYG8d226r68G .label text,#mermaid-svg-fMclTYG8d226r68G span{fill:#333;color:#333;}#mermaid-svg-fMclTYG8d226r68G .node rect,#mermaid-svg-fMclTYG8d226r68G .node circle,#mermaid-svg-fMclTYG8d226r68G .node ellipse,#mermaid-svg-fMclTYG8d226r68G .node polygon,#mermaid-svg-fMclTYG8d226r68G .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-fMclTYG8d226r68G .rough-node .label text,#mermaid-svg-fMclTYG8d226r68G .node .label text,#mermaid-svg-fMclTYG8d226r68G .image-shape .label,#mermaid-svg-fMclTYG8d226r68G .icon-shape .label{text-anchor:middle;}#mermaid-svg-fMclTYG8d226r68G .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-fMclTYG8d226r68G .rough-node .label,#mermaid-svg-fMclTYG8d226r68G .node .label,#mermaid-svg-fMclTYG8d226r68G .image-shape .label,#mermaid-svg-fMclTYG8d226r68G .icon-shape .label{text-align:center;}#mermaid-svg-fMclTYG8d226r68G .node.clickable{cursor:pointer;}#mermaid-svg-fMclTYG8d226r68G .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-fMclTYG8d226r68G .arrowheadPath{fill:#333333;}#mermaid-svg-fMclTYG8d226r68G .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-fMclTYG8d226r68G .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-fMclTYG8d226r68G .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-fMclTYG8d226r68G .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-fMclTYG8d226r68G .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-fMclTYG8d226r68G .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-fMclTYG8d226r68G .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-fMclTYG8d226r68G .cluster text{fill:#333;}#mermaid-svg-fMclTYG8d226r68G .cluster span{color:#333;}#mermaid-svg-fMclTYG8d226r68G div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-fMclTYG8d226r68G .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-fMclTYG8d226r68G rect.text{fill:none;stroke-width:0;}#mermaid-svg-fMclTYG8d226r68G .icon-shape,#mermaid-svg-fMclTYG8d226r68G .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-fMclTYG8d226r68G .icon-shape p,#mermaid-svg-fMclTYG8d226r68G .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-fMclTYG8d226r68G .icon-shape .label rect,#mermaid-svg-fMclTYG8d226r68G .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-fMclTYG8d226r68G .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-fMclTYG8d226r68G .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-fMclTYG8d226r68G :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} send
send
send
inject(message)
target: 'next-step'
wakeup: false
运行中驱动器在最近

Step 边界 claim

idle 驱动器不唤醒

可能错过已 claim 的 Step
steer(message)
target: 'next-step'
wakeup: true
idle 驱动器启动 Turn

running 驱动器在下一个

Step 边界消费
followup(message)
target: 'next-turn'
wakeup: true
成为自己 Turn 的

唯一普通消息
send(message, target, wakeup)
是
否
wakeup &&

phase 非 idle &&

signal 已 abort?
resolvedTarget =

wakingAfterAbort ? 'next-turn' : target
inbox.splice(resolvedTarget, ∞, 0, message)
wakeup?
wakeDriver(wakingAfterAbort)
不唤醒,等待其他消息唤醒

关键设计:wakingAfterAbort 分类

当唤醒输入在活跃工作被 abort 后到达时,它被重定向到 next-turn(而不是原始 target),确保唤醒输入不能加入已中止的活动。这个分类在 splice 之前捕获,防止重入 cancel 重分类它。

3.3 Inbox 消息队列系统

Inbox 是 Agent 的待处理消息队列,维护两个列表并通过持久 agent/inbox/spliced 事件记录所有变更。
#mermaid-svg-rQOWJux9bKthAsyj{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-rQOWJux9bKthAsyj .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-rQOWJux9bKthAsyj .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-rQOWJux9bKthAsyj .error-icon{fill:#552222;}#mermaid-svg-rQOWJux9bKthAsyj .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-rQOWJux9bKthAsyj .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-rQOWJux9bKthAsyj .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-rQOWJux9bKthAsyj .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-rQOWJux9bKthAsyj .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-rQOWJux9bKthAsyj .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-rQOWJux9bKthAsyj .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-rQOWJux9bKthAsyj .marker{fill:#333333;stroke:#333333;}#mermaid-svg-rQOWJux9bKthAsyj .marker.cross{stroke:#333333;}#mermaid-svg-rQOWJux9bKthAsyj svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-rQOWJux9bKthAsyj p{margin:0;}#mermaid-svg-rQOWJux9bKthAsyj .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-rQOWJux9bKthAsyj .cluster-label text{fill:#333;}#mermaid-svg-rQOWJux9bKthAsyj .cluster-label span{color:#333;}#mermaid-svg-rQOWJux9bKthAsyj .cluster-label span p{background-color:transparent;}#mermaid-svg-rQOWJux9bKthAsyj .label text,#mermaid-svg-rQOWJux9bKthAsyj span{fill:#333;color:#333;}#mermaid-svg-rQOWJux9bKthAsyj .node rect,#mermaid-svg-rQOWJux9bKthAsyj .node circle,#mermaid-svg-rQOWJux9bKthAsyj .node ellipse,#mermaid-svg-rQOWJux9bKthAsyj .node polygon,#mermaid-svg-rQOWJux9bKthAsyj .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-rQOWJux9bKthAsyj .rough-node .label text,#mermaid-svg-rQOWJux9bKthAsyj .node .label text,#mermaid-svg-rQOWJux9bKthAsyj .image-shape .label,#mermaid-svg-rQOWJux9bKthAsyj .icon-shape .label{text-anchor:middle;}#mermaid-svg-rQOWJux9bKthAsyj .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-rQOWJux9bKthAsyj .rough-node .label,#mermaid-svg-rQOWJux9bKthAsyj .node .label,#mermaid-svg-rQOWJux9bKthAsyj .image-shape .label,#mermaid-svg-rQOWJux9bKthAsyj .icon-shape .label{text-align:center;}#mermaid-svg-rQOWJux9bKthAsyj .node.clickable{cursor:pointer;}#mermaid-svg-rQOWJux9bKthAsyj .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-rQOWJux9bKthAsyj .arrowheadPath{fill:#333333;}#mermaid-svg-rQOWJux9bKthAsyj .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-rQOWJux9bKthAsyj .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-rQOWJux9bKthAsyj .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-rQOWJux9bKthAsyj .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-rQOWJux9bKthAsyj .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-rQOWJux9bKthAsyj .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-rQOWJux9bKthAsyj .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-rQOWJux9bKthAsyj .cluster text{fill:#333;}#mermaid-svg-rQOWJux9bKthAsyj .cluster span{color:#333;}#mermaid-svg-rQOWJux9bKthAsyj div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-rQOWJux9bKthAsyj .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-rQOWJux9bKthAsyj rect.text{fill:none;stroke-width:0;}#mermaid-svg-rQOWJux9bKthAsyj .icon-shape,#mermaid-svg-rQOWJux9bKthAsyj .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-rQOWJux9bKthAsyj .icon-shape p,#mermaid-svg-rQOWJux9bKthAsyj .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-rQOWJux9bKthAsyj .icon-shape .label rect,#mermaid-svg-rQOWJux9bKthAsyj .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-rQOWJux9bKthAsyj .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-rQOWJux9bKthAsyj .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-rQOWJux9bKthAsyj :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 属性
hasPending

nextTurn.length > 0 ||

nextStep.length > 0
nextTurn

只读 next-turn 列表
nextStep

只读 next-step 列表
Inbox 类 (inbox.ts)
通知回调
inserted(message)

消息进入 inbox
discarded(message)

消息被删除(cancel 时)
claimed(message, turn)

消息被 claim 进 Turn
持久化
agent/inbox/spliced 事件

· target: 'next-turn' | 'next-step'

· start: number

· removedCount?: number

· inserted: UserMessage\[\]

· outcome?: 'canceled'
重放恢复

构造时遍历 seed 之后

的所有 splice 事件
操作方法
claim(target, turn)

取出完整批次

  1. 清空 next-step

  2. 如果 target=next-turn,

取出 next-turn0

  1. 发布 claimed 通知
    append(target, message)

追加到尾部

持久 splice
prepend(target, message)

插入到头部

持久 splice
replace(messageId, newMessage)

原地替换

发布 discarded + inserted
remove(messageId)

删除单个消息

持久 splice
splice(target, start, deleteCount, inserted)

标准 splice 语义

持久记录 + 去重验证
clear()

清空两个队列

先清 next-step 再清 next-turn
内部状态
next-turn: UserMessage\[\]

等待各自 Turn 的消息
next-step: UserMessage\[\]

等待下一个 Step 边界的消息

关键实现细节:

  1. 持久化优先 :mutate() 先 session.append('agent/inbox/spliced', splice) 写入日志,然后 才修改内存状态。同步 session/event 观察者看到 splice 前的列表,可从规范化坐标重建被删除的消息。

  2. 去重验证 :validate() 检查 splice 后两个队列中不存在重复的 message.id。验证时将候选队列与另一个队列合并检查。

  3. outcome 标记 :当 discardRemoved=true && actualDeleteCount > 0 时,splice 事件携带 outcome: 'canceled'。ConsumedWork 折叠器使用此标记区分"被取消的工作"和"被替换的工作"。

  4. 重放恢复 :构造函数遍历 session.events.slice(seedLength) 中的所有 agent/inbox/spliced 事件,通过 apply() 重建内存状态。

3.4 Agent 事件体系

Agent 事件分为三类,全部在 runtime-types.ts 中声明:
#mermaid-svg-8lJ4o2vw5GIJd53V{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-8lJ4o2vw5GIJd53V .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-8lJ4o2vw5GIJd53V .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-8lJ4o2vw5GIJd53V .error-icon{fill:#552222;}#mermaid-svg-8lJ4o2vw5GIJd53V .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-8lJ4o2vw5GIJd53V .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-8lJ4o2vw5GIJd53V .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-8lJ4o2vw5GIJd53V .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-8lJ4o2vw5GIJd53V .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-8lJ4o2vw5GIJd53V .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-8lJ4o2vw5GIJd53V .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-8lJ4o2vw5GIJd53V .marker{fill:#333333;stroke:#333333;}#mermaid-svg-8lJ4o2vw5GIJd53V .marker.cross{stroke:#333333;}#mermaid-svg-8lJ4o2vw5GIJd53V svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-8lJ4o2vw5GIJd53V p{margin:0;}#mermaid-svg-8lJ4o2vw5GIJd53V .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-8lJ4o2vw5GIJd53V .cluster-label text{fill:#333;}#mermaid-svg-8lJ4o2vw5GIJd53V .cluster-label span{color:#333;}#mermaid-svg-8lJ4o2vw5GIJd53V .cluster-label span p{background-color:transparent;}#mermaid-svg-8lJ4o2vw5GIJd53V .label text,#mermaid-svg-8lJ4o2vw5GIJd53V span{fill:#333;color:#333;}#mermaid-svg-8lJ4o2vw5GIJd53V .node rect,#mermaid-svg-8lJ4o2vw5GIJd53V .node circle,#mermaid-svg-8lJ4o2vw5GIJd53V .node ellipse,#mermaid-svg-8lJ4o2vw5GIJd53V .node polygon,#mermaid-svg-8lJ4o2vw5GIJd53V .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-8lJ4o2vw5GIJd53V .rough-node .label text,#mermaid-svg-8lJ4o2vw5GIJd53V .node .label text,#mermaid-svg-8lJ4o2vw5GIJd53V .image-shape .label,#mermaid-svg-8lJ4o2vw5GIJd53V .icon-shape .label{text-anchor:middle;}#mermaid-svg-8lJ4o2vw5GIJd53V .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-8lJ4o2vw5GIJd53V .rough-node .label,#mermaid-svg-8lJ4o2vw5GIJd53V .node .label,#mermaid-svg-8lJ4o2vw5GIJd53V .image-shape .label,#mermaid-svg-8lJ4o2vw5GIJd53V .icon-shape .label{text-align:center;}#mermaid-svg-8lJ4o2vw5GIJd53V .node.clickable{cursor:pointer;}#mermaid-svg-8lJ4o2vw5GIJd53V .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-8lJ4o2vw5GIJd53V .arrowheadPath{fill:#333333;}#mermaid-svg-8lJ4o2vw5GIJd53V .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-8lJ4o2vw5GIJd53V .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-8lJ4o2vw5GIJd53V .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-8lJ4o2vw5GIJd53V .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-8lJ4o2vw5GIJd53V .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-8lJ4o2vw5GIJd53V .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-8lJ4o2vw5GIJd53V .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-8lJ4o2vw5GIJd53V .cluster text{fill:#333;}#mermaid-svg-8lJ4o2vw5GIJd53V .cluster span{color:#333;}#mermaid-svg-8lJ4o2vw5GIJd53V div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-8lJ4o2vw5GIJd53V .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-8lJ4o2vw5GIJd53V rect.text{fill:none;stroke-width:0;}#mermaid-svg-8lJ4o2vw5GIJd53V .icon-shape,#mermaid-svg-8lJ4o2vw5GIJd53V .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-8lJ4o2vw5GIJd53V .icon-shape p,#mermaid-svg-8lJ4o2vw5GIJd53V .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-8lJ4o2vw5GIJd53V .icon-shape .label rect,#mermaid-svg-8lJ4o2vw5GIJd53V .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-8lJ4o2vw5GIJd53V .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-8lJ4o2vw5GIJd53V .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-8lJ4o2vw5GIJd53V :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 分发模式
Agent 事件体系 (runtime-types.ts)
机器扩展点
Inbox 事件 (emit)
生命周期事件 (emit)
agent/created

Agent + Session 发布完成

同步 throw 否决发布
agent/disposed

Agent 离开注册表

驱动器静止 + 注册卸载后
agent/status

idle ⇄ running

每次状态转换触发
agent/session-start

Session 生命周期开始

首次 Turn 之前

source: startup|resume|clear|compact
agent/inbox/inserted

消息进入 inbox
agent/inbox/claimed

消息被 claim 进 Turn

携带 turn 号
agent/inbox/discarded

消息从 inbox 删除
agent/pre-step

(Waterfall)

拒绝或重写 Step 消息

必须调 next()
agent/request

(Waterfall)

替换冻结的调用配置

不能修改 messages
agent/request-error

(Waterfall)

处理失败的模型请求

返回 {kind:'retry'} 或 undefined
agent/turn-stopping

(Serial, 无 next)

Turn 即将关闭

监听器可 steer 延续
agent/error

(emit)

Step 或 Turn 出错

报告 failure
emit: 广播

每个监听器独立调用

失败不影响其他
waterfall: 链式

监听器必须调 next()

可短路返回
serial: 串行

有序调用

无 next() 委托

3.5 Dispatch 融合分发器

dispatch.ts 实现了融合分发器 (Fused Dispatcher),将 Agent 主体与 scope carrier 绑定,确保 scope key 和 payload 中的 agent 字段不会分歧。
#mermaid-svg-6MqSbSqiVWV3TjJV{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-6MqSbSqiVWV3TjJV .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-6MqSbSqiVWV3TjJV .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-6MqSbSqiVWV3TjJV .error-icon{fill:#552222;}#mermaid-svg-6MqSbSqiVWV3TjJV .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-6MqSbSqiVWV3TjJV .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-6MqSbSqiVWV3TjJV .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-6MqSbSqiVWV3TjJV .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-6MqSbSqiVWV3TjJV .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-6MqSbSqiVWV3TjJV .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-6MqSbSqiVWV3TjJV .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-6MqSbSqiVWV3TjJV .marker{fill:#333333;stroke:#333333;}#mermaid-svg-6MqSbSqiVWV3TjJV .marker.cross{stroke:#333333;}#mermaid-svg-6MqSbSqiVWV3TjJV svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-6MqSbSqiVWV3TjJV p{margin:0;}#mermaid-svg-6MqSbSqiVWV3TjJV .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-6MqSbSqiVWV3TjJV .cluster-label text{fill:#333;}#mermaid-svg-6MqSbSqiVWV3TjJV .cluster-label span{color:#333;}#mermaid-svg-6MqSbSqiVWV3TjJV .cluster-label span p{background-color:transparent;}#mermaid-svg-6MqSbSqiVWV3TjJV .label text,#mermaid-svg-6MqSbSqiVWV3TjJV span{fill:#333;color:#333;}#mermaid-svg-6MqSbSqiVWV3TjJV .node rect,#mermaid-svg-6MqSbSqiVWV3TjJV .node circle,#mermaid-svg-6MqSbSqiVWV3TjJV .node ellipse,#mermaid-svg-6MqSbSqiVWV3TjJV .node polygon,#mermaid-svg-6MqSbSqiVWV3TjJV .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-6MqSbSqiVWV3TjJV .rough-node .label text,#mermaid-svg-6MqSbSqiVWV3TjJV .node .label text,#mermaid-svg-6MqSbSqiVWV3TjJV .image-shape .label,#mermaid-svg-6MqSbSqiVWV3TjJV .icon-shape .label{text-anchor:middle;}#mermaid-svg-6MqSbSqiVWV3TjJV .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-6MqSbSqiVWV3TjJV .rough-node .label,#mermaid-svg-6MqSbSqiVWV3TjJV .node .label,#mermaid-svg-6MqSbSqiVWV3TjJV .image-shape .label,#mermaid-svg-6MqSbSqiVWV3TjJV .icon-shape .label{text-align:center;}#mermaid-svg-6MqSbSqiVWV3TjJV .node.clickable{cursor:pointer;}#mermaid-svg-6MqSbSqiVWV3TjJV .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-6MqSbSqiVWV3TjJV .arrowheadPath{fill:#333333;}#mermaid-svg-6MqSbSqiVWV3TjJV .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-6MqSbSqiVWV3TjJV .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-6MqSbSqiVWV3TjJV .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-6MqSbSqiVWV3TjJV .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-6MqSbSqiVWV3TjJV .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-6MqSbSqiVWV3TjJV .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-6MqSbSqiVWV3TjJV .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-6MqSbSqiVWV3TjJV .cluster text{fill:#333;}#mermaid-svg-6MqSbSqiVWV3TjJV .cluster span{color:#333;}#mermaid-svg-6MqSbSqiVWV3TjJV div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-6MqSbSqiVWV3TjJV .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-6MqSbSqiVWV3TjJV rect.text{fill:none;stroke-width:0;}#mermaid-svg-6MqSbSqiVWV3TjJV .icon-shape,#mermaid-svg-6MqSbSqiVWV3TjJV .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-6MqSbSqiVWV3TjJV .icon-shape p,#mermaid-svg-6MqSbSqiVWV3TjJV .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-6MqSbSqiVWV3TjJV .icon-shape .label rect,#mermaid-svg-6MqSbSqiVWV3TjJV .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-6MqSbSqiVWV3TjJV .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-6MqSbSqiVWV3TjJV .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-6MqSbSqiVWV3TjJV :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} AgentEventDispatch 融合分发器
三种分发方法
Scope 过滤
scopeTarget(agent, agent)

构建 Scoped<Agent>

· 过滤未标记监听器

· 加 agent 自己的

· 祖先 scope 可收到后代事件
agentCarrier(agent)

scopeTarget(agent, agent)

构建 scope carrier

stateless routing object
agentEvents(ctx, agent, carrier?)

构建融合分发器

carrier 构造一次,热路径复用
emit(name, payload)

· 注入 agent 到 payload

· 通过 carrier 分发

· 每个 callback 独立 try/catch

· 返回 promise rejection 被 warn

· 不否决生命周期
serial(name, payload)

· 注入 agent 到 payload

· ctx.serial(carrier, name, payload)

· 有序串行调用

· 返回第一个 bail 值
waterfall(name, payload, ...rest)

· 注入 agent 到 payload

· ctx.waterfall(carrier, name, payload, ...rest)

· rest = next() 回调

· 返回组合结果
fused(payload)

({ ...payload, agent })

· spread 在前,agent 注入在后

· 防止 payload 中的 agent 覆盖

· 类型安全

关键设计:

  1. 构造一次,复用多次 :ReactLoopAgent 构造函数中调用 agentEvents(loopCtx, this) 构建分发器并保存,热路径分发零分配。

  2. emit 独立容错 :Cordis 的 emit 通过 Array.map 调用 callback------一个同步 throw 会饿死后续监听器,返回的 promise 被丢弃。Agent 通知是非否决的,所以融合分发器自己解析 callback 集合并独立容错两种失败模式。

  3. assembleContextFor :assembleContextFor(agent, signal?) 构建 prompt 组装上下文,将 agent 和 scope 一起设置,确保 agent-scoped 的 prompt 和 tool 贡献不会被静默遗漏。

3.6 ConsumedWork 工作审计

consumed-work.ts 实现了工作审计折叠,解决"Turn 和 Step 词汇无法回答 Agent 消费了什么工作"的问题。
#mermaid-svg-ayMRWAYG2HqioAuu{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-ayMRWAYG2HqioAuu .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-ayMRWAYG2HqioAuu .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-ayMRWAYG2HqioAuu .error-icon{fill:#552222;}#mermaid-svg-ayMRWAYG2HqioAuu .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-ayMRWAYG2HqioAuu .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-ayMRWAYG2HqioAuu .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-ayMRWAYG2HqioAuu .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-ayMRWAYG2HqioAuu .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-ayMRWAYG2HqioAuu .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-ayMRWAYG2HqioAuu .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-ayMRWAYG2HqioAuu .marker{fill:#333333;stroke:#333333;}#mermaid-svg-ayMRWAYG2HqioAuu .marker.cross{stroke:#333333;}#mermaid-svg-ayMRWAYG2HqioAuu svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-ayMRWAYG2HqioAuu p{margin:0;}#mermaid-svg-ayMRWAYG2HqioAuu .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-ayMRWAYG2HqioAuu .cluster-label text{fill:#333;}#mermaid-svg-ayMRWAYG2HqioAuu .cluster-label span{color:#333;}#mermaid-svg-ayMRWAYG2HqioAuu .cluster-label span p{background-color:transparent;}#mermaid-svg-ayMRWAYG2HqioAuu .label text,#mermaid-svg-ayMRWAYG2HqioAuu span{fill:#333;color:#333;}#mermaid-svg-ayMRWAYG2HqioAuu .node rect,#mermaid-svg-ayMRWAYG2HqioAuu .node circle,#mermaid-svg-ayMRWAYG2HqioAuu .node ellipse,#mermaid-svg-ayMRWAYG2HqioAuu .node polygon,#mermaid-svg-ayMRWAYG2HqioAuu .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-ayMRWAYG2HqioAuu .rough-node .label text,#mermaid-svg-ayMRWAYG2HqioAuu .node .label text,#mermaid-svg-ayMRWAYG2HqioAuu .image-shape .label,#mermaid-svg-ayMRWAYG2HqioAuu .icon-shape .label{text-anchor:middle;}#mermaid-svg-ayMRWAYG2HqioAuu .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-ayMRWAYG2HqioAuu .rough-node .label,#mermaid-svg-ayMRWAYG2HqioAuu .node .label,#mermaid-svg-ayMRWAYG2HqioAuu .image-shape .label,#mermaid-svg-ayMRWAYG2HqioAuu .icon-shape .label{text-align:center;}#mermaid-svg-ayMRWAYG2HqioAuu .node.clickable{cursor:pointer;}#mermaid-svg-ayMRWAYG2HqioAuu .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-ayMRWAYG2HqioAuu .arrowheadPath{fill:#333333;}#mermaid-svg-ayMRWAYG2HqioAuu .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-ayMRWAYG2HqioAuu .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-ayMRWAYG2HqioAuu .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ayMRWAYG2HqioAuu .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-ayMRWAYG2HqioAuu .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ayMRWAYG2HqioAuu .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-ayMRWAYG2HqioAuu .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-ayMRWAYG2HqioAuu .cluster text{fill:#333;}#mermaid-svg-ayMRWAYG2HqioAuu .cluster span{color:#333;}#mermaid-svg-ayMRWAYG2HqioAuu div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-ayMRWAYG2HqioAuu .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-ayMRWAYG2HqioAuu rect.text{fill:none;stroke-width:0;}#mermaid-svg-ayMRWAYG2HqioAuu .icon-shape,#mermaid-svg-ayMRWAYG2HqioAuu .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ayMRWAYG2HqioAuu .icon-shape p,#mermaid-svg-ayMRWAYG2HqioAuu .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-ayMRWAYG2HqioAuu .icon-shape .label rect,#mermaid-svg-ayMRWAYG2HqioAuu .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ayMRWAYG2HqioAuu .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-ayMRWAYG2HqioAuu .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-ayMRWAYG2HqioAuu :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} foldConsumedWork 解决方案
折叠逻辑
stepped: Set<turn>

记录有 step 的 Turn
accountsForClaim(reason)

· completed → false (没有剩余工作)

· blocked → true (pre-step 拒绝)

· aborted → true (中断)

· interrupted → true (打断)

· error → true (错误)

· default → true (未知也要算)
turn/end

· stepped.delete(turn) ||

(claimed.delete(turn) &&

accountsForClaim(reason))

→ end = event, droppedUnrun = false
输入: SessionEvent\[\]
输出: ConsumedWork

· end?: turn/end 事件

· droppedUnrun: boolean
claimed: Set<turn>

记录 claim 了输入的 Turn
open: turn | undefined

当前开放的 Turn
turn/start

open = turn
step/start

stepped.add(turn)
agent/inbox/spliced

· outcome='canceled' && inserted=空

→ droppedUnrun = true

· 否则 open !== undefined

→ claimed.add(open)
问题背景
一个在首步前停止的 Turn

留下一个 turn/end

形状与 no-op Turn 完全一样

缺少的事实是 inbox 自己的记录

核心洞察 :一次取消从 inbox 取走但从未被任何 Turn 处理的输入------没有 turn/end 描述它。droppedUnrun 是这种"在 Turn 开启前被取消的工作"的唯一账目。


4. ReactLoopAgent 驱动器实现(core/agent-loop/agent.ts)

4.1 类结构与状态机

ReactLoopAgent 是 DSH 的默认 Agent 驱动器,实现了完整的 Turn/Step 循环。
#mermaid-svg-RDQXtBSEz7MzWvyW{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-RDQXtBSEz7MzWvyW .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-RDQXtBSEz7MzWvyW .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-RDQXtBSEz7MzWvyW .error-icon{fill:#552222;}#mermaid-svg-RDQXtBSEz7MzWvyW .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-RDQXtBSEz7MzWvyW .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-RDQXtBSEz7MzWvyW .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-RDQXtBSEz7MzWvyW .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-RDQXtBSEz7MzWvyW .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-RDQXtBSEz7MzWvyW .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-RDQXtBSEz7MzWvyW .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-RDQXtBSEz7MzWvyW .marker{fill:#333333;stroke:#333333;}#mermaid-svg-RDQXtBSEz7MzWvyW .marker.cross{stroke:#333333;}#mermaid-svg-RDQXtBSEz7MzWvyW svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-RDQXtBSEz7MzWvyW p{margin:0;}#mermaid-svg-RDQXtBSEz7MzWvyW .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-RDQXtBSEz7MzWvyW .cluster-label text{fill:#333;}#mermaid-svg-RDQXtBSEz7MzWvyW .cluster-label span{color:#333;}#mermaid-svg-RDQXtBSEz7MzWvyW .cluster-label span p{background-color:transparent;}#mermaid-svg-RDQXtBSEz7MzWvyW .label text,#mermaid-svg-RDQXtBSEz7MzWvyW span{fill:#333;color:#333;}#mermaid-svg-RDQXtBSEz7MzWvyW .node rect,#mermaid-svg-RDQXtBSEz7MzWvyW .node circle,#mermaid-svg-RDQXtBSEz7MzWvyW .node ellipse,#mermaid-svg-RDQXtBSEz7MzWvyW .node polygon,#mermaid-svg-RDQXtBSEz7MzWvyW .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-RDQXtBSEz7MzWvyW .rough-node .label text,#mermaid-svg-RDQXtBSEz7MzWvyW .node .label text,#mermaid-svg-RDQXtBSEz7MzWvyW .image-shape .label,#mermaid-svg-RDQXtBSEz7MzWvyW .icon-shape .label{text-anchor:middle;}#mermaid-svg-RDQXtBSEz7MzWvyW .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-RDQXtBSEz7MzWvyW .rough-node .label,#mermaid-svg-RDQXtBSEz7MzWvyW .node .label,#mermaid-svg-RDQXtBSEz7MzWvyW .image-shape .label,#mermaid-svg-RDQXtBSEz7MzWvyW .icon-shape .label{text-align:center;}#mermaid-svg-RDQXtBSEz7MzWvyW .node.clickable{cursor:pointer;}#mermaid-svg-RDQXtBSEz7MzWvyW .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-RDQXtBSEz7MzWvyW .arrowheadPath{fill:#333333;}#mermaid-svg-RDQXtBSEz7MzWvyW .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-RDQXtBSEz7MzWvyW .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-RDQXtBSEz7MzWvyW .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-RDQXtBSEz7MzWvyW .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-RDQXtBSEz7MzWvyW .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-RDQXtBSEz7MzWvyW .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-RDQXtBSEz7MzWvyW .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-RDQXtBSEz7MzWvyW .cluster text{fill:#333;}#mermaid-svg-RDQXtBSEz7MzWvyW .cluster span{color:#333;}#mermaid-svg-RDQXtBSEz7MzWvyW div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-RDQXtBSEz7MzWvyW .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-RDQXtBSEz7MzWvyW rect.text{fill:none;stroke-width:0;}#mermaid-svg-RDQXtBSEz7MzWvyW .icon-shape,#mermaid-svg-RDQXtBSEz7MzWvyW .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-RDQXtBSEz7MzWvyW .icon-shape p,#mermaid-svg-RDQXtBSEz7MzWvyW .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-RDQXtBSEz7MzWvyW .icon-shape .label rect,#mermaid-svg-RDQXtBSEz7MzWvyW .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-RDQXtBSEz7MzWvyW .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-RDQXtBSEz7MzWvyW .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-RDQXtBSEz7MzWvyW :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} ReactLoopAgent 类 (agent.ts)
私有方法
wakeDriver(wakeAfterAbort?)

唤醒驱动器或锁存唤醒
kick()

驱动主循环

while (await turn()) {}
turn()

Turn 级循环

返回是否继续下一个 Turn
preStep(target, position)

Step 前置处理

claim + assemble + waterfall
step(assembly)

Step 级行执行

buildRequest + stream + tools
buildRequest(...)

构建冻结请求

  • request/header 持久化
    throwError(error)

报告失败 + 重新抛出
公开方法
get status()

idle|running
setPhase(next)

提交 phase + 发布状态转换
send(message, target, wakeup)
followup(input)
steer(input)
inject(input)
cancel(cause, options?)
runMaintenance(job)
whenIdle()
属性
inbox: Inbox

消息队列
phase: Phase

三态状态机
activityDone: Promise

当前活动完成信号
scope: Scope

Agent 作用域注册边界
ctx: Context

Agent-scoped 上下文
dispatch: AgentEventDispatch

融合分发器(构造时构建)
requestHeaderLogged: boolean

是否已记录初始/resume 请求锚点
runtimeContext: RuntimeContextProjection

运行时上下文投影

4.2 Phase 三态状态机

#mermaid-svg-G7HHPVdpcj6QoAV7{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-G7HHPVdpcj6QoAV7 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-G7HHPVdpcj6QoAV7 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-G7HHPVdpcj6QoAV7 .error-icon{fill:#552222;}#mermaid-svg-G7HHPVdpcj6QoAV7 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-G7HHPVdpcj6QoAV7 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-G7HHPVdpcj6QoAV7 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-G7HHPVdpcj6QoAV7 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-G7HHPVdpcj6QoAV7 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-G7HHPVdpcj6QoAV7 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-G7HHPVdpcj6QoAV7 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-G7HHPVdpcj6QoAV7 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-G7HHPVdpcj6QoAV7 .marker.cross{stroke:#333333;}#mermaid-svg-G7HHPVdpcj6QoAV7 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-G7HHPVdpcj6QoAV7 p{margin:0;}#mermaid-svg-G7HHPVdpcj6QoAV7 defs #statediagram-barbEnd{fill:#333333;stroke:#333333;}#mermaid-svg-G7HHPVdpcj6QoAV7 g.stateGroup text{fill:#9370DB;stroke:none;font-size:10px;}#mermaid-svg-G7HHPVdpcj6QoAV7 g.stateGroup text{fill:#333;stroke:none;font-size:10px;}#mermaid-svg-G7HHPVdpcj6QoAV7 g.stateGroup .state-title{font-weight:bolder;fill:#131300;}#mermaid-svg-G7HHPVdpcj6QoAV7 g.stateGroup rect{fill:#ECECFF;stroke:#9370DB;}#mermaid-svg-G7HHPVdpcj6QoAV7 g.stateGroup line{stroke:#333333;stroke-width:1;}#mermaid-svg-G7HHPVdpcj6QoAV7 .transition{stroke:#333333;stroke-width:1;fill:none;}#mermaid-svg-G7HHPVdpcj6QoAV7 .stateGroup .composit{fill:white;border-bottom:1px;}#mermaid-svg-G7HHPVdpcj6QoAV7 .stateGroup .alt-composit{fill:#e0e0e0;border-bottom:1px;}#mermaid-svg-G7HHPVdpcj6QoAV7 .state-note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-G7HHPVdpcj6QoAV7 .state-note text{fill:black;stroke:none;font-size:10px;}#mermaid-svg-G7HHPVdpcj6QoAV7 .stateLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5;}#mermaid-svg-G7HHPVdpcj6QoAV7 .edgeLabel .label rect{fill:#ECECFF;opacity:0.5;}#mermaid-svg-G7HHPVdpcj6QoAV7 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-G7HHPVdpcj6QoAV7 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-G7HHPVdpcj6QoAV7 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-G7HHPVdpcj6QoAV7 .edgeLabel .label text{fill:#333;}#mermaid-svg-G7HHPVdpcj6QoAV7 .label div .edgeLabel{color:#333;}#mermaid-svg-G7HHPVdpcj6QoAV7 .stateLabel text{fill:#131300;font-size:10px;font-weight:bold;}#mermaid-svg-G7HHPVdpcj6QoAV7 .node circle.state-start{fill:#333333;stroke:#333333;}#mermaid-svg-G7HHPVdpcj6QoAV7 .node .fork-join{fill:#333333;stroke:#333333;}#mermaid-svg-G7HHPVdpcj6QoAV7 .node circle.state-end{fill:#9370DB;stroke:white;stroke-width:1.5;}#mermaid-svg-G7HHPVdpcj6QoAV7 .end-state-inner{fill:white;stroke-width:1.5;}#mermaid-svg-G7HHPVdpcj6QoAV7 .node rect{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-G7HHPVdpcj6QoAV7 .node polygon{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-G7HHPVdpcj6QoAV7 #statediagram-barbEnd{fill:#333333;}#mermaid-svg-G7HHPVdpcj6QoAV7 .statediagram-cluster rect{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-G7HHPVdpcj6QoAV7 .cluster-label,#mermaid-svg-G7HHPVdpcj6QoAV7 .nodeLabel{color:#131300;}#mermaid-svg-G7HHPVdpcj6QoAV7 .statediagram-cluster rect.outer{rx:5px;ry:5px;}#mermaid-svg-G7HHPVdpcj6QoAV7 .statediagram-state .divider{stroke:#9370DB;}#mermaid-svg-G7HHPVdpcj6QoAV7 .statediagram-state .title-state{rx:5px;ry:5px;}#mermaid-svg-G7HHPVdpcj6QoAV7 .statediagram-cluster.statediagram-cluster .inner{fill:white;}#mermaid-svg-G7HHPVdpcj6QoAV7 .statediagram-cluster.statediagram-cluster-alt .inner{fill:#f0f0f0;}#mermaid-svg-G7HHPVdpcj6QoAV7 .statediagram-cluster .inner{rx:0;ry:0;}#mermaid-svg-G7HHPVdpcj6QoAV7 .statediagram-state rect.basic{rx:5px;ry:5px;}#mermaid-svg-G7HHPVdpcj6QoAV7 .statediagram-state rect.divider{stroke-dasharray:10,10;fill:#f0f0f0;}#mermaid-svg-G7HHPVdpcj6QoAV7 .note-edge{stroke-dasharray:5;}#mermaid-svg-G7HHPVdpcj6QoAV7 .statediagram-note rect{fill:#fff5ad;stroke:#aaaa33;stroke-width:1px;rx:0;ry:0;}#mermaid-svg-G7HHPVdpcj6QoAV7 .statediagram-note rect{fill:#fff5ad;stroke:#aaaa33;stroke-width:1px;rx:0;ry:0;}#mermaid-svg-G7HHPVdpcj6QoAV7 .statediagram-note text{fill:black;}#mermaid-svg-G7HHPVdpcj6QoAV7 .statediagram-note .nodeLabel{color:black;}#mermaid-svg-G7HHPVdpcj6QoAV7 .statediagram .edgeLabel{color:red;}#mermaid-svg-G7HHPVdpcj6QoAV7 #dependencyStart,#mermaid-svg-G7HHPVdpcj6QoAV7 #dependencyEnd{fill:#333333;stroke:#333333;stroke-width:1;}#mermaid-svg-G7HHPVdpcj6QoAV7 .statediagramTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-G7HHPVdpcj6QoAV7 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 构造函数

lastTurn = 最后一个 turn/start
wakeDriver()

setPhase({kind:'running',

turn: lastTurn, step: 0})
runMaintenance(job)

setPhase({kind:'maintenance',

abort: new AC})
kick() finally

setPhase({kind:'idle',

lastTurn: turn})
runMaintenance() finally

setPhase({kind:'idle',

lastTurn: lastTurn})
idle
running
maintenance
idle:

· status = 'idle'

· 可接受 waking 输入

· 可接受 runMaintenance

· wakeDriver 直接启动 Turn
running:

· status = 'running'

· abort: AbortController

· turn: 当前 Turn 号

· step: 当前 Step 号

· wakeRequested: 锁存的唤醒

· kick() 驱动 while(turn())
maintenance:

· status = 'idle' (对外仍是 idle)

· abort: AbortController

· wakeRequested: 锁存的唤醒

· runMaintenance 完成后

如果 wakeRequested && hasPending

则 wakeDriver()

三种 Phase 的关键区别:

Phase status 可接受操作 abort 信号 驱动器
idle 'idle' waking 输入、runMaintenance 无 无活跃驱动器
running 'running' waking 输入(锁存)、cancel AbortController kick() 驱动中
maintenance 'idle' waking 输入(锁存)、cancel AbortController 维护任务运行中

4.3 wakeDriver 驱动唤醒

wakeDriver 是驱动器的入口点,处理唤醒输入在三种 Phase 下的不同行为:
#mermaid-svg-x2kOc8MVdUqWXotD{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-x2kOc8MVdUqWXotD .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-x2kOc8MVdUqWXotD .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-x2kOc8MVdUqWXotD .error-icon{fill:#552222;}#mermaid-svg-x2kOc8MVdUqWXotD .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-x2kOc8MVdUqWXotD .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-x2kOc8MVdUqWXotD .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-x2kOc8MVdUqWXotD .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-x2kOc8MVdUqWXotD .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-x2kOc8MVdUqWXotD .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-x2kOc8MVdUqWXotD .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-x2kOc8MVdUqWXotD .marker{fill:#333333;stroke:#333333;}#mermaid-svg-x2kOc8MVdUqWXotD .marker.cross{stroke:#333333;}#mermaid-svg-x2kOc8MVdUqWXotD svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-x2kOc8MVdUqWXotD p{margin:0;}#mermaid-svg-x2kOc8MVdUqWXotD .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-x2kOc8MVdUqWXotD .cluster-label text{fill:#333;}#mermaid-svg-x2kOc8MVdUqWXotD .cluster-label span{color:#333;}#mermaid-svg-x2kOc8MVdUqWXotD .cluster-label span p{background-color:transparent;}#mermaid-svg-x2kOc8MVdUqWXotD .label text,#mermaid-svg-x2kOc8MVdUqWXotD span{fill:#333;color:#333;}#mermaid-svg-x2kOc8MVdUqWXotD .node rect,#mermaid-svg-x2kOc8MVdUqWXotD .node circle,#mermaid-svg-x2kOc8MVdUqWXotD .node ellipse,#mermaid-svg-x2kOc8MVdUqWXotD .node polygon,#mermaid-svg-x2kOc8MVdUqWXotD .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-x2kOc8MVdUqWXotD .rough-node .label text,#mermaid-svg-x2kOc8MVdUqWXotD .node .label text,#mermaid-svg-x2kOc8MVdUqWXotD .image-shape .label,#mermaid-svg-x2kOc8MVdUqWXotD .icon-shape .label{text-anchor:middle;}#mermaid-svg-x2kOc8MVdUqWXotD .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-x2kOc8MVdUqWXotD .rough-node .label,#mermaid-svg-x2kOc8MVdUqWXotD .node .label,#mermaid-svg-x2kOc8MVdUqWXotD .image-shape .label,#mermaid-svg-x2kOc8MVdUqWXotD .icon-shape .label{text-align:center;}#mermaid-svg-x2kOc8MVdUqWXotD .node.clickable{cursor:pointer;}#mermaid-svg-x2kOc8MVdUqWXotD .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-x2kOc8MVdUqWXotD .arrowheadPath{fill:#333333;}#mermaid-svg-x2kOc8MVdUqWXotD .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-x2kOc8MVdUqWXotD .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-x2kOc8MVdUqWXotD .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-x2kOc8MVdUqWXotD .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-x2kOc8MVdUqWXotD .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-x2kOc8MVdUqWXotD .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-x2kOc8MVdUqWXotD .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-x2kOc8MVdUqWXotD .cluster text{fill:#333;}#mermaid-svg-x2kOc8MVdUqWXotD .cluster span{color:#333;}#mermaid-svg-x2kOc8MVdUqWXotD div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-x2kOc8MVdUqWXotD .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-x2kOc8MVdUqWXotD rect.text{fill:none;stroke-width:0;}#mermaid-svg-x2kOc8MVdUqWXotD .icon-shape,#mermaid-svg-x2kOc8MVdUqWXotD .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-x2kOc8MVdUqWXotD .icon-shape p,#mermaid-svg-x2kOc8MVdUqWXotD .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-x2kOc8MVdUqWXotD .icon-shape .label rect,#mermaid-svg-x2kOc8MVdUqWXotD .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-x2kOc8MVdUqWXotD .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-x2kOc8MVdUqWXotD .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-x2kOc8MVdUqWXotD :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} idle: 启动驱动器
否
是
非 idle: 锁存唤醒
是
否
是
否
abort.reason.kind

=== 'disposed'?
不锁存

(disposed 不锁存)

(活跃驱动器自己 claim)
phase.kind === 'maintenance'

|| wakeAfterAbort?
wakeRequested = true

在收敛时重放
wakeDriver(wakeAfterAbort = false)
phase.kind === 'idle'?
driver = Promise.withResolvers()
activityDone = driver.promise
setPhase({

kind: 'running',

abort: new AbortController(),

turn: lastTurn,

step: 0,

wakeRequested: false

})
loopCtx.agents.withInitiator(this,

() => this.kick())

.then(driver.resolve, driver.reject)

关键设计:

  1. disposed 不锁存:销毁原因的 abort 不锁存唤醒,确保 teardown 不等待任何 model turn。
  2. 活跃驱动器自己 claim:running 状态的驱动器自己 claim 排队工作,不需要锁存。
  3. maintenance 和 wakeAfterAbort 锁存:维护任务完成后或 abort 后的唤醒需要锁存,在收敛到 idle 时重放。
  4. withInitiator :kick 在 agents.withInitiator 上下文中运行,使工具调度器能通过 ctx.agents.requireInitiator() 获取发起 Agent。

4.4 kick 驱动主循环

kick() 是驱动器的主循环入口:
渲染错误: Mermaid 渲染失败: Parse error on line 14: ... while_loop -->|turn() 返回 true| while_lo -----------------------^ Expecting 'SQE', 'DOUBLECIRCLEEND', 'PE', '-)', 'STADIUMEND', 'SUBROUTINEEND', 'PIPE', 'CYLINDEREND', 'DIAMOND_STOP', 'TAGEND', 'TRAPEND', 'INVTRAPEND', 'UNICODE_TEXT', 'TEXT', 'TAGSTART', got 'PS'

核心 :while (await this.turn()) {}------turn() 返回 true 时继续下一个 Turn,返回 false 时停止。turn() 的返回值取决于 inbox 是否还有 pending 消息。

4.5 turn 方法------Turn 级循环

turn() 是 Turn 级循环的核心方法,打开一个 Turn 边界,在内部循环执行 Step:
#mermaid-svg-vzkN3yb2N95dKIeV{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-vzkN3yb2N95dKIeV .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-vzkN3yb2N95dKIeV .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-vzkN3yb2N95dKIeV .error-icon{fill:#552222;}#mermaid-svg-vzkN3yb2N95dKIeV .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-vzkN3yb2N95dKIeV .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-vzkN3yb2N95dKIeV .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-vzkN3yb2N95dKIeV .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-vzkN3yb2N95dKIeV .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-vzkN3yb2N95dKIeV .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-vzkN3yb2N95dKIeV .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-vzkN3yb2N95dKIeV .marker{fill:#333333;stroke:#333333;}#mermaid-svg-vzkN3yb2N95dKIeV .marker.cross{stroke:#333333;}#mermaid-svg-vzkN3yb2N95dKIeV svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-vzkN3yb2N95dKIeV p{margin:0;}#mermaid-svg-vzkN3yb2N95dKIeV .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-vzkN3yb2N95dKIeV .cluster-label text{fill:#333;}#mermaid-svg-vzkN3yb2N95dKIeV .cluster-label span{color:#333;}#mermaid-svg-vzkN3yb2N95dKIeV .cluster-label span p{background-color:transparent;}#mermaid-svg-vzkN3yb2N95dKIeV .label text,#mermaid-svg-vzkN3yb2N95dKIeV span{fill:#333;color:#333;}#mermaid-svg-vzkN3yb2N95dKIeV .node rect,#mermaid-svg-vzkN3yb2N95dKIeV .node circle,#mermaid-svg-vzkN3yb2N95dKIeV .node ellipse,#mermaid-svg-vzkN3yb2N95dKIeV .node polygon,#mermaid-svg-vzkN3yb2N95dKIeV .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-vzkN3yb2N95dKIeV .rough-node .label text,#mermaid-svg-vzkN3yb2N95dKIeV .node .label text,#mermaid-svg-vzkN3yb2N95dKIeV .image-shape .label,#mermaid-svg-vzkN3yb2N95dKIeV .icon-shape .label{text-anchor:middle;}#mermaid-svg-vzkN3yb2N95dKIeV .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-vzkN3yb2N95dKIeV .rough-node .label,#mermaid-svg-vzkN3yb2N95dKIeV .node .label,#mermaid-svg-vzkN3yb2N95dKIeV .image-shape .label,#mermaid-svg-vzkN3yb2N95dKIeV .icon-shape .label{text-align:center;}#mermaid-svg-vzkN3yb2N95dKIeV .node.clickable{cursor:pointer;}#mermaid-svg-vzkN3yb2N95dKIeV .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-vzkN3yb2N95dKIeV .arrowheadPath{fill:#333333;}#mermaid-svg-vzkN3yb2N95dKIeV .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-vzkN3yb2N95dKIeV .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-vzkN3yb2N95dKIeV .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-vzkN3yb2N95dKIeV .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-vzkN3yb2N95dKIeV .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-vzkN3yb2N95dKIeV .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-vzkN3yb2N95dKIeV .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-vzkN3yb2N95dKIeV .cluster text{fill:#333;}#mermaid-svg-vzkN3yb2N95dKIeV .cluster span{color:#333;}#mermaid-svg-vzkN3yb2N95dKIeV div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-vzkN3yb2N95dKIeV .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-vzkN3yb2N95dKIeV rect.text{fill:none;stroke-width:0;}#mermaid-svg-vzkN3yb2N95dKIeV .icon-shape,#mermaid-svg-vzkN3yb2N95dKIeV .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-vzkN3yb2N95dKIeV .icon-shape p,#mermaid-svg-vzkN3yb2N95dKIeV .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-vzkN3yb2N95dKIeV .icon-shape .label rect,#mermaid-svg-vzkN3yb2N95dKIeV .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-vzkN3yb2N95dKIeV .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-vzkN3yb2N95dKIeV .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-vzkN3yb2N95dKIeV :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 连续 Turn 检查
Step 循环 (while true)
否
是
是
否
是
否
是
否
是
是
否
否
是
否
异常
关闭 Turn
是
否
signal.aborted?
turnEnds = {kind: 'aborted', reason}

throw error
turnEnds = {kind: 'error', error}

throwError(error)
session.append('turn/end',

{turn, reason: turnEnds})
执行 Step
session.append('step/start', {turn, step})
phase.step = step
for message of decision.messages:

session.append('user/message', message,

{surfaceOp: 'append'})
stepEnd = await this.step(decision.assembly)
if turnEnds === null ||

turnEnds.kind !== 'max-tokens':

turnEnds = stepEnd
session.append('step/end', {turn, step})
打开 Turn 边界
session.append('turn/start', {turn})
phase.turn = turn
turn()
phase.kind === 'running'?
throwError

无驱动器预留
signal = phase.abort.signal
signal.throwIfAborted()
turn = phase.turn + 1
turnEnds = null

target = 'next-turn'
signal.throwIfAborted()
step = phase.step + 1
preStep(target, {turn, step})
decision.kind === 'reject'?
turnEnds = {kind: 'blocked'}

return false
turnEnds &&

decision.messages.length === 0?
break

(Turn 自然结束)
phase.step === 0 &&

decision.messages.length === 0?
turnEnds = {kind: 'completed'}

return false

(首步空 Turn)
signal.throwIfAborted()
signal.throwIfAborted()
turnEnds &&

inbox.nextStep.length === 0?
dispatch.serial('agent/turn-stopping',

{turn, signal})
signal.throwIfAborted()
turnEnds &&

inbox.nextStep.length === 0?
break

(Turn 停止)
target = 'next-step'

(后续 Step 从 next-step claim)
!inbox.hasPending?

return false
phase.abort = new AbortController()
phase.wakeRequested = false
phase.step = 0
return true

(继续下一个 Turn)

关键设计要点:

  1. max-tokens 粘性 :一旦任何 Step 命中 max-tokens 上限,后续正常完成的 Step 不能降级 Turn 结果。if (turnEnds === null || turnEnds.kind !== 'max-tokens') turnEnds = stepEnd 确保粘性。

  2. 首步空 Turn :如果第一个 Step 的 decision.messages.length === 0(消息被 pre-step 重写为空或被 cancel 清除),Turn 仍然拥有边界但不花费 model call,以 completed 结束。

  3. turn-stopping 是 Serial :agent/turn-stopping 是 serial 事件,无 next()。监听器不能委托,只能观察并 steer 延续。Turn 是否停止由数据决定(inbox 是否有 pending),不是监听器顺序决定。

  4. Turn 间重置 :如果 inbox 有 pending,Turn 返回 true,重置 abort(新 Controller 使旧锁存失效)、wakeRequested = false、step = 0,继续下一个 Turn。

  5. finally 总是记录 turn/end :无论 Turn 如何结束(正常、blocked、aborted、error),finally 块总是 session.append('turn/end', {turn, reason: turnEnds})。

4.6 preStep 方法------Step 前置处理

preStep() 在每个 Step 开始前执行,负责 claim 输入、组装提示和运行 agent/pre-step waterfall:
#mermaid-svg-r499WuYdnJuTKmyi{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-r499WuYdnJuTKmyi .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-r499WuYdnJuTKmyi .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-r499WuYdnJuTKmyi .error-icon{fill:#552222;}#mermaid-svg-r499WuYdnJuTKmyi .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-r499WuYdnJuTKmyi .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-r499WuYdnJuTKmyi .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-r499WuYdnJuTKmyi .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-r499WuYdnJuTKmyi .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-r499WuYdnJuTKmyi .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-r499WuYdnJuTKmyi .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-r499WuYdnJuTKmyi .marker{fill:#333333;stroke:#333333;}#mermaid-svg-r499WuYdnJuTKmyi .marker.cross{stroke:#333333;}#mermaid-svg-r499WuYdnJuTKmyi svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-r499WuYdnJuTKmyi p{margin:0;}#mermaid-svg-r499WuYdnJuTKmyi .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-r499WuYdnJuTKmyi .cluster-label text{fill:#333;}#mermaid-svg-r499WuYdnJuTKmyi .cluster-label span{color:#333;}#mermaid-svg-r499WuYdnJuTKmyi .cluster-label span p{background-color:transparent;}#mermaid-svg-r499WuYdnJuTKmyi .label text,#mermaid-svg-r499WuYdnJuTKmyi span{fill:#333;color:#333;}#mermaid-svg-r499WuYdnJuTKmyi .node rect,#mermaid-svg-r499WuYdnJuTKmyi .node circle,#mermaid-svg-r499WuYdnJuTKmyi .node ellipse,#mermaid-svg-r499WuYdnJuTKmyi .node polygon,#mermaid-svg-r499WuYdnJuTKmyi .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-r499WuYdnJuTKmyi .rough-node .label text,#mermaid-svg-r499WuYdnJuTKmyi .node .label text,#mermaid-svg-r499WuYdnJuTKmyi .image-shape .label,#mermaid-svg-r499WuYdnJuTKmyi .icon-shape .label{text-anchor:middle;}#mermaid-svg-r499WuYdnJuTKmyi .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-r499WuYdnJuTKmyi .rough-node .label,#mermaid-svg-r499WuYdnJuTKmyi .node .label,#mermaid-svg-r499WuYdnJuTKmyi .image-shape .label,#mermaid-svg-r499WuYdnJuTKmyi .icon-shape .label{text-align:center;}#mermaid-svg-r499WuYdnJuTKmyi .node.clickable{cursor:pointer;}#mermaid-svg-r499WuYdnJuTKmyi .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-r499WuYdnJuTKmyi .arrowheadPath{fill:#333333;}#mermaid-svg-r499WuYdnJuTKmyi .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-r499WuYdnJuTKmyi .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-r499WuYdnJuTKmyi .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-r499WuYdnJuTKmyi .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-r499WuYdnJuTKmyi .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-r499WuYdnJuTKmyi .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-r499WuYdnJuTKmyi .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-r499WuYdnJuTKmyi .cluster text{fill:#333;}#mermaid-svg-r499WuYdnJuTKmyi .cluster span{color:#333;}#mermaid-svg-r499WuYdnJuTKmyi div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-r499WuYdnJuTKmyi .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-r499WuYdnJuTKmyi rect.text{fill:none;stroke-width:0;}#mermaid-svg-r499WuYdnJuTKmyi .icon-shape,#mermaid-svg-r499WuYdnJuTKmyi .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-r499WuYdnJuTKmyi .icon-shape p,#mermaid-svg-r499WuYdnJuTKmyi .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-r499WuYdnJuTKmyi .icon-shape .label rect,#mermaid-svg-r499WuYdnJuTKmyi .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-r499WuYdnJuTKmyi .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-r499WuYdnJuTKmyi .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-r499WuYdnJuTKmyi :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 返回阶段
decision.kind === 'reject'?
return {kind: 'reject'}
return {kind: 'enter',

messages, assembly}
agent/pre-step Waterfall
dispatch.waterfall(

'agent/pre-step',

{messages: claimed, ...position, signal},

() => ({kind: 'enter',

messages: context === undefined

? claimed

: ...claimed, context}))
signal.throwIfAborted()
组装阶段
loopCtx.systemPrompt.assemble(

assembleContextFor(this, signal))

组装提示段落 + 工具 Schema
signal.throwIfAborted()
renderContextSections(assembly)

渲染上下文段落
runtimeContext.project(

joinContextSections(sections), sections)

投影运行时上下文
Claim 阶段
inbox.claim(target, position.turn)

  1. 清空 next-step → claimed

  2. 如果 target=next-turn:

取出 next-turn0 → claimed

  1. 发布 claimed 通知
    preStep(target, position)
    phase.kind === 'running'

(否则 throw)
signal = phase.abort.signal

关键流程:

  1. Claim :inbox.claim(target, turn) 取出完整批次------先清空 next-step,如果 target === 'next-turn' 还取出 next-turn[0]。第一个 Step 的 target 是 next-turn,后续 Step 是 next-step。

  2. Assemble :systemPrompt.assemble() 组装完整提示(sections + tools + variables),这是一个 waterfall 事件。

  3. Runtime Context 投影 :runtimeContext.project() 检查动态上下文是否变化,如果变化则生成一条 user message 作为上下文快照。

  4. Waterfall :agent/pre-step waterfall 允许监听器重写消息或拒绝 Step。默认行为是 enter,消息为 claimed + context(如果有运行时上下文)。

4.7 step 方法------Step 级行执行

step() 是 Step 级行执行的核心,包含模型请求、流式响应和工具调用:
#mermaid-svg-m7RKKZ4ELZJXFE2K{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-m7RKKZ4ELZJXFE2K .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-m7RKKZ4ELZJXFE2K .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-m7RKKZ4ELZJXFE2K .error-icon{fill:#552222;}#mermaid-svg-m7RKKZ4ELZJXFE2K .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-m7RKKZ4ELZJXFE2K .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-m7RKKZ4ELZJXFE2K .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-m7RKKZ4ELZJXFE2K .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-m7RKKZ4ELZJXFE2K .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-m7RKKZ4ELZJXFE2K .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-m7RKKZ4ELZJXFE2K .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-m7RKKZ4ELZJXFE2K .marker{fill:#333333;stroke:#333333;}#mermaid-svg-m7RKKZ4ELZJXFE2K .marker.cross{stroke:#333333;}#mermaid-svg-m7RKKZ4ELZJXFE2K svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-m7RKKZ4ELZJXFE2K p{margin:0;}#mermaid-svg-m7RKKZ4ELZJXFE2K .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-m7RKKZ4ELZJXFE2K .cluster-label text{fill:#333;}#mermaid-svg-m7RKKZ4ELZJXFE2K .cluster-label span{color:#333;}#mermaid-svg-m7RKKZ4ELZJXFE2K .cluster-label span p{background-color:transparent;}#mermaid-svg-m7RKKZ4ELZJXFE2K .label text,#mermaid-svg-m7RKKZ4ELZJXFE2K span{fill:#333;color:#333;}#mermaid-svg-m7RKKZ4ELZJXFE2K .node rect,#mermaid-svg-m7RKKZ4ELZJXFE2K .node circle,#mermaid-svg-m7RKKZ4ELZJXFE2K .node ellipse,#mermaid-svg-m7RKKZ4ELZJXFE2K .node polygon,#mermaid-svg-m7RKKZ4ELZJXFE2K .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-m7RKKZ4ELZJXFE2K .rough-node .label text,#mermaid-svg-m7RKKZ4ELZJXFE2K .node .label text,#mermaid-svg-m7RKKZ4ELZJXFE2K .image-shape .label,#mermaid-svg-m7RKKZ4ELZJXFE2K .icon-shape .label{text-anchor:middle;}#mermaid-svg-m7RKKZ4ELZJXFE2K .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-m7RKKZ4ELZJXFE2K .rough-node .label,#mermaid-svg-m7RKKZ4ELZJXFE2K .node .label,#mermaid-svg-m7RKKZ4ELZJXFE2K .image-shape .label,#mermaid-svg-m7RKKZ4ELZJXFE2K .icon-shape .label{text-align:center;}#mermaid-svg-m7RKKZ4ELZJXFE2K .node.clickable{cursor:pointer;}#mermaid-svg-m7RKKZ4ELZJXFE2K .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-m7RKKZ4ELZJXFE2K .arrowheadPath{fill:#333333;}#mermaid-svg-m7RKKZ4ELZJXFE2K .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-m7RKKZ4ELZJXFE2K .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-m7RKKZ4ELZJXFE2K .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-m7RKKZ4ELZJXFE2K .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-m7RKKZ4ELZJXFE2K .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-m7RKKZ4ELZJXFE2K .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-m7RKKZ4ELZJXFE2K .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-m7RKKZ4ELZJXFE2K .cluster text{fill:#333;}#mermaid-svg-m7RKKZ4ELZJXFE2K .cluster span{color:#333;}#mermaid-svg-m7RKKZ4ELZJXFE2K div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-m7RKKZ4ELZJXFE2K .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-m7RKKZ4ELZJXFE2K rect.text{fill:none;stroke-width:0;}#mermaid-svg-m7RKKZ4ELZJXFE2K .icon-shape,#mermaid-svg-m7RKKZ4ELZJXFE2K .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-m7RKKZ4ELZJXFE2K .icon-shape p,#mermaid-svg-m7RKKZ4ELZJXFE2K .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-m7RKKZ4ELZJXFE2K .icon-shape .label rect,#mermaid-svg-m7RKKZ4ELZJXFE2K .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-m7RKKZ4ELZJXFE2K .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-m7RKKZ4ELZJXFE2K .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-m7RKKZ4ELZJXFE2K :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 请求循环 (while true)
完成检查
是
否
是
否
是
否
无错误
finish.kind
buildRequest(turn, step, assembly.tools,

system, session.deriveMessages(), signal)
assembler = new BlockAssembler()
chunkSeqs = \[\]
error | aborted
dispatch.waterfall(

'agent/request-error',

{turn, step, provider, failure,

retryPolicy, signal},

() => undefined)
signal.throwIfAborted()
action?.kind === 'retry'?
continue

(重试请求)
throw new LlmError(...)
max-tokens
return {kind: 'max-tokens'}
normal (stop)
message = createAssistantMessage({

content: assembler.blocks(),

source: {provider, model, replayState?}

})
session.append('assistant/message',

{turn, step, message, usage?},

{surfaceOp: 'append', sourceEventSeqs: chunkSeqs})
toolCalls = message.content

.filter(block => block.type === 'tool-call')
toolCalls.length === 0?
return {kind: 'completed'}
executeToolCalls(loopCtx, turn, step,

toolCalls, signal, acceptContext)
concluded?
return {kind: 'completed'}
return null

(Turn 未结束)
流式响应阶段
逐块处理 (for await)
signal.throwIfAborted()
session.append('assistant/chunk',

{turn, step, chunk})
assembler.push(chunk)
stream = preparedCall?.stream(request)

?? loopCtx.llm.stream(request)
signal.throwIfAborted()
signal.throwIfAborted()
流式错误处理
signal.aborted?
content = assembler.interruptedBlocks()
content.length > 0?
session.append('assistant/message',

{turn, step, message: createAssistantMessage({content, source}),

interrupted: true, usage?},

{surfaceOp: 'append', sourceEventSeqs: chunkSeqs})
throw error
step(assembly)
phase.kind === 'running'

(否则 throw)
{turn, step, signal} = phase
system = renderPrompt(assembly)

关键设计要点:

  1. 中断内容保留 :当流式响应被 abort 时,已经接收的内容通过 assembler.interruptedBlocks() 提取,以 interrupted: true 的 assistant/message 事件持久化。这让中断前的部分输出在重放时可见。

  2. 请求错误恢复 :agent/request-error waterfall 允许监听器返回 {kind: 'retry'} 来重试请求(如 compaction-basic 的溢出恢复)。如果监听器不调 next() 而返回 retry,则拥有恢复;调 next() 委托则默认 undefined(失败是终止的)。

  3. 工具调用后继续 :如果工具执行后 concluded 为 false(模型可能需要更多工具调用),返回 null,Step 循环继续------buildRequest 重新派生消息(现在包含工具结果),发起新的模型请求。

  4. 消息派生 :每次请求的 messages 通过 session.deriveMessages() 从日志派生,确保请求内容是日志的纯函数。

4.8 buildRequest 方法------请求构建

buildRequest() 构建冻结的模型请求并绑定到适配器注册:
#mermaid-svg-tLXLA3PYZilNzcwy{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-tLXLA3PYZilNzcwy .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-tLXLA3PYZilNzcwy .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-tLXLA3PYZilNzcwy .error-icon{fill:#552222;}#mermaid-svg-tLXLA3PYZilNzcwy .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-tLXLA3PYZilNzcwy .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-tLXLA3PYZilNzcwy .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-tLXLA3PYZilNzcwy .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-tLXLA3PYZilNzcwy .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-tLXLA3PYZilNzcwy .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-tLXLA3PYZilNzcwy .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-tLXLA3PYZilNzcwy .marker{fill:#333333;stroke:#333333;}#mermaid-svg-tLXLA3PYZilNzcwy .marker.cross{stroke:#333333;}#mermaid-svg-tLXLA3PYZilNzcwy svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-tLXLA3PYZilNzcwy p{margin:0;}#mermaid-svg-tLXLA3PYZilNzcwy .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-tLXLA3PYZilNzcwy .cluster-label text{fill:#333;}#mermaid-svg-tLXLA3PYZilNzcwy .cluster-label span{color:#333;}#mermaid-svg-tLXLA3PYZilNzcwy .cluster-label span p{background-color:transparent;}#mermaid-svg-tLXLA3PYZilNzcwy .label text,#mermaid-svg-tLXLA3PYZilNzcwy span{fill:#333;color:#333;}#mermaid-svg-tLXLA3PYZilNzcwy .node rect,#mermaid-svg-tLXLA3PYZilNzcwy .node circle,#mermaid-svg-tLXLA3PYZilNzcwy .node ellipse,#mermaid-svg-tLXLA3PYZilNzcwy .node polygon,#mermaid-svg-tLXLA3PYZilNzcwy .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-tLXLA3PYZilNzcwy .rough-node .label text,#mermaid-svg-tLXLA3PYZilNzcwy .node .label text,#mermaid-svg-tLXLA3PYZilNzcwy .image-shape .label,#mermaid-svg-tLXLA3PYZilNzcwy .icon-shape .label{text-anchor:middle;}#mermaid-svg-tLXLA3PYZilNzcwy .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-tLXLA3PYZilNzcwy .rough-node .label,#mermaid-svg-tLXLA3PYZilNzcwy .node .label,#mermaid-svg-tLXLA3PYZilNzcwy .image-shape .label,#mermaid-svg-tLXLA3PYZilNzcwy .icon-shape .label{text-align:center;}#mermaid-svg-tLXLA3PYZilNzcwy .node.clickable{cursor:pointer;}#mermaid-svg-tLXLA3PYZilNzcwy .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-tLXLA3PYZilNzcwy .arrowheadPath{fill:#333333;}#mermaid-svg-tLXLA3PYZilNzcwy .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-tLXLA3PYZilNzcwy .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-tLXLA3PYZilNzcwy .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-tLXLA3PYZilNzcwy .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-tLXLA3PYZilNzcwy .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-tLXLA3PYZilNzcwy .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-tLXLA3PYZilNzcwy .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-tLXLA3PYZilNzcwy .cluster text{fill:#333;}#mermaid-svg-tLXLA3PYZilNzcwy .cluster span{color:#333;}#mermaid-svg-tLXLA3PYZilNzcwy div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-tLXLA3PYZilNzcwy .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-tLXLA3PYZilNzcwy rect.text{fill:none;stroke-width:0;}#mermaid-svg-tLXLA3PYZilNzcwy .icon-shape,#mermaid-svg-tLXLA3PYZilNzcwy .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-tLXLA3PYZilNzcwy .icon-shape p,#mermaid-svg-tLXLA3PYZilNzcwy .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-tLXLA3PYZilNzcwy .icon-shape .label rect,#mermaid-svg-tLXLA3PYZilNzcwy .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-tLXLA3PYZilNzcwy .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-tLXLA3PYZilNzcwy .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-tLXLA3PYZilNzcwy :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 最终构建阶段
request = markAgentLoopRequest(deepFreeze({

...header.config,

messages: boundaryMessages,

system?, tools?,

sessionId: session.id,

signal

}))
return {request, preparedCall?}
请求上下文阶段
requestContext = {provider, model, contextWindow?}
previousContext 不匹配?

→ append('request/context', requestContext)
请求头持久化阶段
header = canonicalHeader({config, adapterDefaults?, system, tools})
!requestHeaderLogged?

→ append('request/header', {header, reason: 'initial'|'resume'})

→ requestHeaderLogged = true
baseline === undefined ||

!headerEquals(baseline, header)?

→ append('request/header', {header, reason: 'change'})
适配器解析阶段
loopCtx.llm.prepareCall(proposedConfig, signal)
catch NO_ADAPTER:

config = proposedConfig (中间件可能服务)
config = preparedCall.config
配置提议阶段
dispatch.waterfall('agent/request',

{turn, step, signal}, () => seedConfig)
signal.throwIfAborted()
!proposedConfig.provider || !proposedConfig.model

→ throw 'no provider/model'
配置解析阶段
persistedHeader = session.requestHeader()
route = {provider: options.provider ?? '',

model: options.model ?? ''}
如果 persistedConfig 匹配 route

且 adapterDefaults.reasoningEffort !== true:

reasoningEffort = persistedConfig.reasoningEffort
seedConfig = deepFreeze(

requestHeaderLogged ? requestProposal(persistedHeader)

: {route, reasoningEffort?, maxTokens?})
buildRequest(turn, step, tools, system,

boundaryMessages, signal)

关键设计:

  1. requestProposal :移除 adapter 派生的值(reasoningEffort 和 maxTokens 如果 adapterDefaults 标记为 true),让插件在下一步重新提议。

  2. 首次 vs 后续 :首次请求使用 reason: 'initial' 或 'resume',后续只在 header 变化时记录 reason: 'change'。

  3. markAgentLoopRequest :标记请求为 Loop 构建,携带 sessionId。到达时被 deepFreeze(修改抛错),invariant 校验其内容是日志的纯函数。

  4. request/context:记录 provider/model/contextWindow 变化,供 compaction 等模块读取路由目标。

4.9 RuntimeContextProjection 运行时上下文投影

RuntimeContextProjection 跟踪动态运行时上下文的最后快照,避免重复注入:
#mermaid-svg-IePfxnj1py2kBOfU{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-IePfxnj1py2kBOfU .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-IePfxnj1py2kBOfU .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-IePfxnj1py2kBOfU .error-icon{fill:#552222;}#mermaid-svg-IePfxnj1py2kBOfU .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-IePfxnj1py2kBOfU .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-IePfxnj1py2kBOfU .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-IePfxnj1py2kBOfU .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-IePfxnj1py2kBOfU .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-IePfxnj1py2kBOfU .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-IePfxnj1py2kBOfU .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-IePfxnj1py2kBOfU .marker{fill:#333333;stroke:#333333;}#mermaid-svg-IePfxnj1py2kBOfU .marker.cross{stroke:#333333;}#mermaid-svg-IePfxnj1py2kBOfU svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-IePfxnj1py2kBOfU p{margin:0;}#mermaid-svg-IePfxnj1py2kBOfU .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-IePfxnj1py2kBOfU .cluster-label text{fill:#333;}#mermaid-svg-IePfxnj1py2kBOfU .cluster-label span{color:#333;}#mermaid-svg-IePfxnj1py2kBOfU .cluster-label span p{background-color:transparent;}#mermaid-svg-IePfxnj1py2kBOfU .label text,#mermaid-svg-IePfxnj1py2kBOfU span{fill:#333;color:#333;}#mermaid-svg-IePfxnj1py2kBOfU .node rect,#mermaid-svg-IePfxnj1py2kBOfU .node circle,#mermaid-svg-IePfxnj1py2kBOfU .node ellipse,#mermaid-svg-IePfxnj1py2kBOfU .node polygon,#mermaid-svg-IePfxnj1py2kBOfU .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-IePfxnj1py2kBOfU .rough-node .label text,#mermaid-svg-IePfxnj1py2kBOfU .node .label text,#mermaid-svg-IePfxnj1py2kBOfU .image-shape .label,#mermaid-svg-IePfxnj1py2kBOfU .icon-shape .label{text-anchor:middle;}#mermaid-svg-IePfxnj1py2kBOfU .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-IePfxnj1py2kBOfU .rough-node .label,#mermaid-svg-IePfxnj1py2kBOfU .node .label,#mermaid-svg-IePfxnj1py2kBOfU .image-shape .label,#mermaid-svg-IePfxnj1py2kBOfU .icon-shape .label{text-align:center;}#mermaid-svg-IePfxnj1py2kBOfU .node.clickable{cursor:pointer;}#mermaid-svg-IePfxnj1py2kBOfU .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-IePfxnj1py2kBOfU .arrowheadPath{fill:#333333;}#mermaid-svg-IePfxnj1py2kBOfU .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-IePfxnj1py2kBOfU .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-IePfxnj1py2kBOfU .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-IePfxnj1py2kBOfU .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-IePfxnj1py2kBOfU .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-IePfxnj1py2kBOfU .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-IePfxnj1py2kBOfU .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-IePfxnj1py2kBOfU .cluster text{fill:#333;}#mermaid-svg-IePfxnj1py2kBOfU .cluster span{color:#333;}#mermaid-svg-IePfxnj1py2kBOfU div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-IePfxnj1py2kBOfU .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-IePfxnj1py2kBOfU rect.text{fill:none;stroke-width:0;}#mermaid-svg-IePfxnj1py2kBOfU .icon-shape,#mermaid-svg-IePfxnj1py2kBOfU .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-IePfxnj1py2kBOfU .icon-shape p,#mermaid-svg-IePfxnj1py2kBOfU .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-IePfxnj1py2kBOfU .icon-shape .label rect,#mermaid-svg-IePfxnj1py2kBOfU .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-IePfxnj1py2kBOfU .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-IePfxnj1py2kBOfU .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-IePfxnj1py2kBOfU :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} RuntimeContextProjection
project(current, sections)
retained === undefined &&

current.length === 0?

→ return undefined (无快照需要)
snapshot = current 为空 ? CLEARED : current
retained.text === snapshot?

→ return undefined (无需更新)
createUserMessage({

content: {type:'text', text: snapshot},

source: {kind:'plugin', plugin: SOURCE,

form: 'snapshot', sections}})
事件跟踪
session/event: user/message

  • isOwned?

→ retained = {seq, text}
session/event: replacement

  • sourceEventSeqs 包含 retained.seq?

→ retained = null
构造(恢复)
从后向前扫描 session.events
找到 source.plugin === 'dsh-system-prompt'

的 user/message
在当前 surface 中?

→ retained = {seq, text}
不在 surface

→ retained = null
从未存在

→ retained = undefined

核心逻辑 :project() 只在保留值与当前值不同时生成新快照消息。被 compaction 替换的快照自动变为 null(通过 sourceEventSeqs 检测)。


相关推荐
AC赳赳老秦5 小时前
采集行为合规自检:OpenClaw 自动校验 robots 协议与采集频率,规避违规采集风险
java·开发语言·c++·python·php·deepseek·openclaw
一 铭6 小时前
Pi实战 05:本地模型 · MCP · 安全沙箱篇
人工智能·ai·agent·harness
Am-Chestnuts8 小时前
DeepSeek 回答怎么落成可编辑文档?DS随心转 三种导出路径对比怎么选
word导出·文档转换·办公效率·deepseek
墨心@9 小时前
Coding Agent 与通用 Agent
自然语言处理·agent·harness
落魄实习生10 小时前
Agent Scope Java 2.x 系列【10】Middleware
java·开发语言·ai
通信瓦工10 小时前
利用浊度和电导率测量确定乙二醇基流体的质量
网络·数据库·ai
林伽一10 小时前
100 万输出词元与窄开放,前沿模型发布范式正在改写|2026年10月02日
人工智能·科技·安全·ai
bigdata-余建新11 小时前
week5
ai
网络毒刘11 小时前
Token 账单的「隐形税」:系统提示、工具定义与历史滚动为何比生成贵
agent·token·cursor·成本·mcp
AC赳赳老秦12 小时前
OpenClaw 数据引用规范自动生成:为公开数据构建可信来源标注与标准引用体系
大数据·开发语言·汇编·数据库·人工智能·deepseek·openclaw