跳转到内容

14. Tasks、Automations 与后台工作

Craft 的并行能力有三个层次:单 turn 内的 background tool、多个 session 组成的 Task DAG、由事件/定时器触发的 Automation。它们没有另造一套“隐藏 Agent”,而是尽量复用 SessionManager 的 create/send/complete/persist 语义。

能力 调度单位 生命周期 事实源
Background task/shell 一个 tool execution 可跨 turn,通常属 session backend + ManagedSession registry
Task DAG node = child session 一次 run,可暂停/恢复/验证 task.yaml + run log + sessions
Automation event matcher → action 长期 workspace 规则 automations config/history

单一来源:tasks/schema.ts

id: release-audit
title: Release Audit
goal: Verify the release is safe
acceptance_criteria: All critical checks pass
runner: conduct
defaults:
model: claude-sonnet-4-6
permissionMode: allow-all
max_parallel: 3
token_budget: 120000
max_iterations: 2
nodes:
- id: inspect
kind: session
prompt: Inspect the diff
- id: test
kind: session
prompt: Run relevant tests
- id: review
kind: session
depends_on: [inspect, test]
inputs:
findings: ${nodes.inspect.output}
prompt: Synthesize ${inputs.findings}

主要字段:

  • task:goal、acceptance、project/cwd、sources、skills、defaults、params;
  • budgets:token、max parallel、max repair iterations;
  • node:kind、prompt、model/connection/permission、labels/status;
  • graph:depends_on、inputs、outputs;
  • future/control:when、route、loop、for_each、aggregate、approval、retry、timeout、cache。

0.11.2 的 schema 会解析 很多控制流 kind,但 Conductor v1 真正执行的是:

  • kind: session
  • depends_on
  • params/inputs/node output 引用与可选 summarize;
  • max_parallel
  • bounded retry;
  • token budget;
  • completion verification/repair frontier。

route/parallel/map/loop/approval/synthesize/... 等为前向兼容 round-trip,不能因 schema 接受就宣称 runner 已实现。源码在 tasks/schema.ts 明确标注。

tasks/validate.ts 做:

  • id/slug/重复 node;
  • session node prompt 非空;
  • depends_on/ref 目标存在;
  • 将显式依赖与 input ref 依赖 materialize;
  • cycle/self-cycle 检测并返回路径;
  • 入口/出口与指标;
  • unsupported execution kind 警告/错误。

一个隐藏依赖例子:node B 未写 depends_on: [a],但 input 引用 ${nodes.a.output},validator 必须把 A→B 加进图,否则 B 会过早 dispatch。

tasks/refs.ts 支持:

${params.<name>}
${inputs.<name>}
${nodes.<id>.output}
${nodes.<id>.output.<field>}

Input 可写:

inputs:
evidence:
from: ${nodes.audit.output}
summarize: true

有 summarize callback 时先用 mini model 压缩上游大输出;没有则原样传递。引用既驱动 prompt 数据流,也驱动依赖图。

TaskRunner.ts 依赖最小 ConductorSessionHost

createSession()
sendMessage()
setSessionStatus()/setKanbanColumn()
cancelProcessing()
onSessionComplete()
getSessionFinalText()
getSessionWorkingDirectory()

SessionManager 结构性满足这个接口;测试用 mock host,不必启动真实 SDK。

flowchart TD
SPEC["task.yaml"] --> V["validate/materialize deps"]
V --> R["ConductorRun"]
R --> S["schedule ready <= max_parallel"]
S --> CS["create child Session"]
CS --> MSG["send interpolated prompt"]
MSG --> DONE["onSessionComplete"]
DONE --> OUT["save node output + run log"]
OUT --> S
OUT --> VERIFY["all done → orchestrator verify"]
VERIFY -->|PASS| END["complete"]
VERIFY -->|FAIL| REPAIR["reset repair frontier"]
REPAIR --> S

