【openclaw】OpenClaw Tasks 模块超深度架构分析

OpenClaw Tasks 模块超深度架构分析

分析版本:2026-04-20 | 代码目录:src/tasks/ | 风格:Dark Terminal | 源码行数:4,717 行(27 文件,不含测试)


一、模块定位

1.1 业务职责

src/tasks/ 是 OpenClaw 的任务与流程编排引擎(Task & Flow Orchestration Engine)。它为 AI Agent 的异步操作提供完整的生命周期管理------从创建、追踪、进度更新、完成通知到自动清理。业务职责精确定义为:

  1. 任务注册表task-registry.ts)--- 2017 行核心引擎:内存索引 + SQLite 持久化 + 多维查询 + 自动生命周期监听 + 投递通知
  2. 任务执行器task-executor.ts)--- 高级编排 API:创建/启动/完成/失败/重试/取消 + Flow 集成
  3. 流程注册表task-flow-registry.ts)--- 695 行流程引擎:流程 CRUD + 状态机 + revision 乐观锁 + 两种同步模式
  4. 任务状态快照task-status.ts)--- 状态聚合 + 可见性过滤 + 文本脱敏 + 用户面输出格式化
  5. 投递策略task-executor-policy.ts)--- 通知策略决策:done_only / state_changes / silent + 自动投递判定
  6. 所有权访问控制task-owner-access.ts + task-flow-owner-access.ts)--- ownerKey 隔离的 CRUD + 查找
  7. 领域视图task-domain-views.ts)--- 内部记录→插件 API DTO 映射
  8. 持久化层task-registry.store.* + task-flow-registry.store.*)--- SQLite 存储 + 快照/增量双模式
  9. 审计与维护task-registry.audit.ts + task-registry.maintenance.ts + task-flow-registry.*.ts)--- 过期清理 + 孤儿检测 + 状态修复

1.2 在系统中的位置

复制代码
┌─────────────────────────────────────────────────────────────────────┐
│                    AI Agent / Gateway / CLI                         │
│   ┌──────────┐ ┌──────────┐ ┌───────────┐ ┌──────────────────────┐ │
│   │ Sessions │ │ Cron     │ │ Subagent  │ │ ACP Harness         │ │
│   │ API      │ │ Scheduler│ │ Spawner   │ │ (Codex/Claude Code) │ │
│   └─────┬────┘ └─────┬────┘ └─────┬─────┘ └──────────┬───────────┘ │
│         │            │            │                   │             │
│  ┌──────┴────────────┴────────────┴───────────────────┴───────────┐ │
│  │                  tasks/ (本模块)                               │ │
│  │                                                               │ │
│  │  ┌──────────────────┐ ┌──────────────────┐ ┌───────────────┐ │ │
│  │  │  Task Registry   │ │  Flow Registry   │ │ Delivery      │ │ │
│  │  │  (2017L core)    │ │  (695L engine)   │ │ Engine        │ │ │
│  │  │  + 5 indices     │ │  + revision lock │ │ (policy+msg)  │ │ │
│  │  │  + lifecycle     │ │  + 2 sync modes  │ │               │ │ │
│  │  │  + agent events  │ │  + state/wait    │ │               │ │ │
│  │  └────────┬─────────┘ └────────┬─────────┘ └───────┬───────┘ │ │
│  │           └───────────┬────────┘                    │         │ │
│  │  ┌────────────────────┴────────────────────────────┐│         │ │
│  │  │              Store Layer (SQLite)               ││         │ │
│  │  │  runs.sqlite + flows.sqlite                     ││         │ │
│  │  └─────────────────────────────────────────────────┘│         │ │
│  └──────────────────────────────────────────────────────┘         │
│         │            │            │                                │
│  ┌──────┴────────────┴────────────┴─────────────────────────────┐ │
│  │  Agent Events · System Events · Heartbeat · Channel Send     │ │
│  └──────────────────────────────────────────────────────────────┘ │
└───────────────────────────────────────────────────────────────────┘

