- 人工智能
- AI 应用
- 交互助手
- AI Agent
【免费下载链接】ironclaw
IronClaw is an Agent OS focused on privacy, security and extensibility
导读
本文围绕 IronClaw 仓库中 docs/internal/design/oobe/AUTOMATION-TASKS-CONTRACT.md 展开,完整梳理了"自动化任务"这一 OOBE(首次运行体验)核心概念的后端接线契约:它最初以AutomationTask领域模型(五个持久事件 + 投影 + 五条 HTTP 路由 + 五个 facade 方法)被提出,后因 PR #7694 落地的持久化 Suggestions 契约而部分被取代。读完本文,你将掌握:原始设计契约的完整骨架(领域模型、事件表、投影规则、传输帧、路由表、Agent 模式门控语义)、实际落地契约的四路由细节(suggestions.list/suggestions.generate/suggestion.start/suggestion.dismiss),以及从ironclaw_product_contracts到ironclaw_webui再到前端 SPA 的源码级实现证据。
一、文档定位:一份被记录在案、部分被取代的设计参考
AUTOMATION-TASKS-CONTRACT.md自身在开头就明确标注了状态:design reference (proposed wiring),即"设计参考(提议的接线方案)"。它的历史角色是为 WebChat v2 的两个 OOBE 概念提供"可评审的接线路径"——包括持久事件、投影、传输帧、HTTP 表面和 facade 方法,供后续 PR 落地实现:
- 已完成的自动化轮播(Completed-automations carousel):位于落地页 composer 上方(原设计中的
components/automation-carousel.tsx); - 线程内联日历改期富预览(Inline calendar reschedule rich-preview):位于线程内部的日历改期卡片(原设计中的
components/calendar-reschedule-card.tsx)。
两者共享同一个动作模型:suggested(已建议)状态支持 Approve / Modify / Cancel 三种操作;automated(已自动化)状态支持 Modify / Revert 两种操作。
需要特别强调文档顶部那条醒目的警示:§1–§3 已被 PR #7694 取代。实际落地的后端改用"基于ScopedFilesystem的 typed store"(带边界 CAS、无 SQL 迁移)和四条 product-surface 路由契约,而非原始的五个事件 + 投影方案。因此阅读本文时应把原契约视为"设计意图的记录",把 docs/internal/design/oobe/VISION-RECONCILIATION.md §1.1 视为"真实契约",两者对照阅读才能获得完整认知。
文档还交代了代码归属背景(#6918 家族文件夹重组后的命名):事件日志在ironclaw_event_log、持久存储在ironclaw_event_store(均位于crates/events/);facadeRebornServicesApi在crates/product/ironclaw_assistant;路由在crates/product/ironclaw_webui/src/webui_v2/。这些与当前仓库结构完全吻合,是理解整个接线路径的地图。
二、原始设计意图 §1:领域模型
原契约规定,一个自动化任务是"持久的、可投影的记录",其 Rust 类型形状是前端 TSAutomationTask的镜像(标识符使用crates/ironclaw_common的 newtype):
pub struct AutomationTaskId(String); // newtype, validated (types.md) pub enum AutomationApp { // #[serde(rename_all = "snake_case")] Gmail, GoogleCalendar, GoogleDocs, Slack, Notion, } pub enum AutomationTaskKind { // snake_case EmailTriage, CalendarAccept, CalendarReschedule, DocInsights, } pub enum AutomationTaskState { // snake_case Suggested, InProgress, Automated, Reverted, Cancelled, }三个设计要点值得展开:
- 标识符 newtype 化:
AutomationTaskId不是裸String,而是经过校验的 newtype,这与 Reborn 的types.md规则一致——所有跨层标识符都要有类型边界,避免字符串散落各处。 - 枚举统一 snake_case:
AutomationApp、AutomationTaskKind、AutomationTaskState均通过 serderename_all = "snake_case"序列化,保证 wire 层与 TS 前端命名一致。 - Payload 默认敏感:Kind 相关的 payload(如
CalendarReschedule、TriagedEmail)与 TS 接口lib/automation-tasks.ts逐字段对齐,且默认视为敏感数据——邮件正文、与会者列表都属于敏感信息,在任何日志写入、持久化追加、投影输出、传输帧、模型可见结果之前,调用方都背负脱敏义务(对应.claude/rules/safety-and-sandbox.md)。
仓库提示:原文档引用的
lib/automation-tasks.ts、hooks/useAutomationTasks.ts等 mock seam 代码已按文档记载被回滚("rolled back so the branch stays code-free"),当前仓库中对应的是下文第七、八节介绍的 suggestions 系列文件,两者不要混淆。
三、持久事件(源真相)与投影
3.1 五个持久事件
原契约要求在 Reborn 运行时事件枚举上新增五个 typed 变体(属主 crate 为ironclaw_event_log,经由ironclaw_event_store的 durable sink 追加,绝不通过 handler 直接广播):
| Event | When(触发时机) | Carries(携带内容) |
|---|---|---|
AutomationTaskProposed | agent 提出任务(Suggest/Plan 模式,或 Auto 模式实际运行之前) | 完整任务,状态为suggested |
AutomationTaskModified | 用户编辑一个 suggested 或 automated 任务 | 任务 id + 校验过的 patch |
AutomationTaskAutomated | 任务运行完成(已批准,或 Auto/Bypass 模式直接运行) | 任务 id + provider 出具的 evidence(见 §6) |
AutomationTaskReverted | 用户撤销一个 automated 任务 | 任务 id + revert evidence |
AutomationTaskCancelled | 用户关闭一条建议 | 任务 id |
每个变体都必须满足:脱敏(redacted)、可重放(replayable),并针对持久化 / 重放 / 投影可见性 / 脱敏 / 排序 / 传输序列化分别编写测试。这体现了 Reborn 事件系统的硬性纪律:事件是唯一的源真相,任何 UI 状态都不是权威。
3.2 投影(Projection)
在ironclaw_event_projections中扩展AutomationTaskProjection,核心约束有四条:
- 按
(tenant, user)作用域过滤——调用方只能看到自己的任务; - 携带投影游标(projection cursor)——支持重放与断点续传(replay/resume);
- 把上述事件折叠成当前的
AutomationTaskState; - 读取语义分两种:轮播读取
state ∈ {automated, reverted}的任务集合;内联卡片按 id 读取单个任务。
同时要求一个专门的跨用户隔离回归测试:为用户 A 提出的任务,绝不能出现在用户 B 的投影里。这条"最小隔离断言"在后续落地契约中同样以(tenant, user)scope 的形式保留了下来(见第七节)。
四、传输层:内联富预览的帧设计
内联日历卡片是"持久的 UI 状态",因此它复用已有的projection → EventStreamManager → SSE/WebSocket通道(events.md规则),不引入定制消息。具体做法是新增一个脱敏的WebChatV2EventFrame变体:
capability_display_preview{ kind: "automation_task", task: <redacted AutomationTask> }前端MessageList已经具备渲染非消息子元素的能力(例如 gates、onboarding),因此它把这一帧映射到<CalendarRescheduleCard>(以及未来的其他 kind)即可。重连时从重放恢复,绝不依赖前端乐观状态——这是 Reborn 事件驱动 UI 的一条总原则:浏览器可以乐观渲染,但持久事实只能来自服务端投影。
五、HTTP 表面与 facade 方法(§5–§6)
5.1 五条路由
原契约规定在ironclaw_webui的src/webui_v2/增加五条路由,同时必须在webui_v2_routes()描述符表中登记对应行——否则tests/webui_v2_descriptors_contract.rs会失败(这正是当前仓库对路由描述符表的强制锁定机制):
| Route ID | Method | Pattern | Effect path |
|---|---|---|---|
webui.v2.list_automation_tasks | GET | /api/webchat/v2/automations/tasks | ProjectionOnly |
webui.v2.approve_automation_task | POST | /api/webchat/v2/automations/tasks/{id}/approve | TurnCoordinator |
webui.v2.modify_automation_task | PATCH | /api/webchat/v2/automations/tasks/{id} | ProductWorkflow |
webui.v2.cancel_automation_task | POST | /api/webchat/v2/automations/tasks/{id}/cancel | ProductWorkflow |
webui.v2.revert_automation_task | POST | /api/webchat/v2/automations/tasks/{id}/revert | TurnCoordinator |
这些是认证调用方路由(tenant/user 作用域),而非 operator 门控路由;handler 只消费RebornServicesApi,错误统一走WebUiV2HttpError(当前仓库中该错误类型正是 handler 返回错误的唯一通道,见 crates/product/ironclaw_webui/CONTRACT.md)。
5.2 五个 facade 方法与真实第三方效应
RebornServicesApi(位于ironclaw_assistant)新增五个方法,每个都返回服务端确认的任务记录,绝不返回乐观回显(events.md规则):
list_automation_tasks(caller) -> Vec<AutomationTask>approve_automation_task(caller, id) -> AutomationTaskmodify_automation_task(caller, id, patch) -> AutomationTaskcancel_automation_task(caller, id) -> AutomationTaskrevert_automation_task(caller, id) -> AutomationTask
两个要点决定了这套接线的安全性:
- Approve/Revert 是真实的第三方效应(Gmail 发送/归档、Calendar 移动/恢复),必须经由 mediated capability host + product adapters(
ironclaw_host_api中的ProductAdapter表面,通过 composition 接线)执行——绝不允许第二条 outbound HTTP 路径。成功只能由 provider 出具的 evidence(message id / event id / revision)加上一次最小化 read-back 来确认,AutomationTaskAutomated/AutomationTaskReverted事件携带这份 evidence。 - Modify 语义按状态分叉:修改suggested任务 = 原地编辑提案(不执行任何效应,直到 Approve);修改已 automated任务 =携带修改重新执行(对 provider 的真实再执行),返回带新 evidence 的全新 automated 记录。前端将后者建模为
rerunModified:刷新完成状态 + 重算派生字段(如邮件发送计数)。因此modify_automation_task必须在服务端按当前状态分支。
六、Agent 模式(composer pill,§7)
原契约第七节定义了落地页 composer 上的模式选择器(原设计中的components/mode-selector.tsx+ storelib/agent-mode.ts,当前持久化到 scoped localStorage),并规划了持久化 home:
GET /api/webchat/v2/settings/agent-mode→{ mode: AgentMode }POST /api/webchat/v2/settings/agent-mode{ mode }→ 确认后的{ mode }- 在 session(
session.features/settings)上暴露agent_mode,使 pill 在加载时水合——与既有的global_auto_approve特性完全一致。
语义接入现有审批系统(ApprovalCard、resolve_gate路径、global_auto_approve特性),四种模式的 gate 行为如下:
| Mode | Gate behavior |
|---|---|
suggest | 每个动作都触发审批 gate(今天的默认行为) |
plan | agent 输出一揽子待办任务计划,一次审批解决整个集合 |
auto | 已获批的任务类型(邮件整理、邀请接受、文档洞察)跳过逐动作 gate;其他任务仍走 gate。这是global_auto_approve的 typed 泛化 |
bypass | 完全不触发 gate(全自动化) |
auto/bypass是特权升级模式,需要为 gate 抑制路径编写显式测试,并为每个自动运行的动作留下审计轨迹。
七、实际落地契约:持久化 Suggestions 四路由
正如第一节所述,原契约 §1–§3 已被 PR #7694 取代。真实的契约冻结在ironclaw_product_contracts中,由 docs/internal/design/oobe/VISION-RECONCILIATION.md §1.1 记录,并与当前仓库源码完全一致:
| Method | WebUI 路由 | Product 操作 |
|---|---|---|
GET | /api/webchat/v2/suggestions | suggestions.list |
POST | /api/webchat/v2/suggestions/generate | suggestions.generate |
POST | /api/webchat/v2/suggestions/{id}/start | suggestion.start |
DELETE | /api/webchat/v2/suggestions/{id} | suggestion.dismiss |
其 wire 类型定义在 crates/contracts/ironclaw_product_contracts/src/product_wire.rs,产品面描述符在 crates/contracts/ironclaw_product_contracts/src/suggestions.rs:
RebornSuggestionsResponse { status: "empty" | "generating" | "ready" | "failed", generation_id?: string, retry_after_seconds?: number, suggestions: RebornSuggestion[], } RebornSuggestion { id, title, description, suggested_prompt, icon, sources, thread_id?, run_id? } RebornSuggestionStartResponse { suggestion_id, thread_id, run_id } RebornSuggestionDismissResponse { suggestion_id, dismissed }关键行为(均有源码佐证):
- 生成是异步的:
POST generate(携带client_action_id作为幂等键)返回202、status: generating和retry_after_seconds提示;客户端轮询GET suggestions直到终态。 - 每 (tenant, user) 只有一套卡片;新一次生成会清除上一套(replace-only)。
- 卡片数量有界 1–5,且字段有硬约束。需要指出的是:产品侧校验(crates/product/ironclaw_assistant/src/suggestions.rs)实际执行的是
title ≤ 48、description ≤ 240、suggested_prompt ≤ 2000、sources 1–5 条且每条 ≤128、icon ≤128,并拒绝控制字符与重复 source;提示词文件prompts/suggestion_generation.md同样写明 "titleis 48 characters or fewer"。 - 存储模型:正如契约警示所述,落地实现不是事件投影,而是基于
ScopedFilesystem的 typed store + 边界 CAS + 无 SQL 迁移;生成以 30 秒租约(GENERATION_LEASE_DURATION_SECONDS = 30)保护,崩溃可安全恢复(源码 crates/product/ironclaw_assistant/src/suggestions.rs)。
八、源码级验证:路由描述符与 handler
当前仓库中这四条路由是真实存在且始终开启的。在 crates/product/ironclaw_webui/src/webui_v2/descriptors.rs 中,每个路由都在webui_v2_routes()描述符表登记了精确的 method、pattern、body 限制、限速与 Effect path:
suggestions_list_descriptor():GET,走read_policy,Effect path 为ProductSurface,无流式;suggestions_generate_descriptor():POST,body_limit_kib(4),限速rate_limit_per_caller(10, 60)(每 60 秒 10 次)——注释明确指出"每个被接受的请求都可能启动一次 provider 支撑的 agent 运行",因此需要限速;suggestion_start_descriptor():POST,NoBody,走通用 mutation 限速;suggestion_dismiss_descriptor():DELETE,NoBody,同样 mutation 限速。
handler 实现位于 crates/product/ironclaw_webui/src/webui_v2/handlers.rs,统一经由query_product_view/invoke_product_command走ProductSurface,错误收敛到WebUiV2HttpError:
list_suggestions调用SUGGESTIONS_LIST_VIEW.descriptor()投影视图;generate_suggestions解析client_action_id后调用SUGGESTIONS_GENERATE_COMMAND;当响应status == Generating时返回202 并设置 HTTPRetry-After头,否则返回 200;retry_after_seconds会被钳制到SUGGESTIONS_MAX_RETRY_AFTER_SECONDS之内;start_suggestion调用SUGGESTION_START_COMMAND,返回{suggestion_id, thread_id, run_id};dismiss_suggestion调用SUGGESTION_DISMISS_COMMAND,返回{suggestion_id, dismissed: true}。
产品面编排在 crates/product/ironclaw_assistant/src/suggestions.rs 中,几个实现细节与契约精神高度吻合:
- scope 按用户:
suggestion_scope以tenant_id + user_id为主键(注释明确 "/suggestionsis a per-user mount by design"),agent/project 仍携带用于文件系统授权与审计上下文; start不在浏览器注入 prompt:start_suggestion服务端先create_thread,再以suggested_prompt作为消息submit_turn——线程和运行都由服务端通过正常 ProductSurface 路径创建;thread_action_id/turn_action_id由tenant:user:suggestion_id的 UUIDv5 派生,保证幂等;- 生成冲突返回 409:
GenerationInProgress映射为409 Conflict,前端据此收敛到胜出的生成(见第九节); - 生成器本身是"零工具推断":
submit_suggestion_generation声明tools: Vec::new()、require_no_approval: true,以suggestion_generation.md作为 system prompt,按schemas/suggestions.output.json结构化输出——即一个只读的、边界内的 canonical run。
九、生产者的行为约束:suggestion_generation.md
生成器的提示词 crates/product/ironclaw_assistant/prompts/suggestion_generation.md 是这条契约的灵魂,它定义了"好建议"的标准,值得逐条理解:
- 先看再建议(Look before you suggest):模型被允许读用户已连接的工具与记忆;"一条基于真实读取的建议胜过五条凭空想象";如果读完无事可做,宁少勿滥。
- 具体优于泛泛:
"Reply to Dana about the contract question from Tuesday"优于"Check your email."。 - 只读是绝对的:生成期间只 read / list,绝不 draft / modify / send / post;"Suggesting an action is your job; taking it is not"——建议可以写进
suggested_prompt,但执行必须由用户触发。 - 只主张亲眼所见:不得声称某个账号/扩展/能力可用,除非有证据(扩展以
installation_phase == active为唯一可用判据);suggested_prompt以用户第一人称书写、可独立成立(接收方看不到这张列表)。
这些约束解释了为什么卡片天然"工具无关":模型能看到扩展,但输出 schema 不声明扩展身份——这直接导向第十一节"connect 去耦"的决策。
十、icon / sources 语义契约
卡片上两个必填字段的契约由 docs/internal/design/oobe/SUGGESTION-ICONS.md 单独定义:
icon:必填、provider 中立的任务类别语义枚举(email/calendar/document/storage/spreadsheet/presentation/code/messaging/notes/web/memory/generic),只控制卡片字形,不是扩展/厂商/能力身份;generic是保证兜底——未知、缺失、遗留值一律映射到generic,保证卡片永远可渲染。sources:1–5 条简洁的人类可读来源标签(如"Gmail"、"GitHub"),是展示字符串而非扩展 ID,前端绝不从它们推导 icon 或 setup 路由。
Rust wire 层刻意把icon存为普通String,使 schema 演进无需持久化迁移;前端渲染由pages/chat/lib/suggestion-icons.tsx负责,位于懒加载的 suggestion surface chunk 中(避免给/chat的 eager bundle 增重)。
十一、前端消费层:API 客户端、数据 hook 与落地表面
落地契约的前端一半在crates/product/ironclaw_webui/frontend/src/pages/chat/下,与第七、八节的服务端一一对应:
- lib/suggestions-api.ts:四个 typed 调用(
fetchSuggestions/generateSuggestions/startSuggestion/dismissSuggestion),文件头注释明确"后端拥有生成、持久化与 suggestion → thread/run 绑定;浏览器从不臆造卡片状态"。pollDelayMs把retry_after_seconds钳制在 1–30 秒,防止缺失值造成热循环、敌意值造成无限挂起。 - hooks/useSuggestions.ts:浏览器中 suggestion 状态的唯一 owner。轮询只在
status === "generating"时按后端节奏运行,终态即停;生成冲突(另一 tab/设备先 claim 的 409)触发invalidateQueries重新读取权威状态;start成功后同时刷新 suggestions 与 threads 两个查询键;dismiss成功后本地立即移除卡片(服务端已提交)。 - components/suggested-task-surface.tsx:四种生成状态驱动四种呈现——
empty→ 生成 CTA(生成消耗真实模型运行,必须用户主动请求);generating→ 品牌化进行指示器 + 静态 skeleton 瓦片(尊重 motion policy);ready→ 横向滚动卡片条;failed→ 重试按钮。start成功后通过返回的thread_id导航到对应线程。
前端细节中还包含两个超出基础契约的入口(issue #7815 F1/F2):抽屉头部的refresh(重新执行 generate,因后端是 replace-only,所以"刷新即诚实刷新")与connect(链接到既有/extensions表面,作为连接流程的第一段,而非卡片内的 connect 状态)。
十二、从设计冲突中沉淀的三个关键决策
VISION-RECONCILIATION.md 记录了契约落地过程中三个已拍板的关键决策,它们直接塑造了当前实现:
- 卡片并行运行——单一活动锁被移除。原
PROPOSAL.md §2A.3以submit_turn返回DeferredBusy/RejectedBusy为据限制"同一时刻一个活动任务";但suggestion.start为每条建议创建独立线程,后端不存在并行约束,Vision 的多卡片抽屉天然并行。 - "+Automation" 被移除。卡片 schema 没有
automation_prompt字段、也没有 automation 路由——与其保留一个无持久化背书的客户端合成 prompt 注入入口,不如直接从卡片上去掉该动作(未来若要做循环自动化,需要单独的契约,而不是在此打补丁)。 - 事件/投影契约被取代。即本文 §1–§3 的
AutomationTask领域模型,被 typed store overScopedFilesystem取代,作为"原始设计意图的记录"保留。
另一个值得注意的取舍是connect 与卡片的解耦:卡片不携带工具身份、不设 connect 状态、不依赖连接即可启动;如果启动的运行需要未连接的工具,agent 会发出既有的AuthRequiredgate 帧,线程内渲染AuthOauthCard——这是一条已上线且无需改动的路径。连接面板独立为"由扩展目录驱动的落地表面"(Connect panel ← extensions catalog),与建议抽屉成为两个并列表面。
十三、当前状态:什么是 stub、什么是 shipped
最后,厘清本文两个契约的"实现真相"(避免把设计文档误当实现文档):
AutomationTask契约(§2–§7)全部未实现:原文档明确 "Everything in §§2–7 isnot implemented"。当时前端基于 mock 数据 + localStorage 模式 store 运行;Rust 事件/投影/路由桩有意不写——零警告 clippy gate 会把未使用的脚手架类型判为告警,必须与首次实现 + 测试一起落地。- Suggestions 契约已 shipped 且始终开启:服务端(
ironclaw_product_contracts描述符、ironclaw_assistant编排、ironclaw_webui四路由)与前端(suggestions-api.ts、useSuggestions.ts、suggested-task-surface.tsx)均已上线,路由无 feature flag 保护。前端表面保持懒加载,其卡片与图标代码不进入/chat的 eager bundle。 - 尚未构建的 Vision 后续项(见 VISION-RECONCILIATION §5.1):live card status(订阅绑定
run_id反映运行中/完成/失败)、V1 冷启动连接面板(批量 OAuth)、Agent 模式选择器(Suggest / Plan / Auto / Bypass,仍无持久化 home)、输入时卡片折叠为 pill 行、命名问候(V5)等。
结语:一份契约的两种命运
AUTOMATION-TASKS-CONTRACT.md的价值不在于它是最终实现,而在于它是一份完整、可评审的接线蓝图:它把"自动化任务"拆解为领域模型、持久事件、投影、传输帧、HTTP 表面、facade 方法与 Agent 门控语义,任何一步都有明确的属主 crate 与纪律约束(脱敏、可重放、服务端确认、无第二 outbound 路径)。而 PR #7694 的 Suggestions 契约则证明了同一套纪律在更轻量的存储模型上同样成立——typed store + CAS 取代事件投影,四条路由取代五条,但"浏览器从不臆造状态、成功只由 provider evidence 确认、卡片不携带工具身份"这些原则原封不动地延续了下来。对于要在 IronClaw 上新增产品表面的开发者,这份文档连同 VISION-RECONCILIATION.md、SUGGESTION-ICONS.md 以及上文列出的源码,构成了从设计到实现最完整的参照系。
- 人工智能
- AI 应用
- 交互助手
- AI Agent
【免费下载链接】ironclaw
IronClaw is an Agent OS focused on privacy, security and extensibility
相关推荐
IronClaw 进程生命周期契约:基于 row-native Journal 的 ProcessJournalStore 持久化权威设计解析
IronClaw 进程生命周期契约:基于 row native Journal 的 ProcessJournalStore 持久化权威设计解析 导读 :本文以
人工智能AI 应用交互助手AI AgentIronClaw Reborn 事件与审计契约:RuntimeEvent、AuditEnvelope 双观测面、脱敏不变量与 JSONL 持久化语义
IronClaw Reborn 事件与审计契约:RuntimeEvent、AuditEnvelope 双观测面、脱敏不变量与 JSONL 持久化语义 IronC
人工智能AI 应用交互助手AI AgentThorium浏览器完整指南:老机器和隐私用户该装哪个 Chromium 分支?
Thorium浏览器完整指南:老机器和隐私用户该装哪个 Chromium 分支? Thorium 浏览器是一个以放射性元素 90 号"钍"命名的 Chromiu
桌面应用跨平台
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考