scheduleReady()

  1. 只有 run status=running;
  2. 先检查 token budget;
  3. 计算 running count;
  4. 按 spec 顺序选 pending && all deps done
  5. 不超过 max_parallel,默认 4;
  6. 异步 dispatch;
  7. 无 ready 时判断完成/失败/等待。

Node dispatch 创建普通 child session,带:

  • parent/orchestrator id;
  • task slug/run id/node id;
  • cwd/project/source/skill;
  • model/connection;
  • kanban/status;
  • node prompt + upstream inputs。

Task child 没人守着弹窗。优先级:

node.permissionMode
> task.defaults.permissionMode
> AUTONOMOUS_DEFAULT_MODE = allow-all

源码注释指出缺省不能是 ask(会挂),safe 又可能静默做不成工作。这是明确的产品取舍,不是普适安全答案。实际项目最好要求 task editor 总是持久化显式 default,并在高风险 cwd/source 上支持 sandbox/profile。

SessionManager 统一发 SessionCompletionEvent。Runner:

  • 找到 session 对应 node;
  • 读取最终 assistant text 作为 node output;
  • 写 output 文件;
  • 将 child status/kanban 改 done 或 needs-review;
  • 记录 token usage delta;
  • append run log;
  • 重新调度 dependent。

Token usage 是 session 累计值,所以 runner 记录上次观察值,只加 delta;否则重发 completion 会重复计费。

Node retry 有 limit/backoff/when。dispatch error、timeout、agent error等映射为 failure class;在预算内将 node 重置 pending 并重发。超过 retry:

  • node failed;
  • 下游依赖不再 ready;
  • run最终 failed,除非 trigger 语义允许(v1 受限)。

重试是同 node 新 child session或重新 dispatch,run log记录 attempt;不能覆盖旧失败证据。

Run 累加各 child input+output token。检查发生:

  • schedule 前:超预算不再派新 node;
  • completion 时:刚完成 node 使预算突破,立即 pause;
  • verification/repair 前:防继续消耗。

达到预算是 paused 而非强行把所有结果判 failed,用户可审查/调整。Run log写 budget-breach

注意预算是软边界:一个已在运行的 turn 可能超过剩余额度;没有 provider token hard stop 就不能保证精确上限。

全部 node done 后,不立即宣告 task 完成。若有 orchestrator 且 verifyOnComplete

  1. run 进入 verifying
  2. 给 orchestrator发送 outputs + acceptance criteria;
  3. 监听 orchestrator completion;
  4. 解析最后一行 PASSFAIL,可含 nodes=a,b
  5. 无法解析时有限次数 re-ask,不消耗 repair budget;
  6. FAIL 且未超 max_iterations,计算 repair frontier;
  7. 重置被点名 node及所有传递依赖的 done 状态;
  8. prompt 注入拒绝原因,重新执行;
  9. 达 cap/token budget 则 failed。

Repair frontier 而非全 DAG重跑,可复用仍有效的上游证据。

Run log 是 append-only。重启后 hydrate()

  • replay node started/completed/failed/retry/verification entries;
  • done node只有 output 文件仍存在才复用;
  • crash 时 running/cancelled node回 pending;
  • 恢复 attempts/repairs/token;
  • 继续 schedule。

这是 event sourcing 的轻量用法:task.yaml 是定义,run log 是执行事实,node output 是大 payload projection。

automations/types.ts

AutomationMatcher
event / cron / timezone
optional conditions
permissionMode / labels
actions[]
prompt
webhook

事件分两类:

  • AppEvent:LabelAdd/Remove、PermissionModeChange、FlagChange、SessionStatusChange、SchedulerTick 等;
  • AgentEvent:SessionStart、PreToolUse、PostToolUse 等 SDK bridge 事件。

当前 prompt handler 只处理 App events;Agent event 的旧 command execution 已移除,部分 matcher 只是匹配/记录 no-op。文档不能把所有 schema event都当成 prompt trigger 已完成。

AutomationSystem 每 workspace 拥有:

  • typed EventBus
  • config provider/watcher;
  • PromptHandler;
  • WebhookHandler;
  • SchedulerService;
  • event/history log;
  • retry scheduler。