tasks/异步操作的统一追踪层------所有需要后台执行的 AI Agent 操作(子代理、ACP 会话、Cron 任务、CLI 命令)都通过此模块注册、追踪和通知。

1.3 核心业务价值

价值维度 具体体现 量化指标
生命周期完整性 7 种任务状态 × 8 种流程状态 + 自动状态转换 queued→running→succeeded/failed/timed_out/cancelled/lost
持久化可靠性 SQLite 增量写入 + 快照备份 进程崩溃后自动恢复
多维索引 runId / ownerKey / parentFlowId / relatedSessionKey 4 个索引 O(1) 查找 vs O(n) 扫描
投递通知 3 种策略 + 2 层 fallback + 幂等键 done_only / state_changes / silent
所有权隔离 ownerKey 匹配 + scopeKind(session/system) 安全的多租户任务访问
乐观并发 revision 字段 + expectedRevision 检查 防止 Lost Update
自动清理 cleanupAfter + 7 天保留期 + 过期过滤 防止无限增长
流程编排 managed / task_mirrored 双模式 + 等待/阻塞/恢复 复杂多步骤编排

二、模块整体结构

2.1 文件清单与职责矩阵

文件 行数 导出数 职责层 核心职责
task-registry.ts 2017 30+ Core 任务注册表主引擎
task-executor.ts 738 14 API 高级编排 API
task-flow-registry.ts 695 20+ Core 流程注册表引擎
task-registry.store.sqlite.ts 546 8 Store SQLite 持久化实现
task-registry.maintenance.ts 392 6 Ops 过期清理 + 孤儿修复
task-flow-registry.audit.ts 286 4 Ops 流程审计
task-status.ts 183 8 View 状态快照 + 文本脱敏
task-registry.audit.ts 187 4 Ops 任务审计
task-executor-policy.ts 121 7 Policy 通知策略 + 消息格式化
task-flow-registry.maintenance.ts 131 3 Ops 流程维护
task-owner-access.ts 132 7 Access 任务所有权网关
task-domain-views.ts 95 4 View DTO 映射
task-registry.store.ts 92 7 Store Store 抽象 + 配置
task-registry.types.ts 85 13 Types 核心类型定义
task-flow-registry.types.ts 43 3 Types 流程类型定义
task-registry.summary.ts 56 3 Util 任务汇总统计
task-flow-owner-access.ts 49 4 Access 流程所有权网关
task-registry.audit.shared.ts 41 2 Ops 审计共享逻辑
runtime-internal.ts 36 30+ Bridge Task Registry 内部 API 重导出
task-flow-runtime-internal.ts 21 15+ Bridge Flow Registry 内部 API 重导出
task-registry.paths.ts 30 3 Util 状态目录路径解析
task-registry-control.types.ts 28 1 Types Control runtime 类型
task-status-access.ts 10 1 Bridge 状态快照访问重导出
task-registry.reconcile.ts 5 1 Ops Reconcile 入口
task-flow-registry.paths.ts 10 1 Util Flow 状态目录路径
task-registry-control.runtime.ts 2 1 Bridge Control runtime 懒加载
task-registry-delivery-runtime.ts 1 1 Bridge Delivery runtime 懒加载
task-registry.store.types.ts 6 1 Types Store 快照类型
task-flow-registry.store.types.ts 5 1 Types Flow Store 快照类型
task-flow-registry.store.ts 77 5 Store Flow Store 抽象 + 配置

总计:4,717 行有效代码(27 文件,不含 7 个测试文件 2,352 行)。

2.2 四层架构

