【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/startturn/end
Step 一次模型请求 + 工具调用 Turn 内的一次完整 LLM 交互 step/startstep/enduser/messageassistant/*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. assembleContextForassembleContextFor(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 是 Serialagent/turn-stopping 是 serial 事件,无 next()。监听器不能委托,只能观察并 steer 延续。Turn 是否停止由数据决定(inbox 是否有 pending),不是监听器顺序决定。

  4. Turn 间重置 :如果 inbox 有 pending,Turn 返回 true,重置 abort(新 Controller 使旧锁存失效)、wakeRequested = falsestep = 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. Claiminbox.claim(target, turn) 取出完整批次------先清空 next-step,如果 target === 'next-turn' 还取出 next-turn[0]。第一个 Step 的 target 是 next-turn,后续 Step 是 next-step

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

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

  4. Waterfallagent/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: trueassistant/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 派生的值(reasoningEffortmaxTokens 如果 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 检测)。


相关推荐
一技安身3 小时前
【大模型】Win11 部署 DeepSeek+ RAGFlow0.27.2 (Ollama方案,断网可用)
docker·ai·语言模型
99316664 小时前
一个人用 AI 的困局:它从不反对你,所以我让 5 个 AI 开了场圆桌会议
github·deepseek
武子康4 小时前
把 SGLang 接进 Agent,查一次订单要走几步
人工智能·llm·agent
六面体混凝土移动师4 小时前
我把 Cloudflare 开源了
架构·云计算·agent
韩曙亮5 小时前
【AI 大模型】国内各平台 AI 大模型 价格、性能 对比分析 ② ( 智谱 BigModel | 腾讯云 TokenHub | 千问 AI 平台 )
人工智能·ai·大模型·腾讯云·ai大模型·千问·智谱
掰头战士5 小时前
三道保险丝,最后只能放弃治疗? 一文聊聊我的agent是怎么做死循环检测的
typescript·llm·agent
程序员萤火5 小时前
精简MCP Client 实现 Java版
agent
掰头战士6 小时前
AgentLoop: 从 while(true) 到生产级循环
typescript·llm·agent
张忠琳6 小时前
【deepseek-harness】DeepSeek Harness Agent Loop 模块深度架构分析之二
ai·agent·deepseek·harness·dsh