初始化订阅 handler、压缩旧 history、启动 cron。dispose 停 scheduler、handler 与 retry timers。

Session metadata 更新前后 snapshot 比较:

  • permission mode 变化;
  • labels set diff;
  • flag;
  • status。

只有真实变化才 emit,避免保存同值触发自动化循环。setInitialMetadata() 初始化时不发事件,防启动加载所有 session 重新跑 automation。

支持:

  • time/day/timezone;
  • state field from/to/equality;
  • logical and/or/not。

Matcher 先匹配 event/cron,再评 conditions。条件求值必须纯且使用 event snapshot,而不是在 handler 执行很久后重新读已经变化的 session。

PromptHandler:

  1. 对 AppEvent 找 matching matchers;
  2. 按 matcher 聚合 prompt actions;
  3. 用受限 event env 展开 $VAR/${VAR}
  4. 解析 @source mentions;
  5. 生成 PendingPrompt,携 automation name、session id、labels、permission、connection/model/thinking、Telegram topic等;
  6. callback 给 SessionManager 执行。

SessionManager 可创建新 session或向指定 session发送,并写 triggeredBy 与 history。实际 send 仍走耐久主链。

WebhookHandler:

  • URL/method/header/body 展开;
  • 使用 webhook-safe env,不暴露整个 process.env
  • 安全校验目标;
  • 多 action 并行;
  • 立即最多 2 次 transient retry;
  • 5xx/timeout 可入持久 deferred retry scheduler;
  • URL日志 redaction;
  • 每次结果写 history。

Webhook 是外发数据边界;配置验证要限制 header secrets、内网 URL/SSRF、body 大小和 event 字段泄漏。

Automation history 使用 JSONL。启动时做两层保留/compaction,避免定时器长期运行后无限增长。History entry应包含 matcher/action、触发 event、started/completed、成功/失败和脱敏 endpoint,但不保存 secret header。

Provider tool 可把长任务后台化,事件:

task_backgrounded(taskId, workflowId?)
task_progress(elapsed)
workflow_agent_completed
task_completed(status, outputFile, summary)
shell_backgrounded(shellId, command?)
shell_killed

SessionManager registry 把 toolUseId、task/shell id、turn关联;UI显示卡片/进度。keepBackgroundTasksAlive 决定 turn完成后是否保留 runtime;不保留时 cleanup 标 orphan/stopped。

后台完成发生在两 turn 之间时,backend background event sink唤醒 SessionManager;系统可发送 hidden nudge 让 Agent读取结果,而不伪装成用户 bubble。

flowchart TD
AUTO["Automation event"] --> ORCH["创建/唤醒 orchestrator session"]
ORCH --> TASK["create_task / TaskRunner"]
TASK --> C1["child session A"]
TASK --> C2["child session B"]
C1 --> BG1["background shell/tool"]
C2 --> BG2["background workflow"]
BG1 --> DONE["SessionCompletion seam"]
BG2 --> DONE
DONE --> TASK

组合的关键是每层有明确 completion seam,不互相轮询 UI state。

  • Task schema 超前于 executor,用户可能误以为控制节点已工作;
  • 默认 allow-all child 权限强,需要工作区级隔离;
  • task token budget 是软上限;
  • orchestrator verifying 时用户手动输入存在已知竞态;
  • automation 自触发循环需 event diff/dedup/cycle guard;
  • webhook SSRF/secret leakage 是高风险;
  • background task在 agent/subprocess重启时的恢复语义不完全等同普通 session。

TaskRunner 把 DAG node建成普通 session,从而免费获得模型、source、权限、消息和 UI;Automation 把长期事件变成 prompt/webhook,而 background task处理单 turn 内的长执行。三者的共同基座仍是 SessionManager 的生命周期和持久化。

下一章看另一个跨进程能力:内置浏览器如何在完整桌面中运行,又如何被远程 headless Agent 安全借用。