复制代码
┌─────────────────────────────────────────────────────────┐
│              API Layer (task-executor.ts)                │
│  createQueuedTaskRun · createRunningTaskRun ·           │
│  completeTaskRunByRunId · failTaskRunByRunId ·          │
│  runTaskInFlow · cancelFlowById · retryBlockedFlow      │
│  → 面向调用者的高级编排 API                               │
├─────────────────────────────────────────────────────────┤
│           Core Layer (task-registry + flow-registry)     │
│  Task Registry: 5 indices + lifecycle listener +        │
│                 delivery engine + idempotent send        │
│  Flow Registry: revision lock + 2 sync modes +          │
│                 state/wait/blocked management            │
│  → 面向内部的低级 CRUD + 状态机                           │
├─────────────────────────────────────────────────────────┤
│           Policy Layer (executor-policy + status)        │
│  shouldAutoDeliver · formatTerminalMessage ·             │
│  sanitizeTaskStatus · buildTaskStatusSnapshot            │
│  → 策略决策 + 用户面格式化                                │
├─────────────────────────────────────────────────────────┤
│           Store Layer (store.ts + store.sqlite.ts)       │
│  SQLite 增量写入 (upsertTask/deleteTask) +               │
│  快照备份 (saveSnapshot/loadSnapshot)                    │
│  → 持久化抽象 + 具体实现                                  │
└─────────────────────────────────────────────────────────┘

2.3 核心类型体系

TaskRecord --- 任务记录(22 个字段)
typescript 复制代码
export type TaskRecord = {
  taskId: string;                   // 全局唯一 ID (crypto.randomUUID())
  runtime: TaskRuntime;             // 执行运行时: subagent|acp|cli|cron
  taskKind?: string;                // 任务类别标签
  sourceId?: string;                // 来源 ID (如 cron job ID)
  requesterSessionKey: string;      // 请求者会话密钥
  ownerKey: string;                 // 所有者密钥 (权限隔离)
  scopeKind: TaskScopeKind;         // 作用域: session|system
  childSessionKey?: string;         // 子会话密钥 (subagent/acp)
  parentFlowId?: string;            // 父流程 ID
  parentTaskId?: string;            // 父任务 ID
  agentId?: string;                 // Agent ID
  runId?: string;                   // 运行 ID (多任务可共享)
  label?: string;                   // 人类可读标签
  task: string;                     // 任务描述 (必填)
  status: TaskStatus;               // 当前状态
  deliveryStatus: TaskDeliveryStatus; // 通知投递状态
  notifyPolicy: TaskNotifyPolicy;   // 通知策略
  createdAt: number;                // 创建时间戳
  startedAt?: number;               // 启动时间戳
  endedAt?: number;                 // 结束时间戳
  lastEventAt?: number;             // 最后事件时间戳
  cleanupAfter?: number;            // 清理时间戳 (自动计算)
  error?: string;                   // 错误信息
  progressSummary?: string;         // 进度摘要
  terminalSummary?: string;         // 终端摘要
  terminalOutcome?: TaskTerminalOutcome; // 终端结果: succeeded|blocked
};
TaskStatus --- 七态状态机
复制代码
queued ──→ running ──→ succeeded ──→ (blocked?)
  │           │            │
  │           ├──→ failed   └──→ (normal success)
  │           ├──→ timed_out
  │           └──→ cancelled
  └──→ lost (backing session disappeared)
