乐于分享
好东西不私藏

openclaw会话树型依赖-中篇(查询与生命周期)

openclaw会话树型依赖-中篇(查询与生命周期)

四、树的查询与遍历

4.1 核心遍历算法 — BFS

该算法实现在'src/agents/subagent-registry-queries.ts';
forEachDescendantRun(runs, rootSessionKey, visitor):  pending = [root]     // BFS 队列  visited = Set([root])  // 防循环  for each requester in pending:    for each run where run.requesterSessionKey === requester:      visitor(runId, run)  // 访问该运行记录      if run.childSessionKey not in visited:        visited.add(childKey)        pending.push(childKey)  // 将子会话入队

4.2 查询函数清单

函数
用途

forEachDescendantRun()

BFS 遍历所有后代 run

countActiveDescendantRunsFromRuns()

统计未结束(`!endedAt`)的后代数

countPendingDescendantRunsFromRuns()

统计未完成清理(`!endedAt \|\| !cleanupCompletedAt`)的后代数

countPendingDescendantRunsExcludingRunFromRuns()

排除指定 run 后统计(防自锁)

listDescendantRunsForRequesterFromRuns()

收集所有后代 run 记录列表

listRunsForRequesterFromRuns()

列出直接子 run(支持时间窗口过滤)

listRunsForControllerFromRuns()

按 controller 列出受控 run 

countActiveRunsForSessionFromRuns()

统计活跃 run(含等待后代的)

findRunIdsByChildSessionKeyFromRuns()

按子会话 key 查 runId 

resolveRequesterForChildSessionFromRuns()

查子会话的父会话信息

五、生命周期管理

5.1 Run状态机

CREATED   ↓REGISTERED   ↓STARTED     ↓RUNNING → ENDED            ↓      CLEANUP_STARTED            ↓┌─ DEFER_DESCENDANTS(后代未完成,等待)│           ↓│    WAKE_ON_SETTLE(后代完成后唤醒)└─────────  ↓     ANNOUNCE_PENDING            ↓┌─ ANNOUNCE_RETRYING(指数退避 1s→2s→4s→8s)└─────────  ↓     CLEANUP_COMPLETED            ↓DELETED 或 KEPT(取决于 cleanup 策略)

5.2 结束原因枚举

常量
含义

subagent-complete

正常完成

subagent-error

运行出错

subagent-killed

被父会话主动杀死

session-reset

会话被重置

session-delete

会话被删除

5.3 等待子代完成

当父run结束时,如果还有后代未完成清理:
  1. 设置 ‘wakeOnDescendantSettle = true’
  2. 进入 ’defer-descendants‘ 状态,每 1 秒重新检查
  3. 后代全部 settle 后,唤醒父 run 继续 announce 流程
  4. 硬超时 30 分钟后放弃等待
resolveDeferredCleanupDecision():  如果 expectsCompletionMessage && activeDescendantRuns > 0:    → defer-descendants(延迟 1s 后重试)  如果超过硬超时(30min):    → give-up  如果重试次数 >= 3 或超过普通超时(5min):    → give-up  否则:    → retry(指数退避)

5.4级联杀死(Cascade Kill)

定义在‘src/agents/subagent-control.ts’,当杀死父会话时递归杀死所有后代。

cascadeKillChildren(parentChildSessionKey):  childRuns = listSubagentRunsForController(parent)  for each run in childRuns:    if not visited(run.childSessionKey):      visited.add(childSessionKey)      killSubagentRun(run)      // 中止 PI 运行 + 清空队列 + 标记终止      cascadeKillChildren(childSessionKey)  // 递归杀死孙代

每次 kill 操作会:

  1. `abortEmbeddedPiRun()` — 中止嵌入的 PI 运行
  2. `clearSessionQueues()` — 清空消息队列
  3. 更新 session store:`abortedLastRun = true`
  4. `markSubagentRunTerminated()` — 标记 run 为 killed
  5. 触发 `subagent_ended` 钩子

5.5 Steer(转向控制)

父会话可以重定向正在运行的子 agent:
  1. 标记旧 run 为 `steer-restart`(抑制 announce)
  2. 中止旧嵌入会话 + 清空队列
  3. 等待 `agent.wait()` settle
  4. 发送新指令启动新 run
  5. 替换旧 run 记录为新 run 记录
  6. 速率限制:同一目标最少间隔 2 秒

5.6 生命周期错误宽限期

嵌入 run 可能发出瞬态 error 事件(如 provider 重试)。系统给予15 秒宽限期
  • 15 秒内如果收到 start/end 成功事件 → 取消错误
  • 15 秒后仍无成功信号 → 最终确认为 error 并触发清理

5.7 进程重启恢复

restoreSubagentRunsOnce():  1. 从磁盘恢复 run 记录  2. 清理孤儿 run(session store 中已不存在的)  3. 恢复待完成的清理/announce 流程  4. 启动 sweeper 定时器(每 60s 扫描可归档的 run)