词元之母TOK.MOM - 平台充值汇率 1:1 即 1 人民币充值到账 1 美元,支持一个 Key 调用近 600+ 海内外模型,限时特价模型低至 1 折,欢迎上岸!
想要详细教程? 请阅读 Kanban 教程 —— 包含四个用户故事(独立开发者、批量任务、带重试的角色流水线、熔断器),并附有各场景的仪表盘截图。本页是参考文档,教程是叙述性说明。
~/.hermes/kanban.db 中的一行记录;每次交接都是任何人都可以读写的一行记录;每个 worker 都是拥有独立身份的完整 OS 进程。~/.hermes/kanban.db 支撑:kanban_* 工具集驱动看板 —— kanban_show、kanban_list、kanban_complete、kanban_block、kanban_heartbeat、kanban_comment、kanban_create、kanban_link、kanban_unblock。调度器在 schema 中已内置这些工具来启动每个 worker;编排器(orchestrator)配置文件也可以通过 kanban 工具集显式启用。模型通过直接调用工具来读取和路由任务,而不是通过 shell 执行 hermes kanban。详见下方Worker 如何与看板交互。hermes kanban …、斜杠命令 /kanban … 或仪表盘驱动看板。 这些界面面向人类和自动化场景——即没有工具调用模型的场合。kanban_db 层路由,因此读取视图一致,写入不会产生偏差。本页其余部分展示 CLI 示例,因为它们便于复制粘贴,但每个 CLI 动词都有模型使用的等效工具调用。delegate_task 无法处理的工作负载:inbox-triage、ops-review),随时间积累记忆。docs/hermes-kanban-v1-spec.pdf。delegate_task 的对比delegate_task | Kanban | |
|---|---|---|
| 形态 | RPC 调用(fork → join) | 持久化消息队列 + 状态机 |
| 父级 | 阻塞直到子级返回 | create 后即发即忘 |
| 子级身份 | 匿名子 agent | 具有持久记忆的具名配置文件 |
| 可恢复性 | 无 —— 失败即失败 | 阻塞 → 解除阻塞 → 重新运行;崩溃 → 回收 |
| 人工介入 | 不支持 | 随时可评论 / 解除阻塞 |
| 每任务 agent 数 | 一次调用 = 一个子 agent | 任务生命周期内 N 个 agent(重试、审查、跟进) |
| 审计追踪 | 上下文压缩后丢失 | 永久保存在 SQLite 行中 |
| 协调方式 | 层级式(调用方 → 被调用方) | 对等式 —— 任意配置文件可读写任意任务 |
delegate_task 是函数调用;Kanban 是工作队列,每次交接都是任意配置文件(或人类)可见和编辑的一行记录。delegate_task 的场景: 父 agent 在继续之前需要一个简短的推理答案,无需人工介入,结果返回到父 agent 的上下文中。delegate_task。default 看板,在本文档章节之外不会看到"board"这个词。triage | todo | ready | running | blocked | done | archived)、可选租户命名空间、可选幂等键(用于重试自动化的去重)的一行记录。task_links 行,记录父 → 子依赖关系。当所有父任务变为 done 时,调度器将 todo → ready。scratch(默认)—— 在 ~/.hermes/kanban/workspaces/<id>/ 下(非默认看板为 ~/.hermes/kanban/boards/<slug>/workspaces/<id>/)创建的临时目录。任务完成时删除 —— scratch 是临时性的,worker(或 hermes kanban complete <id>)将任务标记为完成的那一刻,目录即被清除。如果想保留 worker 的输出,请使用 worktree: 或 dir:<path>。在某次安装中首次创建 scratch 工作区时,调度器会记录警告并在任务上发出 tip_scratch_workspace 事件(可通过 hermes kanban show <id> 查看)。dir:<path> —— 现有的共享目录(Obsidian vault、邮件运维目录、每账号文件夹)。必须是绝对路径。 像 dir:../tenants/foo/ 这样的相对路径在调度时会被拒绝,因为它们会相对于调度器碰巧所在的 CWD 解析,这是模糊的,也是混淆代理(confused-deputy)逃逸向量。路径本身是受信任的 —— 这是你的机器、你的文件系统,worker 以你的 uid 运行。这是受信任本地用户的威胁模型;kanban 设计为单主机。完成时保留。worktree —— 用于编码任务的 git worktree,位于 .worktrees/<id>/ 下。使用 worktree:<path> 固定确切的目标路径。Worker 端的 git worktree add 创建它,提供 --branch 时使用该分 支。完成时保留。kanban.dispatch_in_gateway: true)。每次 tick 一个调度器扫描所有看板;worker 启动时固定了 HERMES_KANBAN_BOARD,因此无法看到其他看板。在同一任务上连续启动失败 kanban.failure_limit 次(默认:2)后,调度器会以最后一个错误为原因自动阻塞该任务 —— 防止因配置文件不存在、工作区无法挂载等原因导致的反复抖动。--tenant business-a)。租户是软过滤器;看板是硬隔离边界。default 的看板(DB 位于 ~/.hermes/kanban.db,保持向后兼容)。只需要一个工作流的用户无需了解看板;该功能是可选启用的。~/.hermes/kanban/boards/<slug>/kanban.db)。workspaces/ 和 logs/ 目录。HERMES_KANBAN_BOARD,worker 可访问的每个 kanban_* 工具都会读取它。--board <slug>。HERMES_KANBAN_BOARD 环境变量(调度器在启动 worker 时设置,因此 worker 无法看到其他看板)。~/.hermes/kanban/current —— 由 hermes kanban boards switch 持久化的 slug。default。..)在 CLI 层被拒绝,以防止路径遍历技巧命名看板。hermes dashboard → Kanban 标签页在存在多个看板(或任何看板有任务)时,顶部会显示看板切换器。单看板用户只看到一个小的 + New board 按钮;切换器在需要时才显示。localStorage 中,因此在重新加载后仍然有效,不会影响你打开的终端中 CLI 的 current 指针。default 看板上显示。确认后,将看板目录移动到 boards/_archived/。?board=<slug> 进行看板范围限定。事件 WebSocket 在连接时固定到一个看板;在 UI 中切换会针对新看板打开一个新的 WS。kanban_* 工具调用驱动任务,而不是 CLI 命令 —— 详见Worker 如何与看板交互。t_abcd 并启动 researcher 配置文件时,该 worker 的模型做的第一件事是调用 kanban_show() 读取其任务。它不会运行 hermes kanban show t_abcd。HERMES_KANBAN_DISPATCH_IN_GATEWAY=0 在运行时覆盖配置标志以进行调试。标准 gateway 监督适用:直接运行 hermes gateway start,或将 gateway 配置为 systemd 用户单元(参见 gateway 文档)。没有运行中的 gateway,ready 任务会保持原状,直到 gateway 启动 —— hermes kanban create 在创建时会对此发出警告。hermes kanban daemon 作为单独进程运行已弃用;请使用 gateway。如果你确实无法运行 gateway(无头主机策略禁止长期运行的服务等),--force 逃生舱口在一个发布周期内保持旧的独立守护进程可用,但同时运行 gateway 内嵌调度器和针对同一 kanban.db 的独立守护进程会导致认领竞争,不受支持。hermes kanban。 当调度器启动 worker 时,它在子进程环境中设置 HERMES_KANBAN_TASK=t_abcd,该环境变量在模型的 schema 中启用专用的 kanban 工具集。同一工具集也可供在工具集配置中启用 kanban 的编排器配置文件使用。这些工具通过 Python kanban_db 层直接读取和修改看板,与 CLI 的做法相同。运行中的 worker 像调用任何其他工具一样调用这些工具;它从不看到或需要 hermes kanban CLI。| 工具 | 用途 | 必需参数 |
|---|---|---|
kanban_show | 读取当前任务(标题、正文、先前尝试、父级交接、评论、完整预格式化的 worker_context)。默认使用环境变量中的任务 id。 | — |
kanban_list | 列出带有 assignee、status、tenant、归档可见性和限制过滤器的任务摘要。供编排器发现看板工作使用。 | — |
kanban_complete | 以 summary + metadata 结构化交接完成任务。 | summary / result 至少一个 |
kanban_block | 以 reason 上报需要人工输入。 | reason |
kanban_heartbeat | 在长时间操作期间发出存活信号。纯副作用。 | — |
kanban_comment | 向任务线程追加持久化备注。 | task_id、body |
kanban_create | (编排器)将任务扇出为带有 assignee、可选 parents、skills 等的子任务。 | title、assignee |
kanban_link | (编排器)事后添加 parent_id → child_id 依赖边。 | parent_id、child_id |
kanban_unblock | (编排器)将被阻塞的任务移回 ready。 | task_id |
# 模型的工具调用,按顺序:
kanban_show() # 无参数 —— 使用 HERMES_KANBAN_TASK
# (模型读取返回的 worker_context,通过终端/文件工具完成工作)
kanban_heartbeat(note="halfway through — 4 of 8 files transformed")
# (更多工作)
kanban_complete(
summary="migrated limiter.py to token-bucket; added 14 tests, all pass",
metadata={"changed_files": ["limiter.py", "tests/test_limiter.py"], "tests_run": 14},
)kanban_show()
kanban_create(
title="research ICP funding 2024-2026",
assignee="researcher-a",
body="focus on seed + series A, North America, AI-adjacent",
)
# → 返回 {"task_id": "t_r1", ...}
kanban_create(title="research ICP funding — EU angle", assignee="researcher-b", body="…")
# → 返回 {"task_id": "t_r2", ...}
kanban_create(
title="synthesize findings into launch brief",
assignee="writer",
parents=["t_r1", "t_r2"], # 两者都完成时推进到 ready
body="one-pager, 300 words, neutral tone",
)
kanban_complete(summary="decomposed into 2 research tasks + 1 writer; linked dependencies")kanban_list、kanban_create、kanban_link、kanban_unblock,以及对外部任务的 kanban_comment —— 通过同一工具集提供;约定(由 kanban-orchestrator skill 强制执行)是 worker 配置文件不进行扇出或路由无关工作,编排器配置文件不执行实现工作。调度器启动的 worker 仍然针对破坏性生命周期操作限定在任务范围内,无法修改无关任务。hermes kanbanhermes kanban complete,而容器中没有安装 hermes,也没有挂载 ~/.hermes/kanban.db。kanban 工具在 agent 自己的 Python 进程中运行,无论终端后端如何,始终能访问 ~/.hermes/kanban.db。--metadata '{"files": [...]}' 是潜在的隐患。结构化工具参数完全绕过了这个问题。hermes chat 会话在其 schema 中没有任何 kanban_* 工具,除非活动配置文件为编排器工 作显式启用了 kanban 工具集。调度器启动的任务 worker 因为设置了 HERMES_KANBAN_TASK 而获得任务范围的工具;编排器配置文件通过配置获得更广泛的路由界面。对于从不使用 kanban 的用户,没有工具膨胀。kanban-worker 和 kanban-orchestrator skill 教导模型何时调用哪个工具以及调用顺序。kanban_complete(summary=..., metadata={...}) 是有意灵活的:summary 是人类可读的收尾说明,metadata 是机器可读的交接信息,下游 agent、审查者或仪表盘可以直接复用,无需从文本中提取。{
"changed_files": ["path/to/file.py"],
"verification": ["pytest tests/hermes_cli/test_kanban_db.py -q"],
"dependencies": ["parent task id or external issue, if any"],
"blocked_reason": null,
"retry_notes": "what failed before, if this was a retry",
"residual_risk": ["what was not tested or still needs human review"]
}metadata。改为存储指针和摘要。如果任务没有文件或测试,在 summary 中明确说明,并在 metadata 中放置确实存在的证据,例如来源 URL、issue id 或手动审查步骤。kanban-worker skill。它通过工具调用(而非 CLI 命令)教导 worker 完整的生命周期:kanban_show() 读取标题 + 正文 + 父级交接 + 先前尝试 + 完整评论线程。cd $HERMES_KANBAN_WORKSPACE,在那里完成工作。kanban_heartbeat(note="...")。如果你的工作可能运行超过 1 小时,请至少每小时调用一次 kanban_heartbeat —— 调度器会回收运行时间超过 kanban.dispatch_stale_timeout_seconds(默认 4 小时)且最近一小时内没有心跳的任务,认为 worker 在没有清理的情况下崩溃了。回收是无害的(任务返回 ready 重新调度,不增加失败计数器),但你会失去当前运行的进度。kanban_complete(summary="...", metadata={...}) 完成,或在卡住时以 kanban_block(reason="...") 完成。kanban_complete / kanban_block 调用是 worker 协议的一部分。如果 worker 进程以状态 0 退出而任务仍处于 running 状态,调度器将其视为协议违规,发出 protocol_violation 事件,并在下一个 tick 自动阻塞任务而不是重新启动它进入同一循环。这通常意味着模型写了一个纯文本答案并退出,而没有使用 Kanban 工 具界面。kanban-worker 是一个内置 skill,在安装和更新期间同步到每个配置文件 —— 无需单独的 Skills Hub 安装步骤。验证它是否存在于你用于 kanban worker 的配置文件中(researcher、writer、ops 等):--skills kanban-worker,因此即使配置文件的默认 skills 配置不包含它,worker 也始终拥有该模式库。translation skill 的翻译任务、需要 github-code-review 的审查任务、需要 security-pr-audit 的安全审计。与其每次都编辑受让人的配置文件,不如直接将 skill 附加到任务上。kanban_create 工具的 skills 数组:kanban_create(
title="translate README to Japanese",
assignee="linguist",
skills=["translation"],
)
kanban_create(
title="audit auth flow",
assignee="reviewer",
skills=["security-pr-audit", "github-code-review"],
)--skill:kanban-worker 的补充 —— 调度器为每个 skill(以及内置的)发出一个 --skills <name> 标志,因此 worker 启动时加载了所有这些 skill。skill 名称必须与受让人配置文件上实际安装的 skill 匹配(运行 hermes skills list 查看可用内容);没有运行时安装。kanban-orchestrator skill 将此编码为工具调用模式:反诱惑规则、Step-0 配置文件发现提示(调度器在未知受让人名称上静默失败,因此编排器必须将每张卡片落地到你机器上实际存在的配置文件),以及以 kanban_create / kanban_link / kanban_comment 为核心的分解手册。# 来自用户的目标:"draft a launch post on the ICP funding landscape"
kanban_create(title="research ICP funding, NA angle", assignee="researcher-a", body="…") # → t_r1
kanban_create(title="research ICP funding, EU angle", assignee="researcher-b", body="…") # → t_r2
kanban_create(
title="synthesize ICP funding research into launch post draft",
assignee="writer",
parents=["t_r1", "t_r2"], # 两个研究员都完成时推进到 'ready'
body="one-pager, neutral tone, cite sources inline",
) # → t_w1
# 可选:事后发现的跨切依赖,无需重新创建任务
kanban_link(parent_id="t_r1", child_id="t_followup")
kanban_complete(
summary="decomposed into 2 parallel research tasks → 1 synthesis task; writer starts when both researchers finish",
)kanban-orchestrator 是一个内置 skill。它在安装和更新期间同步到每个配置文件,因此无需单独的 Skills Hub 安装步骤。验证它是否存在于你的编排器配置文件中:kanban、gateway、memory)的配置文件配对,这样编排器即使尝试也无法执行实现任务。/kanban CLI 和斜杠命令足以无头运行看板,但可视化看板通常是人工介入的正确界面:分诊、跨配置文件监督、阅 读评论线程以及在列之间拖动卡片。Hermes 将此作为内置仪表盘插件在 plugins/kanban/ 中提供 —— 不是核心功能,不是单独的服务 —— 遵循扩展仪表盘中描述的模型。triage、todo、ready、running、blocked、done(开启切换时还有 archived)。triage 是粗略想法的停车列。默认情况下(kanban.auto_decompose: true),调度器会自动对落在这里的任务运行分解器 —— 编排器配置文件读取粗略想法,查看你的配置文件名册(含描述),并将任务扇出为路由到最合适专家的小型子任务图。原始任务作为每个子任务的父级保持存活,因此当所有子任务完成时,编排器会重新唤醒以判断完成情况,并在工作未完成时添加更多任务。点击页面顶部的 Orchestration: Auto/Manual 切换按钮(或设置 kanban.auto_decompose: false)切换到手动模式,在手动模式下分诊任务保持原位,直到你点击卡片上的 ⚗ Decompose 或运行 hermes kanban decompose <id>。对于不需要扇出的任务(或没有编排器配置文件的设置),✨ Specify 按钮通过相同的 LLM 机制进行单任务规格重写(标题 + 正文,包含目标、方法、验收标 准)。详见下方自动与手动编排。N/M 子任务已完成)以及"N 前创建"。每张卡片的复选框启用多选。task_events 表 ;任何配置文件(CLI、gateway 或另一个仪表盘标签页)操作后,看板立即反映变化。重新加载经过防抖处理,因此一批事件只触发一次重新获取。PATCH /api/plugins/kanban/tasks/:id,通过与 CLI 使用的相同 kanban_db 代码路由 —— 三个界面永远不会产生偏差。移动到破坏性状态(done、archived、blocked)时会提示确认。触摸设备使用基于指针的回退,因此看板可以在平板电脑上使用。+,输入标题、受让人、优先级,以及(可选)从所有现有任务的下拉菜单中选择父任务。按 Enter 创建任务,Shift+Enter 在标题字段中插入换行,或按 Escape 取消。从 Triage 列创建会自动将新任务 停放在分诊中。http(s) / mailto: 链接、项目符号列表),带有"编辑"按钮可切换到文本区域。Markdown 渲染是一个微型、防 XSS 的渲染器 —— 每次替换都在 HTML 转义的输入上运行,只有 http(s) / mailto: 链接通过,并且始终设置 target="_blank" + rel="noopener noreferrer"。× 用于取消 链接,加上所有其他任务的下拉菜单用于添加新的父级或子级。循环尝试在服务器端被拒绝并给出清晰的消息。hermes kanban decompose <id> / specify <id> / --all)、任何 gateway 平台(/kanban decompose <id>)以及通过 POST /api/plugins/kanban/tasks/:id/decompose 和 …/specify 以编程方式访问。在 config.yaml 的 auxiliary.kanban_decomposer 和 auxiliary.triage_specifier 下配置模型。config.yaml 中的 dashboard.kanban.default_tenant)、受让人下拉菜单、"显示已归档"切换、"按配置文件分组"切换,以及推动调度器按钮,这样你就不必等待下一个 60 秒 tick。--color-*、--radius、--font-mono 等),因此它会随活动的仪表盘主题自动重新换肤。kanban.auto_decompose: true。Gateway 内嵌调度器在每个 tick 运行分解器,受 kanban.auto_decompose_per_tick(默认每 tick 3 个任务)限制,以防批量加载分诊任务时突发消耗辅助 LLM。分解器读取粗略想法,查看你安装的配置文件及其描述,并要求 LLM 生成 JSON 任务图:要启动哪些任务、分配给谁,以及哪些依赖哪些。原始分诊任务成为图中每个叶节点的父级,因此它保持存活直到整个图完成 —— 然后推进回 ready,让其受让人(编排器配置文件)判断完成情况,并在工作未完成时添加更多任务。这是"丢一行描述,走开"的流程。kanban.auto_decompose: false。分诊任务保持在分诊中,直到你操作。点击卡片上的 ⚗ Decompose 按钮,运行 hermes kanban decompose <id>(或 --all),或从聊天中使用 /kanban decompose <id>。这与看板的预分解器行为一致,适合需要完全控制运行时机的场景。config.yaml。两种模式都与 hermes kanban specify 共存 —— 当你不想扇出时,它仍然可用作单任务规格重写。hermes profile create --description "..."、hermes profile describe <name> --text "..."、hermes profile describe <name> --auto(LLM 从配置文件安装的 skill + 模型自动生成),或仪表盘展开的 Orchestration settings 面板中的每配置文件编辑器来设置。没有描述的配置文件仍然出现在名册中 —— 它们可以按名称路由,只是精度较低。分解器绝不会将子任务落地为 assignee=None:当 LLM 选择未知配置文件时,子任务路由到 kanban.default_assignee(如果未设置,则路由到活动默认配置文件)。~/.hermes/config.yaml 的 kanban: 下):| 键 | 默认值 | 用途 |
|---|---|---|
auto_decompose | true | 调度器每 tick 自动运行分解器。 |
auto_decompose_per_tick | 3 | 每个调度器 tick 的分解上限。超出部分推迟到下一个 tick。 |
orchestrator_profile | "" | 拥有分解权的配置文件。空 = 回退到活动默认配置文件。 |
default_assignee | "" | LLM 选择未知配置文件时子任务的落地位置。空 = 回退到活动默认配置文件。 |
| 键 | 用途 |
|---|---|
auxiliary.kanban_decomposer | 生成任务图的模型(由 Decompose 调用)。设置 provider/model 以覆盖主聊天模型。 |
auxiliary.profile_describer | 自动生成配置文件描述的模型(由 hermes profile describe --auto 调用)。 |
┌────────────────────────┐ WebSocket (tails task_events)
│ React SPA (plugin) │ ◀──────────────────────────────────┐
│ HTML5 drag-and-drop │ │
└──────────┬─────────────┘ │
│ REST over fetchJSON │
▼ │
┌────────────────────────┐ writes call kanban_db.* │
│ FastAPI router │ directly — same code path │
│ plugins/kanban/ │ the CLI /kanban verbs use │
│ dashboard/plugin_api.py │
└──────────┬─────────────┘ │
│ │
▼ │
┌────────────────────────┐ │
│ ~/.hermes/kanban.db │ ───── append task_events ──────────┘
│ (WAL, shared) │
└────────────────────────┘/api/plugins/kanban/ 下,并受仪表盘的临时会话 token 保护:| 方法 | 路径 | 用途 |
|---|---|---|
GET | /board?tenant=<name>&include_archived=… | 按状态列分组的完整看板,加上用于过滤下拉菜单的租户和受让人 |
GET | /tasks/:id | 任务 + 评论 + 事件 + 链接 |
POST | /tasks | 创建(封装 kanban_db.create_task,接受 triage: bool 和 parents: [id, …]) |
PATCH | /tasks/:id | 状态 / 受让人 / 优先级 / 标题 / 正文 / 结果 |
POST | /tasks/bulk | 对 ids 中的每个 id 应用相同的补丁(状态 / 归档 / 受让人 / 优先级)。每个 id 的失败不会中止其他操作 |
POST | /tasks/:id/comments | 追加评论 |
POST | /tasks/:id/specify | 运行分诊规格器 —— 辅助 LLM 充实任务正文并将其从 triage 推进到 todo。返回 {ok, task_id, reason, new_title};"不在分诊中" / 无辅助客户端 / LLM 错误时 ok=false 并附人类可读原因,返回 200 而非 4xx |
POST | /tasks/:id/decompose | 运行 kanban 分解器 —— 辅助 LLM 生成任务图,辅助函数原子性创建子任务 + 链接根任务 + 翻转 triage → todo。返回 {ok, task_id, reason, fanout, child_ids, new_title}。与 /specify 相同的 LLM 错误返回 200 约定。 |
GET | /profiles | 列出已安装的配置文件及其描述(供仪表盘的配置文件描述编辑器和编排器选择器使用)。 |
PATCH | /profiles/:name | 设置或清除配置文件的描述(用户编写 —— description_auto: false)。返回 {ok, profile, description}。 |
POST | /profiles/:name/describe-auto | 通过 auxiliary.profile_describer 为配置文件生成描述。以 description_auto: true 持久化,以便仪表盘可以显示"审查"徽章。 |
GET | /orchestration | 读取 kanban 编排设置(orchestrator_profile、default_assignee、auto_decompose)以及回退后的解析有效值。 |
PUT | /orchestration | 在 config.yaml 中更新三个编排键中的一个或多个。验证非空配置文件名实际存在。 |
POST | /links | 添加依赖关系(parent_id → child_id) |
DELETE | /links?parent_id=…&child_id=… | 删除依赖关系 |
POST | /dispatch?max=…&dry_run=… | 推动调度器 —— 跳过 60 秒等待 |
GET | /config | 从 config.yaml 读取 dashboard.kanban 偏好设置 —— default_tenant、lane_by_profile、include_archived_by_default、render_markdown |
WS | /events?since=<event_id> | task_events 行的实时流 |
_conn() 辅助函数在每次读写时自动初始化 kanban.db,因此无论用户是先打开仪表盘、直接访问 REST API,还是运行 hermes kanban init,全新安装都能正常工作。~/.hermes/config.yaml 中 dashboard.kanban 下的任何这些键都会更 改标签页的默认值 —— 插件在加载时通过 GET /config 读取它们:/api/plugins/ —— 插件路由在设计上是未认证的,因为仪表盘默认绑定到 localhost。这意味着 kanban REST 接口可以从主机上的任何进程访问。?token=… 查询参数(浏览器无法在升级请求上设置 Authorization),与浏览器内 PTY 桥使用的模式一致。hermes dashboard --host 0.0.0.0,每个插件路由 —— 包括 kanban —— 都可以从网络访问。不要在共享主机上这样做。 看板包含任务正文、评论和工作区路径;攻击者访问这些路由可以读取你整个协作界面,还可以创建 / 重新分配 / 归档任务。~/.hermes/kanban.db 中的任务是有意与配置文件无关的(这是协调原语)。如果你用 hermes -p <profile> dashboard 打开仪表盘,看板仍然显示主机上任何其他配置文件创建的任务。同一用户拥有所有配置文件,但如果多个角色共存,这一点值得了解。task_events 是一个带有单调递增 id 的仅追加 SQLite 表。WebSocket 端点保存每个客户端最后看到的事件 id,并在新行到达时推送。当一批事件到达时,前端重新加载(非常廉价的)看板端点 —— 比尝试从每种事件类型修补本地状态更简单、更正确。WAL 模式意味着读取循环永远不会阻塞调度器的 BEGIN IMMEDIATE 认领事务。tab.override 替换都可以表达,无需 fork 此插件。config.yaml 中添加 dashboard.plugins.kanban.enabled: false(或删除 plugins/kanban/dashboard/manifest.json)。tools/approval.py 的复用 —— 正如设计规范的范围外章节所列。kanban_* 工具界面进行相同的操作 —— 这里的 CLI 和那里的工具都通过 kanban_db 路由,因此两个界面在构造上是一致的。hermes kanban init # 创建 kanban.db + 打印守护进程提示
hermes kanban create "<title>" [--body ...] [--assignee <profile>]
[--parent <id>]... [--tenant <name>]
[--workspace scratch|worktree|worktree:<path>|dir:<path>]
[--branch <name>]
[--priority N] [--triage] [--idempotency-key KEY]
[--max-runtime 30m|2h|1d|<seconds>]
[--max-retries N]
[--skill <name>]...
[--json]
hermes kanban list [--mine] [--assignee P] [--status S] [--tenant T] [--archived] [--json]
hermes kanban show <id> [--json]
hermes kanban assign <id> <profile> # 或 'none' 取消分配
hermes kanban link <parent_id> <child_id>
hermes kanban unlink <parent_id> <child_id>
hermes kanban claim <id> [--ttl SECONDS]
hermes kanban comment <id> "<text>" [--author NAME]
# 批量动词 —— 接受多个 id:
hermes kanban complete <id>... [--result "..."]
hermes kanban block <id> "<reason>" [--ids <id>...]
hermes kanban unblock <id>...
hermes kanban archive <id>...
hermes kanban tail <id> # 跟踪单个任务的事件流
hermes kanban watch [--assignee P] [--tenant T] # 将所有事件实时流式传输到终端
[--kinds completed,blocked,…] [--interval SECS]
hermes kanban heartbeat <id> [--note "..."] # 长时间操作的 worker 存活信号
hermes kanban runs <id> [--json] # 尝试历史(每次运行一行)
hermes kanban assignees [--json] # 磁盘上的配置文件 + 每受让人任务计数
hermes kanban dispatch [--dry-run] [--max N] # 单次扫描
[--failure-limit N] [--json]
hermes kanban daemon --force # 已弃用 —— 独立调度器(改用 `hermes gateway start`)
[--failure-limit N] [--pidfile PATH] [-v]
hermes kanban stats [--json] # 每状态 + 每受让人计数
hermes kanban log <id> [--tail BYTES] # 来自 ~/.hermes/kanban/logs/ 的 worker 日志
hermes kanban notify-subscribe <id> # gateway 桥接钩子(由 gateway 中的 /kanban 使用)
--platform <name> --chat-id <id> [--thread-id <id>] [--user-id <id>]
hermes kanban notify-list [<id>] [--json]
hermes kanban notify-unsubscribe <id>
--platform <name> --chat-id <id> [--thread-id <id>]
hermes kanban context <id> # worker 看到的内容
hermes kanban specify [<id> | --all] [--tenant T] # 将分诊列的想法充实
[--author NAME] [--json] # 为完整规格并推进到 todo
hermes kanban gc [--event-retention-days N] # 工作区 + 旧事件 + 旧日志
[--log-retention-days N]/kanban 斜杠命令)。--max-retries 是调度器的每任务熔断器覆盖。--max-retries 1 在第一次不成功的尝试后阻塞任务,而 --max-retries 3 允许两次重试并在第三次失败时阻塞。省略它则使用 config.yaml 中的 kanban.failure_limit,然后是内置默认值。/kanban 斜杠命令 {#kanban-slash-command}hermes kanban <action> 动词也可以作为 /kanban <action> 访问 —— 从交互式 hermes chat 会话内部以及从任何 gateway 平台(Telegram、Discord、Slack、WhatsApp、Signal、Matrix、Mattermost、电子邮件、SMS)。两个界面都调用完全相同的 hermes_cli.kanban.run_slash() 入口点,该入口点复用 hermes kanban argparse 树,因此参数界面、标志和输出格式在 CLI、/kanban 和 hermes kanban 之间完全相同。你不必离开聊天来驱动看板。/kanban list
/kanban show t_abcd
/kanban create "write launch post" --assignee writer --parent t_research
/kanban comment t_abcd "looks good, ship it"
/kanban unblock t_abcd
/kanban dispatch --max 3
/kanban specify t_abcd # 将分诊一行描述充实为真正的规格
/kanban specify --all --tenant engineering # 一次性扫描某个租户中的所有分诊任务run_slash 用 shlex.split 解析行的其余部分,因此 "..." 和 '...' 都有效。/kanban 绕过运行中 agent 保护/kanban 被明确豁免于此保护。 看板存在于 ~/.hermes/kanban.db 中,而不是运行中 agent 的状态中,因此读取(list、show、context、tail、watch、stats、runs)和写入(comment、unblock、block、assign、archive、create、link 等)都会立即执行,即使在轮次进行中。/kanban unblock t_abcd,调度器在下一个 tick 接管对等方。被阻塞的 worker 不会被中断 —— 它只是不再被阻塞。/kanban comment t_xyz "use the 2026 schema, not 2025" 落在任务线程上,该任务的下一次运行将在 kanban_show() 中读取它。/kanban list --mine 或 /kanban stats 在不触及主对话的情况下检查看板。/kanban create 时自动订阅(仅限 gateway)/kanban create "…" 创建任务时,发起聊天(平台 + 聊天 id + 线程 id)会自动订阅该任务的终端事件(completed、blocked、gave_up、crashed、timed_out)。每个终端事件你会收到一条消息回复 —— 包括 completed 时 worker 结果摘要的第一行 —— 无需轮询或记住任务 id。you> /kanban create "transcribe today's podcast" --assignee transcriber
bot> Created t_9fc1a3 (ready, assignee=transcriber)
(subscribed — you'll be notified when t_9fc1a3 completes or blocks)
… ~8 minutes later …
bot> ✓ t_9fc1a3 completed by transcriber
transcribed 42 minutes, saved to podcast/2026-05-04.mddone 或 archived 后自动移除。如果你用 --json(机器输出)脚本化创建,则跳过自动订阅 — — 假设脚本化调用者希望通过 /kanban notify-subscribe 显式管理订阅。/kanban list、/kanban show 或 /kanban tail 产生超过约 3800 个字符的输出,响应会被截断,并附上 … (truncated; use \hermes kanban …` in your terminal for full output)` 页脚。CLI 界面没有此限制。/kanban 并按 Tab 会循环显示内置子命令列表(list、ls、show、create、assign、link、unlink、claim、comment、complete、block、unblock、archive、tail、dispatch、context、init、gc)。上方 CLI 参考中列出的其余动词(watch、stats、runs、log、assignees、heartbeat、notify-subscribe、notify-list、notify-unsubscribe、daemon)也有效 —— 它们只是尚未出现在自动补全提示列表中。| 模式 | 形态 | 示例 |
|---|---|---|
| P1 扇出 | N 个同级,相同角色 | "并行研究 5 个角度" |
| P2 流水线 | 角色链:侦察 → 编辑 → 写作 | 每日简报组装 |
| P3 投票 / 法定人数 | N 个同级 + 1 个聚合器 | 3 个研究员 → 1 个审查者选择 |
| P4 长期运行日志 | 相同配置文件 + 共享目录 + cron | Obsidian vault |
| P5 人工介入 | worker 阻塞 → 用户评论 → 解除阻塞 | 模糊决策 |
P6 @mention | 从文本内联路由 | @reviewer look at this |
| P7 线程范围工作区 | 线程中的 /kanban here | 每项目 gateway 线程 |
| P8 批量任务 | 一个配置文件,N 个对象 | 50 个社交账号 |
| P9 分诊规格器 | 粗略想法 → triage → hermes kanban specify 扩展正文 → todo | "将这个一行描述变成规格化任务" |
docs/hermes-kanban-v1-spec.pdf。$HERMES_TENANT 并按前缀命名空间化其内存写入。看板、调度器和配置文件定义都是共享的;只有数据是有范围的。/kanban create … 时,发起聊天会自动订阅新任务。Gateway 的后台通知器每隔几秒轮询 task_events,并为每个终端事件(completed、blocked、gave_up、crashed、timed_out)向该聊天发送一条消息。已完成的任务还会发送 worker --result 的第一行,这样你无需 /kanban show 就能看到结果。done 或 archived 后自动移除;无需清理。task_runs 中创建一行,并将 tasks.current_run_id 指向它。当该尝试结束时 —— 完成、阻塞、崩溃、超时、启动失败、回收 —— 运行行以 outcome 关闭,任务的指针清除。被尝试三次的任务有三行 task_runs。kanban_complete(...))时,它可以传递:summary(工具参数)/ --summary(CLI)—— 人类交接;放在运行上;下游子任务在其 build_worker_context 中看到它。metadata(工具参数)/ --metadata(CLI)—— 运行上的自由格式 JSON 字典;子任务看到它与摘要一起序列化。result(工具参数)/ --result(CLI)—— 放在任务行上的简短日志行(遗留字段,保留向后兼容)。# worker 实际做的事 —— agent 循环内的工具调用:
kanban_complete(
summary="implemented token bucket, keys on user_id with IP fallback, all tests pass",
metadata={"changed_files": ["limiter.py", "tests/test_limiter.py"], "tests_run": 14},
result="rate limiter shipped",
)GET /api/plugins/kanban/tasks/:id 返回 runs[] 数组)。带有 {status: "done", summary, metadata} 的 PATCH /api/plugins/kanban/tasks/:id 将两者都转发到内核,因此仪表盘的"标记完成"按钮等同于 CLI。task_events 行携带它们所属的 run_id,以便 UI 可以按尝试分组,completed 事件在其有效载荷中嵌入第一行摘要(上限 400 个字符),这样 gateway 通知器无需第二次 SQL 往返即可渲染结构化交接。hermes kanban complete a b c --summary X 被拒绝 —— 结构化交接是每次运行的,因此将相同的摘要复制粘贴到 N 个任务几乎总是错误的。不带 --summary / --metadata 的批量关闭仍然适用于常见的"我完成了一堆管理任务"情况。running 拖走(回到 ready,或直接到 todo),或归档仍在运行的任务,进行中的运行以 outcome='reclaimed' 关闭,而不是被孤立。当 tasks.current_run_id 为 NULL 时,task_runs 行始终处于终端状态,反之亦然 —— 该不变量在 CLI、仪表盘、调度器和通知器之间保持。ready 任务,或 CLI 用户运行 hermes kanban complete <ready-task> --summary X)否则会丢失交接。相反,内核插入一个零持续时间运行行(started_at == ended_at),携带摘要 / 元数据 / 原因,以保持尝试历史完整。completed / blocked 事件的 run_id 指向该行。useEffect 依赖列表中的每任务事件计数器)。不再需要关闭并重新打开才能看到运行的新行或更新的结果。tasks 上的两个可空列为 v2 工作流路由保留:workflow_template_id(此任务属于哪个模板)和 current_step_key(该模板中哪个步骤处于活动状态)。v1 内核忽略它们用于路由,但允许客户端写入它们,因此 v2 版本可以添加路由机制而无需另一次 schema 迁移。task_events 追加一行。每行携带一个可选的 run_id,以便 UI 可以按尝试分组事件。类型分为三个集群,便于过滤(hermes kanban watch --kinds completed,gave_up,timed_out):| 类型 | 有效载荷 | 时机 |
|---|---|---|
created | {assignee, status, parents, tenant} | 任务插入。run_id 为 NULL。 |
promoted | — | 因所有父任务达到 done 而 todo → ready。run_id 为 NULL。 |
claimed | {lock, expires, run_id} | 调度器原子性认领 ready 任务以启动。 |
completed | {result_len, summary?} | Worker 写入 --result / --summary 且任务达到 done。summary 是第一行交接(400 字符上限);完整版本存在于运行行上。如果在从未认领的任务上调用 complete_task 并带有交接字段,则合成零持续时间运行,以便 run_id 仍然指向某处。 |
blocked | {reason} | Worker 或人类将任务翻转为 blocked。在带有 --reason 的从未认领任务上调用时合成零持续时间运行。 |
unblocked | — | blocked → ready,手动或通过 /unblock。run_id 为 NULL。 |
archived | — | 从默认看板中隐藏。如果任务仍在运行,携带作为副作用被回收的运行的 run_id。 |
| 类型 | 有效载荷 | 时机 |
|---|---|---|
assigned | {assignee} | 受让人更改(包括取消分配)。 |
edited | {fields} | 标题或正文更新。 |
reprioritized | {priority} | 优先级更改。 |
status | {status} | 仪表盘拖放直接写入状态(例如 todo → ready)。从 running 拖走时携带被回收运行的 run_id;否则 run_id 为 NULL。 |
| 类型 | 有效载荷 | 时机 |
|---|---|---|
spawned | {pid} | 调度器成功启动 worker 进程。 |
heartbeat | {note?} | Worker 在长时间操作期间调用 hermes kanban heartbeat $TASK 发出存活信号。 |
reclaimed | {stale_lock} | 认领 TTL 在完成前过期;任务返回 ready。 |
crashed | {pid, claimer} | Worker PID 不再存活但 TTL 尚未过期。 |
timed_out | {pid, elapsed_seconds, limit_seconds, sigkill} | 超过 max_runtime_seconds;调度器发送 SIGTERM(5 秒宽限后发送 SIGKILL)并重新排队。 |
stale | {elapsed_seconds, last_heartbeat_at, heartbeat_age_seconds, timeout_seconds, pid, terminated} | 任务运行时间超过 kanban.dispatch_stale_timeout_seconds(默认 4 小时)且最近一小时内没有 kanban_heartbeat。调度器向本地 worker(如有)发送 SIGTERM,将任务重置为 ready 重新调度。不增加失败计数器(stale 是调度器端的缺席检测,不是 worker 故障)。运行长时间操作的 Worker 应至少每小时调用一次 kanban_heartbeat 以避免此情况。 |
respawn_guarded | {reason} | 调度器拒绝在本 tick 重新启动此就绪任务。原因:blocker_auth(上次失败是配额/认证/429 错误 —— 等待速率窗口重置)、recent_success(最近一小时内有完成的运行 —— 在重新运行前等待审查)、active_pr(最近的评论中出现 GitHub PR URL —— 先前的 worker 已经打开了 PR)。任务保持在 ready;下一个 tick 有另一次启动机会。如果底层条件持续存在,正常的 consecutive_failures 熔断器将在 failure_limit 次失败后通过 gave_up 自动阻塞。 |
spawn_failed | {error, failures} | 一次启动尝试失败(PATH 缺失、工作区无法挂载等)。计数器递增;任务返回 ready 重试。 |
protocol_violation | {pid, claimer, exit_code} | Worker 在任务仍处于 running 状态时成功退出,通常是因为它回答了问题而没有调用 kanban_complete 或 kanban_block。调度器还会立即发出 gave_up 并自动阻塞,而不是重试。 |
gave_up | {failures, effective_limit, limit_source, error} | N 次连续不成功尝试后熔断器触发。任务以最后一个错误自动阻塞。有效限制解析为任务 max_retries,然后是调度器 failure_limit / kanban.failure_limit,然后是内置默认值。 |
hermes kanban tail <id> 显示单个任务的这些事件。hermes kanban watch 在整个看板范围内流式传输它们。~/.hermes/kanban.db 是本地 SQLite 文件,调度器在同一台机器上启动 worker。不支持跨两台主机运行共享看板 —— 没有"主机 A 上的 worker X,主机 B 上的 worker Y"的协调原语,崩溃检测路径假设 PID 是主机本地的。如果你需要多主机,每台主机运行独立的看板,并使用 delegate_task / 消息队列来桥接它们。docs/hermes-kanban-v1-spec.pdf 中。在提交任何行为变更 PR 之前请先阅读它。