TaskFlowRecord --- 流程记录(16 个字段)
typescript 复制代码
export type TaskFlowRecord = {
  flowId: string;                   // 全局唯一 ID
  syncMode: TaskFlowSyncMode;       // task_mirrored|managed
  ownerKey: string;                 // 所有者密钥
  requesterOrigin?: DeliveryContext; // 请求来源 (频道+to+accountId)
  controllerId?: string;            // 控制器 ID (managed 模式必填)
  revision: number;                 // 乐观锁版本号
  status: TaskFlowStatus;           // 当前状态
  notifyPolicy: TaskNotifyPolicy;   // 通知策略
  goal: string;                     // 流程目标描述
  currentStep?: string;             // 当前步骤
  blockedTaskId?: string;           // 阻塞的任务 ID
  blockedSummary?: string;          // 阻塞摘要
  stateJson?: JsonValue;            // 任意状态数据
  waitJson?: JsonValue;             // 等待条件数据
  cancelRequestedAt?: number;       // 取消请求时间
  createdAt: number;                // 创建时间
  updatedAt: number;                // 更新时间
  endedAt?: number;                 // 结束时间
};
TaskFlowStatus --- 八态状态机
复制代码
queued ──→ running ──→ waiting ──→ running (loop)
  │           │            │
  │           │      ┌─────┘
  │           │      blocked ──→ running (retry)
  │           │
  │           ├──→ succeeded
  │           ├──→ failed
  │           ├──→ cancelled (cancelRequestedAt set first)
  │           └──→ lost
  └──→ cancelled

三、核心业务逻辑深度解析

3.1 Task Registry(task-registry.ts)--- 核心引擎

5 个内存索引
typescript 复制代码
const tasks = new Map<string, TaskRecord>();                    // 主表: taskId → TaskRecord
const taskDeliveryStates = new Map<string, TaskDeliveryState>(); // 投递状态: taskId → DeliveryState
const taskIdsByRunId = new Map<string, Set<string>>();          // runId → taskId[]
const taskIdsByOwnerKey = new Map<string, Set<string>>();       // ownerKey → taskId[]
const taskIdsByParentFlowId = new Map<string, Set<string>>();   // parentFlowId → taskId[]
const taskIdsByRelatedSessionKey = new Map<string, Set<string>>(); // sessionKey → taskId[]

索引维护 :每次 updateTask() 调用时,检测 ownerKey/childSessionKey/parentFlowId 是否变化,增量更新索引。避免全量重建(O(n)),改为精确的 add/delete 操作(O(1))。

createTaskRecord() --- 创建任务(完整流程)
复制代码
1. ensureTaskRegistryReady() → 首次调用时从 SQLite 恢复 + 启动 Agent Event 监听器
2. resolveTaskRequesterSessionKey() → 确定 requesterSessionKey
3. resolveTaskScopeKind() → 确定 session/system 作用域
4. resolveTaskOwnerKey() → 确定 ownerKey (优先 params.ownerKey > requesterSessionKey)
5. assertTaskOwner() → 验证 ownerKey 非空 (system 除外)
6. assertParentFlowLinkAllowed() → 验证父流程链接合法性:
   - scopeKind 必须是 session
   - flow 必须存在
   - ownerKey 必须匹配
   - flow 不能已取消
   - flow 不能已终止
7. findExistingTaskForCreate() → 去重检查:
   - 同 runId + runtime + scopeKind + ownerKey + childSessionKey + parentFlowId + label + task → 精确匹配
   - ACP: 同 runId + scope 匹配 → 选择优先级最高的 (cli > 其他)
8. 如有 existing → mergeExistingTaskForCreate() → 增量补全空字段
9. 如无 existing → 创建新记录:
   - taskId = crypto.randomUUID()
   - deliveryStatus = ensureDeliveryStatus() → system→not_applicable, 有ownerKey→pending, 无→parent_missing
   - notifyPolicy = ensureNotifyPolicy() → silent(不可投递) / done_only(默认)
   - cleanupAfter = 终态时自动计算 (endedAt + 7天)
10. 写入主表 + 更新 5 个索引 + 持久化
11. syncFlowFromTask() → 同步父流程状态 (task_mirrored 模式)
12. emitTaskRegistryObserverEvent("upserted")
13. 如已终态 → maybeDeliverTaskTerminalUpdate()
mergeExistingTaskForCreate() --- 幂等合并

设计目的:同一次 ACP 运行可能多次调用 createTaskRecord(每次 LLM turn 都可能报告状态)。幂等合并确保不创建重复记录,而是补全空字段。

合并规则

  • 源数据(sourceId/parentFlowId/parentTaskId/agentId/label/task)只在原记录为空时才填充
  • preferMetadata=true 时允许覆盖已有 label/task
  • deliveryStatus 只能从非 pending → pending(不能回退)
  • notifyPolicy 只能从 silent → 非 silent(不能变回 silent)
updateTask() --- 更新任务(核心写入路径)
复制代码
1. 获取 current record
2. merge patch → next
3. 如果 next 是终态且无 cleanupAfter → 自动设置 (endedAt + 7天)
4. 检测索引变化 → 增量更新 5 个索引
5. persistTaskUpsert() → SQLite 增量写入
6. syncFlowFromTask() → 同步父流程 (task_mirrored)
7. syncManagedFlowCancellationFromTask() → 检查 managed 流程是否所有子任务已终态
8. emitTaskRegistryObserverEvent("upserted")
9. return cloneTaskRecord(next)
ensureListener() --- Agent Event 自动状态转换
typescript 复制代码
listenerStop = onAgentEvent((evt) => {
  // 按 runId + sessionKey 查找匹配的任务
  const scopedTasks = getTasksByRunScope({ runId: evt.runId, sessionKey: evt.sessionKey });
  
  for (const current of scopedTasks) {
    if (isTerminalTaskStatus(current.status)) continue; // 终态任务忽略
    
    const patch: Partial<TaskRecord> = { lastEventAt: now };
    
    if (evt.stream === "lifecycle") {
      if (phase === "start")  → patch.status = "running"
      if (phase === "end")    → patch.status = "succeeded" / "timed_out" (aborted)
      if (phase === "error")  → patch.status = "failed"
    }
    
    updateTask(current.taskId, patch);
    maybeDeliverTaskStateChangeUpdate(); // state_changes 策略通知
    maybeDeliverTaskTerminalUpdate();     // done_only 策略通知
  }
});

设计目的:自动监听 Agent 生命周期事件(start/end/error),无需手动调用 start/complete/fail。当 ACP 或 subagent 运行状态变化时,任务记录自动更新。

3.2 Delivery Engine --- 投递通知

maybeDeliverTaskTerminalUpdate() --- 终态投递
复制代码
1. shouldAutoDeliverTaskTerminalUpdate() 检查:
   - notifyPolicy ≠ silent
   - runtime ≠ subagent (subagent 由调用者直接处理)
   - 是终态
   - deliveryStatus = pending
   
2. tasksWithPendingDelivery 去重 → 防止并发投递

3. shouldSuppressDuplicateTerminalDelivery() → ACP 多 runId 去重

4. resolveTaskDeliveryOwner() → 确定投递目标:
   - 有父流程 → 用流程的 ownerKey + requesterOrigin
   - session 作用域 → 用任务的 ownerKey
   - system 作用域 → 无投递目标

5. canDeliverTaskToRequesterOrigin() → 检查是否有可投递的频道

6. 投递路径:
   ├─ 可投递频道 → sendMessage() 直接发送
   │   ├─ 成功 → deliveryStatus = "delivered"
   │   └─ 失败 → fallback 到 queueTaskSystemEvent()
   │              ├─ 成功 → deliveryStatus = "session_queued"
   │              └─ 失败 → deliveryStatus = "failed"
   └─ 不可投递频道 → queueTaskSystemEvent() 直接走系统事件
       ├─ 成功 → deliveryStatus = "session_queued"
       └─ 失败 → deliveryStatus = "failed"

7. blocked 后续通知:
   - terminalOutcome = "blocked" → 额外 queueBlockedTaskFollowup()
   - 使用不同 contextKey (task:<id>:blocked-followup)

幂等键设计task-terminal:<taskId>:<status>:<outcome> / flow-terminal:<flowId>:<taskId>:<status>:<outcome>。确保同一条消息不会重复发送。

maybeDeliverTaskStateChangeUpdate() --- 状态变更投递
复制代码
1. shouldAutoDeliverTaskStateChange() 检查:
   - notifyPolicy = state_changes
   - deliveryStatus = pending
   - 非终态

2. deliveryState.lastNotifiedEventAt 检查 → 防止重复通知旧事件

3. formatTaskStateChangeMessage() → 生成消息:
   - running → "Background task started: <title>."
   - progress → "Background task update: <title>. <summary>"
   - 其他 → null (不发送)

4. 投递路径同终端投递,但使用不同的幂等键格式:
   task-event:<taskId>:<eventAt>:<eventKind>
   flow-event:<flowId>:<taskId>:<eventAt>:<eventKind>

3.3 Flow Registry(task-flow-registry.ts)--- 流程引擎

两种同步模式
模式 用途 状态同步 控制器
task_mirrored 单任务流程(自动创建) 流程状态 = 任务状态镜像 无需 controllerId
managed 多步骤编排流程 控制器手动设置状态 需要 controllerId

task_mirroredcreateTaskFlowForTask() 自动从任务创建,syncFlowFromTask() 在每次任务更新时自动同步流程状态。一对一无缝映射。

managed :控制器(如 TaskFlow skill)通过 setFlowWaiting()/resumeFlow()/finishFlow()/failFlow() 手动管理流程状态。支持等待(wait)、阻塞(blocked)、恢复(resume)等复杂语义。

Revision 乐观锁
typescript 复制代码
updateFlowRecordByIdExpectedRevision(params: {
  flowId: string;
  expectedRevision: number;  // 调用者持有的 revision
  patch: FlowRecordPatch;
}): TaskFlowUpdateResult {
  const current = flows.get(params.flowId);
  if (!current) return { applied: false, reason: "not_found" };
  if (current.revision !== params.expectedRevision) {
    return { applied: false, reason: "revision_conflict", current: cloneFlowRecord(current) };
  }
  // revision 匹配 → 应用 patch,revision += 1
  return { applied: true, flow: writeFlowRecord(applyFlowPatch(current, params.patch), current) };
}

设计目的:多个子任务可能同时更新同一个流程。revision 确保更新基于最新状态,防止 Lost Update。调用者需要重新读取 flow 并重试。

deriveTaskFlowStatusFromTask() --- 状态映射
typescript 复制代码
task.queued       → flow.queued
task.running      → flow.running
task.succeeded + terminalOutcome="blocked" → flow.blocked
task.succeeded + terminalOutcome=undefined/"succeeded" → flow.succeeded
task.cancelled    → flow.cancelled
task.lost         → flow.lost
task.failed/timed_out → flow.failed

3.4 Task Executor(task-executor.ts)--- 高级编排

ensureSingleTaskFlow() --- 自动流程包装
typescript 复制代码
function isOneTaskFlowEligible(task: TaskRecord): boolean {
  // 条件:session 作用域 + 无父流程 + 可投递 + acp/subagent 运行时
  if (task.parentFlowId?.trim() || task.scopeKind !== "session") return false;
  if (task.deliveryStatus === "not_applicable") return false;
  return task.runtime === "acp" || task.runtime === "subagent";
}

设计目的:ACP/subagent 任务如果没有显式父流程,自动包装为 task_mirrored 流程。这样即使单个任务也有 Flow 的投递和取消能力。

retryBlockedFlowTask() --- 阻塞重试
复制代码
1. resolveRetryableBlockedFlowTask():
   - flow 存在 + status = "blocked"
   - latestTask.status = "succeeded" + terminalOutcome = "blocked"
   
2. 创建新任务:
   - 继承原任务的 runtime/ownerKey/task/label/notifyPolicy
   - parentFlowId = flow.flowId
   - parentTaskId = 原任务的 taskId
   - status = queued/running (由调用者选择)
   
3. 返回 RetryBlockedFlowResult:
   { found, retried, previousTask, task }
cancelFlowById() --- 流程取消(5 步)
复制代码
1. 验证流程存在 + 非终态
2. markFlowCancelRequested() → 设置 cancelRequestedAt (revision 保护)
3. 取消所有活动子任务:
   for each active task → cancelTaskById()
4. 检查是否还有活动子任务:
   - 有 → 返回 "One or more child tasks are still active."
   - 无 → 继续
5. cancelManagedFlowAfterChildrenSettle():
   revision 保护更新 flow.status = "cancelled"

3.5 Task Status(task-status.ts)--- 状态快照

buildTaskStatusSnapshot()
typescript 复制代码
function buildTaskStatusSnapshot(tasks, opts?): TaskStatusSnapshot {
  const now = Date.now();
  const visibleCandidates = tasks.filter(t => !isExpiredTask(t, now));  // 排除已过期
  const active = visibleCandidates.filter(isActiveTask);                 // queued + running
  const recentTerminal = visibleCandidates.filter(t => isRecentTerminalTask(t, now)); // 5 分钟内终态
  const visible = active.length > 0
    ? [...active, ...recentTerminal]  // 有活动任务时显示全部
    : recentTerminal;                  // 只显示最近终态
  const focus = active[0]  // 第一个活动任务
    ?? recentTerminal.find(t => isFailureTask(t))  // 或最近的失败任务
    ?? recentTerminal[0];  // 或最近的终态任务
  
  return { latest, focus, visible, active, recentTerminal,
           activeCount, totalCount, recentFailureCount };
}

visible 逻辑:有活动任务时,活动+最近终态都显示;无活动任务时,只显示最近 5 分钟的终态。过期任务(cleanupAfter <= now)始终不可见。

文本脱敏管线
复制代码
原始文本 → stripInlineLeakedInternalContext()  // 移除内部运行时上下文
        → sanitizeUserFacingText()              // 移除敏感信息
        → replace(/\s+/g, " ").trim()           // 折叠空白
        → truncateUtf16Safe(maxChars)            // 截断 + "..." 后缀

stripInlineLeakedInternalContext :检测 INTERNAL_RUNTIME_CONTEXT_BEGIN 标记和 legacy 格式,截断内部上下文泄漏。防止 AI Agent 的内部运行时提示泄漏到用户可见的通知中。

3.6 Executor Policy(task-executor-policy.ts)--- 策略决策

通知策略矩阵
notifyPolicy 终态通知 运行中通知 适用场景
done_only 默认策略------只在完成/失败时通知
state_changes 需要进度更新的场景
silent 系统内部任务,不需要通知
shouldAutoDeliverTaskTerminalUpdate() 细化规则
typescript 复制代码
- notifyPolicy = "silent" → 不投递
- runtime = "subagent" + status ≠ "cancelled" → 不投递 (subagent 由父会话处理)
- 非终态 → 不投递
- deliveryStatus ≠ "pending" → 不投递 (已投递过)

subagent 例外:subagent 完成时,其结果由父会话的 Agent 直接处理,不需要额外通知。但 cancelled 状态需要通知(用户可能主动取消了)。

消息格式
状态 格式
succeeded "Background task done: {title} (run {id}). {summary}"
succeeded+blocked "Background task blocked: {title} (run {id}). {summary}"
failed "Background task failed: {title} (run {id}). {error}"
timed_out "Background task timed out: {title} (run {id})."
cancelled "Background task cancelled: {title} (run {id})."
lost "Background task lost: {title} (run {id}). {error/fallback}"

四、关键设计决策

4.1 为什么使用 5 个内存索引而非数据库查询?

问题:任务查找可能按 runId、ownerKey、parentFlowId、sessionKey 进行,纯 Map 扫描是 O(n)。

方案:维护 5 个倒排索引(Map<string, Set>),每个索引将一个键映射到 taskId 集合。

权衡

  • 优点:O(1) 查找,无需 SQL 查询
  • 缺点:每次更新需维护索引(增量更新,非全量重建)
  • 持久化:索引不持久化,重启时从 SQLite 全量重建

4.2 为什么使用 Agent Event 监听器自动转换状态?

问题:ACP/subagent 运行时的状态变化(start/end/error)需要同步到任务记录。

方案onAgentEvent() 监听全局事件流,按 runId+sessionKey 匹配任务,自动应用状态 patch。

好处

  1. 调用者无需手动调用 start/complete/fail------事件驱动自动同步
  2. 即使调用者遗漏了状态更新,事件监听器也能补全
  3. 解耦------任务注册表不依赖具体运行时的实现

4.3 为什么投递使用两层 fallback?

问题:直接消息发送可能失败(频道不可用、API 限流等)。

方案

  1. 首选:通过频道直接发送(sendMessage)------即时送达
  2. 回退:通过系统事件队列(enqueueSystemEvent + requestHeartbeatNow)------延迟送达

requestHeartbeatNow:确保主会话尽快被唤醒处理系统事件。没有心跳请求,系统事件可能在下一个心跳周期(30 分钟)才被处理。

4.4 为什么 Flow 使用 revision 乐观锁?

问题:多个子任务可能并发更新同一个流程(如 ACP 多步骤流程)。

方案expectedRevision 参数。调用者读取 flow 时获得 revision,更新时必须传入相同 revision。如果中间有其他更新,revision 不匹配 → 返回 revision_conflict → 调用者重试。

vs 悲观锁:无锁等待,适合低冲突场景。任务更新频率低(秒级),冲突概率低。

4.5 为什么 task_mirrored 流程自动创建?

问题:单个 ACP/subagent 任务没有显式父流程,但需要流程级别的投递和取消能力。

方案ensureSingleTaskFlow() 在创建任务时自动包装为 task_mirrored 流程。流程状态自动跟随任务状态。

好处:所有任务都有统一的"流程"视图------无论是否显式编排。UI 和 API 可以统一通过 flowId 访问。

4.6 为什么 cleanupAfter 自动计算?

问题:任务记录无限增长会消耗内存和磁盘。

方案 :任务进入终态时,自动计算 cleanupAfter = endedAt + 7天。维护任务定期扫描并删除过期记录。

7 天保留期:足够用户回顾最近任务历史,但不会无限堆积。系统任务(scopeKind=system)也有相同的保留期。

相关推荐
FIT2CLOUD飞致云2 小时前
学习笔记丨基于MaxKB实现JumpServer堡垒机自动化巡检
人工智能·ai·开源·智能体·maxkb
是垚不是土2 小时前
Kafka 故障排查周期长?试试 Kdoctor
linux·运维·分布式·ai·kafka·运维开发
军军君012 小时前
【人工智能/AI】项目实战一:AI产品汇总(非完全)
人工智能·ai
艾莉丝努力练剑2 小时前
【Linux线程】Linux系统多线程(九):线程池实现(附代码示例)
linux·运维·服务器·c++·学习·架构
2301_780789662 小时前
游戏盾是如何防护各个重要的游戏端口呢
服务器·网络·人工智能·游戏·架构·零信任
小江的记录本4 小时前
【分布式】分布式核心组件——分布式锁:Redis/ZooKeeper/etcd 实现方案(附全方位对比表)、优缺点、Redlock、时钟回拨问题
java·网络·redis·分布式·后端·zookeeper·架构
小江的记录本4 小时前
【分布式】分布式核心组件——分布式ID生成:雪花算法、号段模式、美团Leaf、百度UidGenerator、时钟回拨解决方案
分布式·后端·算法·缓存·性能优化·架构·系统架构
小锋学长生活大爆炸5 小时前
【教程】在Docker中部署Hermes Agent
docker·容器·agent·教程·工具·openclaw·hermes
HTTP帕克猴子5 小时前
为什么现代网站越来越依赖“中间层架构”?
架构