关于 sea-lite
sea-lite 是供并行运行编码 Agent 的团队使用的 MCP 工作队列。它通过带租约的路径范围认领、依赖感知的工单和哈希链事件历史,帮助避免编辑冲突并保留可审查记录。
主要功能
- 01原子选择工单并租约认领
- 02路径范围互斥
- 03带类型的依赖链接与环路检查
- 04批量创建工单和链接
- 05哈希链事件历史
适用场景
- 01协调多个 Agent 处理共享待办
- 02防止 Agent 编辑重叠路径
- 03仅在依赖项关闭后分派工作
- 04审查工单变更并验证历史
MCP 相关
向 MCP 客户端提供队列、认领、范围、工单链接和历史记录等工具。支持 stdio 与 Streamable HTTP,也兼容通过 initialize 建立会话的旧版客户端。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
npx -y sea-liteKRONICLE_LITE_DATA_DIRDirectory of the shared SQLite store (default ~/.kronicle-lite)SEA_LITE_ACTORActor label used on claims and writes (default local-agent)SEA_LITE_ROLEagent (default) or operator; operator unlocks force releaseSEA_LITE_TRACKSEnabled lifecycle tracks, e.g. work,design (default work)本地服务需要在你的电脑上安装运行环境(如 Node.js 18+)。各客户端的配置文件位置不同,请参考客户端文档。
工具
本地服务需要在你的电脑上运行后才能读取工具,维护者暂未提供工具列表。
详细介绍
sea-lite
一个让并行编码 Agent 不会互相冲突的工作队列。
让三个 Agent 同时处理一个待办列表,问题不在于它们速度太慢,而在于两个 Agent 领到了同一个工单,都“完成”了它,后完成的那个覆盖了先完成的结果。 或者其中一个 Agent 死掉时仍占着工单,其他人就再也无法处理它。
sea-lite 是一个小型 MCP 服务,工单采用围栏机制:
sea_next会原子地选出最佳的符合条件的工单,并认领它。调用方会获得一个一次性认领令牌;每次写入(sea_update、sea_transition、sea_link)都必须出示该令牌。其他人的写入会被拒绝,并告知谁持有工单以及持有期限。- 认领采用租约(默认 15 min,最长 1 h),并会在持有者活跃时续期:持有者每次调用 sea-lite 工具,以及钩子运行的
sea-lite heartbeat,都会再次延长到期时间。Agent 死掉后就会停止续期;下一位读取者会发现租约已过期,将工单视为可领取,并记录这一情况。无需清理器,也无需 cron。 - 认领可以携带范围:持有者将要编辑的路径。两个持有者的有效范围不得重叠(
scope_conflict会列出持有者、工单、路径和到期时间),而sea_scope_check可让编辑钩子拒绝在他人范围内写入。持有者由参与者标签和会话组成,因此两个都使用--actor claude的 Claude Code 终端仍是两个不同的持有者(见身份)。促成这一设计的数据是:来自不同 Agent 的 PR 配对有 41.7% 的冲突率,同一 Agent 的配对则为 19.8% (arXiv 2607.04697)——Agent 可以锁定工单,却无法锁定彼此的文件。 - 在模型之外执行约束:适用于 Claude Code 的编辑钩子,以及git pre-commit 防护(
sea-lite precommit --install),会拒绝提交暂存了他人有效认领范围内文件的提交,对 Cursor、Codex、Copilot 和人类同样适用。 - 工单归属于创建它们的项目(git 仓库),因此一个共享存储可服务多个仓库,而不会让某个仓库的队列或锁泄漏到另一个仓库。
- 工单带有类型化链接(
depends_on、blocks、parent_of等)。sea_next不会发放前置工单仍未关闭的工单,并且每次写入都会确保优先级图保持无环。 sea_apply可通过一次全有或全无的写入创建多达 50 个工单和 100 个链接,因此一个包含子项和依赖边的史诗要么整体写入,要么完全不写入,而且其中的链接可以指向已存在的工单。 模板让模型只需编写一次 20 个工单的链式列表(for_each+{i}/{value}/{ref}/{prev});服务端会以确定性的方式展开。结果是 ref → id 映射,而不是原样回显。- 精简上下文:
serve --core仅发布日常使用的九个工具,tools/list约为 12 KB(完整的十五个工具约为 32 KB;0.4.0 则为 59 KB),结果采用紧凑 JSON 格式。sea_finish可通过一次调用结束一个或多个工单的工作:提供结果(done/blocked/abandoned)、备注和可选的 commit sha,并释放认领。 - 认领可以绑定到 git 工作树;移除工作树(
sea-lite release-worktree、WorktreeRemove钩子)时会释放认领。 - 每次更改都是哈希链上的一个事件——该哈希链由
kronicle-lite 保留。两者共用同一个 SQLite 文件。
sea_history会显示工单记录以及哈希链是否仍可验证;sea-lite rebuild会根据哈希链重放并重建数据表。 一个进程,一个文件,无需账户。支持 MCP 2026-07-28(每个请求的元数据、server/discover、可缓存的tools/list、每个工具都提供outputSchema+structuredContent、多轮确认),同时也兼容使用initialize建立连接的 2025 年客户端;支持 stdio 和 HTTP,同一进程两种方式都能响应。可通过 stdio 与 Claude Code、Cursor、Codex、Copilot、Claude Desktop 或任何 MCP 客户端一起运行,也可通过 Streamable HTTP 运行(serve --http、loopback、bearer token;如需让声明可追溯,可为每个参与者分配一个 token)。sea_stats/sea-lite stats会记录谁持有了什么、持有了多久;sea-lite doctor会检查安装情况。多个服务进程(每个 Agent 一个,或共用一个)可以指向同一个文件;SQLite 的写锁和BEGIN IMMEDIATE可确保跨进程的声明互斥。
安装
需要 Node ≥ 20。数据存储在 ~/.kronicle-lite/kronicle-lite.db(或 --data-dir,或 $KRONICLE_LITE_DATA_DIR),如果同时运行 kronicle-lite,则与其共享。一个存储服务于所有仓库:工单按项目分类。--actor 标签是人们在看板上看到的名称;你无需为每个终端设置不同的标签——每个服务进程都是独立会话(参见身份:谁持有声明)。
Claude Code
claude mcp add sea-lite -- npx -y sea-lite serve --core --actor claude
--core 会提供 Agent 日常使用的工具(参见配置方案:core 和 full);移除该选项即可启用全部十五个工具。
每个 Claude Code 会话的服务都会读取 CLAUDE_CODE_SESSION_ID 和 CLAUDE_PROJECT_DIR,因此,同一仓库中的两个会话就是两个持有者,其队列也属于该仓库。将 skills/work-queue/ 复制到 .claude/skills/work-queue/,让 Agent 了解声明 → 界定范围 → 工作 → 释放的流程;根据 hooks/README.md 配置编辑钩子(或安装自带该钩子的 cc-anchor 插件),这样在他人的范围内进行 Edit/Write 操作时,就会在操作发生前被阻止;然后添加 git 防护:
npx sea-lite precommit --install # .git/hooks/pre-commit
心跳。 sea-lite 会在调用每个 sea-lite 工具时续租持有者的租约,因此只要 Agent 持续使用队列,租约就能续期。长时间只进行普通编辑和测试运行不会调用 sea-lite,因此还应配置 PostToolUse 心跳:推荐使用 cc-anchor 的心跳钩子(它会在后台限流续租你的声明,让你可以继续工作),也可以使用 hooks/README.md 中的 sea-lite heartbeat --hook。测量结果(agent-memory-bench S13,租约 5 s,任务 12 s):不使用钩子时,租约会在任务进行中失效,持有者只能等到下次尝试受保护的写入时才发现;使用 cc-anchor 的心跳时,声明 3/3 次均保持有效。
Cursor
在项目中的 .cursor/mcp.json(或全局配置的 ~/.cursor/mcp.json)中添加:
{
"mcpServers": {
"sea-lite": {
"type": "stdio",
"command": "npx",
"args": ["-y", "sea-lite", "serve", "--actor", "cursor"],
"env": { "KRONICLE_LITE_DATA_DIR": "${userHome}/.kronicle-lite" }
}
}
}
Cursor 在运行 MCP 工具前会征求许可,除非你将其加入允许列表;只读工具(sea_get、sea_list、sea_history、sea_scope_check、sea_stats)可以安全地加入允许列表。将 skills/work-queue/SKILL.md 添加到项目规则中,让 Agent 在编辑前先声明其路径。
Codex(CLI、IDE 扩展、应用)
在 ~/.codex/config.toml(或受信任项目中的 .codex/config.toml)中添加:
[mcp_servers.sea_lite]
command = "npx"
args = ["-y", "sea-lite", "serve", "--actor", "codex"]
env_vars = ["KRONICLE_LITE_DATA_DIR"]
startup_timeout_sec = 20
default_tools_approval_mode = "writes" # prompt only on write tools
[mcp_servers.sea_lite.tools.sea_transition]
approval_mode = "prompt" # terminal states are irreversible
[mcp_servers.sea_lite.tools.sea_finish]
approval_mode = "prompt" # closes tickets (terminal, no exit)
Codex 会读取服务的 instructions;其中包含工作流程。
GitHub Copilot 编码 Agent
在仓库的Settings → Copilot → MCP servers 中选择“MCP configuration”。编码 Agent 会在全新的 Actions 沙盒中运行,因此请将其指向该运行可访问的存储,并明确将工具加入允许列表:GitHub 建议使用只读工具,且 Copilot 代码审查只会加载 annotations.readOnlyHint 为 true 的工具。符合条件的工具包括:sea_get、sea_list、sea_history、sea_scope_check、sea_stats。
{
"mcpServers": {
"sea-lite": {
"type": "local",
"command": "npx",
"args": ["-y", "sea-lite", "serve", "--actor", "copilot", "--data-dir", "/tmp/sea-lite"],
"env": { "SEA_LITE_ROLE": "agent" },
"tools": ["sea_get", "sea_list", "sea_history", "sea_scope_check", "sea_stats"]
}
}
}
配置好服务后,Copilot 调用其工具时不会征求许可,因此只有在你希望 Agent 自行领取和关闭工单时,才添加写入工具(sea_create、sea_apply、sea_claim、sea_next、sea_update、sea_transition、sea_finish、sea_link、sea_refresh、sea_release)。env 块中的机密和变量必须命名为 COPILOT_MCP_*。
其他客户端
{ "mcpServers": { "sea-lite": { "command": "npx", "args": ["-y", "sea-lite", "serve", "--actor", "me"] } } }
2026-07-28 客户端通过 server/discover 探测;2025 客户端发送
initialize;两者都可使用。
通过 HTTP(serve --http)
对于无法启动进程的客户端,或多个 Agent 共享一个 队列服务:
npx sea-lite serve --http --actor owner # http://127.0.0.1:7418/mcp
npx sea-lite add-token --actor agent-a # prints a token once; stores its sha256
npx sea-lite add-token --actor agent-b
{ "mcpServers": { "sea-lite": { "type": "http", "url": "http://127.0.0.1:7418/mcp",
"headers": { "Authorization": "Bearer <agent-a's token>" } } } }
| 标志 | 默认值 | |
|---|---|---|
--port N / --host H | 7418 / 127.0.0.1 | kronicle-lite 的 serve --http 使用 7417 |
--bearer T / --bearer-file F | <data dir>/http-bearer.token(以 0600 创建) | 共享所有者令牌(与 kronicle-lite 使用同一文件);除非指定 --allow-insecure-token-file,否则拒绝组用户或其他用户可读取的令牌文件 |
--actors-file F | 如果存在,则为 <data dir>/http-actors.json | 来自 add-token 的各 Agent 令牌(仅存储 sha256,以 0600 保存);文件变更时重新读取,因此 add-token 无需重启即可生效 |
--allow-origin H,.. / --allow-host H,.. | 仅限回环地址 | 防御 DNS 重绑定;添加客户端使用的名称 |
--legacy stateless|reject | stateless | 为 2025 年前后的 initialize 客户端提供服务,或使用 -32022 拒绝它们 |
--project KEY|* | *(每个项目) | 此服务队列所属的项目 |
- 每个请求的身份标识。 共享 bearer 作为
--actor(角色为--role),而单次调用的actor_id是自行声明的标签,与 stdio 相同。Agent 令牌代表其自身 Agent(以及其条目的role);若actor_id指定了其他人,则会以forbidden拒绝。因此,声明、 备注、链事件和sea_stats都可按令牌归属。 - 支持两个协议时代,无状态。 POST 到同一个端点;没有会话 ID;
GET/DELETE→ 405。2026-07-28 客户端会发送每个请求的_meta; 2025 年前后的客户端(目前的 Cursor)通过initialize建立连接,并由 SDK 的无状态旧版回退机制提供服务,使用相同的工具和相同的 每请求身份标识。--legacy reject会让端点仅支持现代协议 (initialize→ 400 /-32022)。缺少 bearer 或 bearer 错误 → 401;外部Origin或Host→ 403。 - 项目。 团队服务可查看每个项目,除非使用
--project KEY启动(作用域相对于仓库,因此每个仓库运行一个服务,或为其命名)。 - 单一信任域。 每个令牌都能访问整个存储;没有按工单设置的权限,也没有读取隔离机制,且不提供 TLS。在外部机器上使用时,请在前面配置 TLS。暴露服务前,请阅读
docs/THREAT-MODEL.md→ “HTTP transport”。
六十秒导览
无需 MCP 客户端或大模型:CLI 驱动的队列与 Agent 使用的相同。以下每条命令都会由测试套件针对全新存储运行。
npm i -g sea-lite # or: alias sea-lite='npx -y sea-lite'
export KRONICLE_LITE_DATA_DIR=$(mktemp -d) # a scratch store
cd "$(mktemp -d)" && git init -q # any repository: tickets belong to it
sea-lite create "Add rate limiting to login" --triaged
sea-lite create "Fix session expiry" --triaged
sea-lite next --paths src/auth --actor alice # alice takes the first ticket and locks src/auth
sea-lite next --paths src/auth/session.ts --actor bob # exit 1: refused, alice holds src/auth
sea-lite next --paths src/billing --actor bob # bob takes the other ticket instead
sea-lite board # who holds what, which paths, time left
sea-lite scope-check src/auth/login.ts --actor bob # exit 1: what an edit hook or pre-commit asks
sea-lite release --all --actor alice # alice is done; src/auth is free again
sea-lite scope-check src/auth/login.ts --actor bob # exit 0
sea-lite verify # every chain hash recomputed
board 会输出:
sea-lite board — project /tmp/tmp.BUtjgMu1sO; 2 open ticket(s); 02:45:16Z
ID STATUS PRI TITLE HOLDER LOCKS LEASE
cecadce3 triaged medium Fix session expiry bob src/billing 15m00s left
a57e63b8 triaged medium Add rate limiting to login alice src/auth 14m59s left
Agent 会显示为 claude#9b1d0c22(actor#session);在空闲终端窗格中运行 watch -n1 sea-lite board,即可实时查看。
Agent 的循环流程(通过工具调用):
sea_next scope.paths=[src/handlers] → { ticket, claim_token, expires_at } (paths locked too)
sea_scope_check paths=[...] before touching a path you did not claim
sea_update (notes, tags) fenced by claim_token; any call renews your leases
sea_finish outcome=done note="…" resolved + claim and scope released, one call
— or —
sea_release note="where I stopped"
此外还有:sea-lite list、show ID、stats --since 7d、rebuild、
doctor、whoami、heartbeat。sea-lite help 会列出所有命令。
工具
| 工具 | 功能 |
|---|---|
sea_create | 创建一张工单(idea,或 triaged 以使其可供调度)。返回 possible_duplicates(精确匹配 + 全文标题匹配),但绝不会阻止创建。claim_seconds 会在同一次调用中认领工单。 |
sea_apply | 一次性处理 1–50 张工单和 ≤ 100 条链接,要么全部成功,要么全部失败。链接端点可以是批次 ref,也可以是已有工单(from_id 由 claims 中的令牌进行保护;to_id 不受保护)。templates 会在服务端展开为工单和链接(参见模板化批次);数量上限按展开后的批次计算。若存在重复的 ref、未知的 ref/id、自环、重复链接或环路(包括现有链接),则会拒绝请求并列出所有问题。返回 {created: {ref: id}, links: n, idempotent_replay};verbose: true 会返回每张工单和每条链接的 id(即 0.4.0 的结果)。dry_run 会返回展开后的计划。 |
sea_get | 按 id 或 ≥ 8 个字符的前缀获取工单:字段、row_version、认领者/到期时间(绝不返回令牌)、带对端标题/状态的活动链接、备注、legal_next。 |
sea_list | 支持筛选(状态、类型、优先级、受理人 me/none、标签、认领状态 any/yes/no/mine)、FTS5 query、游标分页、compact/full。默认列出此项目的工单(以及没有项目的旧工单),除非 project 指定其他项目或 *。 |
sea_claim | 认领一张或一组工单(要么全部成功,要么全部失败;按 id 顺序锁定)。每张工单都会返回一个令牌。重新认领自己的工单会轮换令牌。scope.paths 会在租约期间锁定你将编辑的路径;若与他人仍有效的范围重叠,则返回 scope_conflict。scope.worktree 会将认领绑定到一个 git worktree。wait_seconds 会等待已被认领的工单或范围,而不是直接失败(参见等待认领)。 |
sea_refresh | 将租约延长至当前时间 + N(默认值:该认领自身的租期,≤ 3600);绝不会缩短;仅限持有者调用。不会链式延长。通常不需要:持有者的任何 sea-lite 调用都会续期其认领。 |
sea_release | 持有者将工单退回(附带交接 note)。操作员可使用 force: true(--role operator),并会记录为强制释放。 |
sea_update | 更新标题/正文/类型/优先级/受理人,添加/移除标签,追加备注。受保护;支持 expected_version(OCC)、idempotency_key、dry_run。 |
sea_transition | 更改状态。受保护。拒绝操作时会附带 legal_next。duplicate 需要 duplicate_of,并会记录一条 duplicates 链接。终止状态会释放认领(及范围),并要求确认:设置 confirm: true,或回答 input_required 提示。note 会在同一次写入中追加备注。批量操作:使用 tickets: [{ticket_id, claim_token}, …] 替代 ticket_id + claim_token,最多可更改 50 张工单,每张工单都由自己的令牌进行保护;要么全部成功,要么全部失败(batch_invalid 会列出每张失败的工单);整组只需确认一次。 |
sea_finish | 一次调用即可结束你的工作。必填 outcome:done → resolved,blocked → blocked(非终止状态;可恢复;已阻塞的工单仍保持阻塞),abandoned → dropped。必填 note(已交付的内容或原因;≤ 1000 个字符),可选 commit(sha,7–64 位十六进制字符)。无论哪种情况,都会释放认领和范围。备注会以 done @ 3f2a9c1: … 的形式存储在工单上;事件链会在状态事件中记录 outcome / commit,并在备注事件中记录 finish: {outcome, commit, from, to},因此 sea_history 会显示原因。tickets: [...] 可一次结束多张工单,每张工单都由自己的令牌进行保护;要么全部成功,要么全部失败。调用此工具即表示确认(不会显示提示)。 |
sea_link | 添加/移除有类型的链接,from 端受保护。parent_of / depends_on / blocks 会确保现有链接及新链接整体无环。移除操作会创建墓碑记录。 |
sea_next | 选择并认领优先级最高、未被认领的 triaged/in_progress 工单;该工单的受理人必须是我或未指定,且所有前置工单都必须处于终止状态。接受与 sea_claim 相同的 scope.paths。wait_seconds 会等待已占用的范围或繁忙的队列(参见等待认领)。 |
sea_history | 查看单张工单的事件,或通过 since_seq 获取所有 sea 事件的动态记录,并显示链验证状态。 |
sea_scope_check | 只读:编辑这些路径是否会与他人仍有效的范围冲突?返回 { ok, conflicts }(持有者、会话、工单标题、重叠路径、到期时间)。无需认领。 |
sea_stats | 只读,按参与者统计并从事件链推导:认领次数 / 轮换次数 / 释放次数 / 过期次数 / 强制释放次数、持有租约的秒数、状态变更次数、创建的工单数、有效认领数。since: 24h | 7d | ISO。 |
另外还有 sea://tickets/{id} 资源(与 sea_get 的结构相同)。 |
每个工具都会发布一个 outputSchema,并以 structuredContent 和紧凑文本的形式返回相同的 JSON。发布的 schema 精简易读(顶层结果字段完整列出,嵌套对象则以字段列表表示,例如 {id,kind,title,…},不含 $schema 或字符串长度等冗余信息);服务端仍会根据完整 schema 验证每个参数和每个结果。每个错误都采用 { error: <code>, message, details } 格式,错误代码集合固定(fence_invalid、fence_foreign_holder、
fence_expired、already_claimed、scope_conflict、version_conflict、
illegal_transition、cycle、batch_invalid、confirmation_required、
confirmation_declined、…),方便 Agent 根据错误代码进行分支处理。
配置:core 和 full
每个会话的上下文都需要为 tools/list 付费,因此 serve 提供两种配置:
| 配置 | 工具 | tools/list |
|---|---|---|
full(默认) | 全部十五个 | ~32 KB |
core(--core、--profile core、SEA_LITE_PROFILE=core) | sea_next、sea_claim、sea_get、sea_scope_check、sea_apply、sea_transition、sea_finish、sea_release、sea_refresh | ~12 KB |
core 还会提供更短的描述,不会列出极少需要的输入(actor_id、idempotency_key、expected_version、sea_next 的 kind / tag 筛选条件、仅限操作员使用的 force、…;但仍接受这些输入),并且发布的结果 schema 仅包含字段名和类型。两种配置下所有工具的行为完全相同。serve --http --core 同样可用。
core 不包含 sea_create、sea_update、sea_link、sea_list、
sea_history 和 sea_stats,其服务端说明也会注明这一点:要使用这些工具,请在不带 --core 的情况下运行 serve(或使用 --profile full /
SEA_LITE_PROFILE=full)。无论使用哪种配置,CLI(sea-lite list、show、
stats)都能覆盖读取操作。
模板化批次
模板会为 for_each 中的每个值重复创建一个工单及其链接(for_each 可以是字符串/数字数组,也可以是表示 1..n 的数字 n)。在模板的 ticket 和 links 中的每个字符串,以及其中的 ref 中:
| 占位符 | 替换为 |
|---|---|
{i} | 从 1 开始的项目索引 |
{value} | for_each 中的值 |
{ref} | 此项目展开后的 ref |
{prev} | 同一模板中前一项目(项目 i−1)展开后的 ref,而非批次中前一个工单的 ref;项目 1 没有前一项目,因此引用 {prev} 的链接会在项目 1 中跳过(在工单字段中则原样保留) |
花括号中的其他内容会保持原样。展开过程是确定性的(先处理显式 tickets,再按顺序处理每个模板中的项目),并在验证前由服务端执行;批次上限(50 个工单、100 个链接)、循环检查和幂等性都适用于展开后的批次。dry_run: true 会返回展开后的计划,但不会写入。
错误会准确指出失败的位置。每个 batch_invalid 问题都包含 where(tickets[2] (a)、links[4]、templates[0] item 3 (t03)、
templates[0] item 3 (t03) links[1]、claims[0]),以及结构化的 template、item(从 1 开始)、ref 和 link 索引;其 message 以相同位置开头。cycle 错误会提供由 ref(现有工单使用其 id)组成的循环路径,以及关闭该循环的链接 closed_by。只要有任何失败,就不会写入任何内容。
通过一次调用创建一个 epic 和一条包含 20 个步骤的迁移链,每个步骤都是 epic 的子项,并且依赖前一个步骤(共 21 个工单、39 个链接):
{
"tickets": [{ "ref": "epic", "title": "Retire the v1 API", "kind": "epic" }],
"templates": [{
"for_each": ["users", "orders", "invoices", "…17 more"],
"ref": "m{i}",
"ticket": { "title": "Migrate endpoint /{value} to v2", "status": "triaged" },
"links": [
{ "from_ref": "epic", "to_ref": "{ref}", "link_type": "parent_of" },
{ "from_ref": "{ref}", "to_ref": "{prev}", "link_type": "depends_on" }
]
}],
"idempotency_key": "retire-v1-plan"
}
结果:{"created":{"epic":"…","m1":"…",…,"m20":"…"},"links":39,"idempotent_replay":false}。
使用相同的 idempotency_key 重新发送此请求会重放该结果(idempotent_replay: true),而不会再创建 21 个工单。键会与其请求绑定:使用同一个键提交不同批次会返回 invalid_input("idempotency_key reused with a different request"),绝不会重放第一次请求的结果——所有带键的工具都遵循相同规则。
等待认领(wait_seconds)
sea_claim 和 sea_next 接受 wait_seconds(≤ 300;CLI 使用 --wait S)。
不会立即因 already_claimed / scope_conflict 而失败,而是等待工单或范围空闲后再认领。结果包含 waited: {seconds, attempts, blocked_by: {kind, ticket_id, title, holder, holder_label, expires_at}, freed},其中 freed 为
released、finished(持有人关闭了工单)、lease_expired 或 changed。如果始终未能空闲,调用会失败并返回 wait_timeout,指出仍在阻塞的持有人、工单和到期时间(CLI 退出码为 1)。当没有可用工单,但其他人持有可能因此空闲的工作(符合条件的工单,或某个工单的前置工单)时,sea_next
也会等待;如果队列为空,则会立即返回 nothing_eligible。无法通过等待解决的拒绝原因(未知工单、输入错误)会立即导致失败。
等待机制不会频繁访问存储:等待期间不会锁定任何内容。只有在发生变化时,等待方才会重试:其他进程完成了提交(读取一次 SQLite PRAGMA data_version 计数器并以 20 → 250 ms 的间隔轮询)、本进程完成了提交(进程内通知器会立即唤醒等待方),或阻塞租约已到期。其他进程中的释放会在约 250 ms 内被检测到;同一进程中则会立即检测到。
客户端超时。 等待属于单个 MCP 请求,因此必须在客户端的请求超时时间内完成。TypeScript SDK 的默认超时时间为 60 s;其他客户端各不相同。客户端发送 progressToken 时,sea-lite 每 5 s 会发送一次进度通知,说明正在等待谁;如果客户端会在收到进度时重置超时时间(TypeScript SDK 中的 resetTimeoutOnProgress),等待就能持续到结束。否则,请将 wait_seconds 设为低于客户端的超时时间。取消请求会结束等待(wait_cancelled)。
作用范围:锁定文件,而不只是工单
工单锁可以阻止两个 Agent 做同一项工作,但无法阻止他们因不同工单而编辑同一个文件——大多数跨 Agent 合并冲突都源于此。因此,认领可以带上持有者将要修改的路径:
sea_claim { ticket_ids: [id], scope: { paths: ["src/handlers", "docs/*.md"] } }
- 不含 glob 字符的条目是前缀:指该路径;如果它是目录,也包括其下的所有内容。
src/handlers/claims.ts表示一个文件;src/handlers表示整个子树;next.js/(末尾带斜杠)表示带点号的名称是一个目录。Git worktree 或软件包目录都是自然的作用范围。 - 含有
* ? [ ] { }的条目是 glob(picomatch):表示它匹配的路径。*只匹配一个路径段,**可以跨越多个路径段。.表示整个仓库。 - 两个作用范围重叠,是指其中一个范围的任意条目可能与另一个范围的任意条目指向同一个文件(
src↔src/**/*.ts、docs/x.md↔docs/*.md;但src↔src2不重叠,*.ts↔*.md也不重叠)。完整规则见docs/MODEL.md。 - 路径会经过符号链接解析(stdio
serve、CLI、钩子和 pre-commit 防护程序都能识别仓库):条目的最长现存前缀(glob 之前的字面部分)会替换为其 realpath,然后重新转换为相对于仓库的路径。对于hlink/claims.ts,如果hlink → src/handlers,那么对每项认领和每项检查而言,它都表示src/handlers/claims.ts,结果也会显示为这种形式。解析到仓库外部的链接会被拒绝(invalid_input,reason: "outside_repo")。serve --http服务于多台机器,因此会保留原样书写的路径。 - 如果作用范围与其他人的有效、未过期作用范围重叠——包括其他执行者,或同一执行者在另一会话中的作用范围——则会拒绝请求,并通过
scope_conflict列出所有持有者、工单、重叠路径对和到期时间。你自己的作用范围之间永不冲突。只会比较同一项目的认领。 - 作用范围随认领而生灭:释放认领、状态转为终态或租约到期都会释放作用范围。作用范围会记录在链上(
sea_ticket.claimed),因此sea-lite rebuild可以将其恢复。 sea_scope_check { paths }/sea-lite scope-check是钩子使用的快速路径:只需读取一次,不会认领。参见执行约束。- 路径相对于仓库,使用
/;也接受反斜杠并会将其规范化,因此 Windows Agent 和 POSIX Agent 会锁定相同的条目。
绑定到 worktree 的认领
在 Git worktree 中工作的 Agent 可以将认领绑定到该 worktree:
sea_next { scope: { paths: ["src/auth"], worktree: "/home/me/repo-wt-auth" } }
worktree 是绝对路径(会规范化:C:\Repo\wt\ → c:/Repo/wt),可以与 paths 一起使用,也可以单独使用(单独使用时不会锁定任何路径),并会显示为 sea_get / sea_list 中的 claim.worktree。移除 worktree 时,sea-lite release-worktree <path>——或 hooks/ 中的 WorktreeRemove 钩子(release-worktree --hook)——会释放所有绑定到该 worktree 的有效认领,无论持有者是谁;释放事件会以 via: release_worktree 记录为 claim_released。如果有效认领绑定的 worktree 已不存在,sea-lite doctor 会发出警告。
身份:谁持有声明
一项声明属于一个 参与者(一个标签:claude、alice、agent-a)
和一个会话。只有两者都匹配时,才属于同一个持有者。
| Where | Actor | Session |
|---|---|---|
sea-lite serve(stdio) | --actor、SEA_LITE_ACTOR,否则为 local-agent | --session、SEA_LITE_SESSION、CLAUDE_CODE_SESSION_ID(Claude Code 会为 stdio MCP 服务设置此变量),否则为每个进程随机生成的 inst-xxxxxxxx |
| 钩子和 CLI 命令 | --actor,否则为会话所属服务的参与者,再否则为 SEA_LITE_ACTOR / 你的操作系统用户 | 按顺序查找:会话键(--session、SEA_LITE_SESSION、CLAUDE_CODE_SESSION_ID),前提是它指向一个已注册且正在运行的服务;与其同属一个 Claude Code 进程的服务(CLAUDE_PID);否则为键本身;再否则为空 |
serve --http | 令牌的参与者(每个参与者一个令牌),或共享 bearer 的 --actor | 无:一个令牌就是一个持有者 |
库(new SeaLite()) | actor 选项 | session 选项,默认无(0.4 版之前的行为) |
每个 stdio 服务都会向 <data dir>/sessions/ 写入一条简短记录
(0600:参与者、会话、pid、祖先进程 pid、项目),并在退出时将其删除;读取时会清理已终止的记录。该注册表使 Claude Code 会话的 PreToolUse 钩子能够根据钩子输入中的 session_id,找到其自身 MCP 服务声明时使用的身份——即使在 /clear 之后,钩子的会话 ID 发生变化,但服务仍沿用启动时分配的 ID(已根据 Claude Code 环境变量参考验证:CLAUDE_CODE_SESSION_ID 会传递给钩子、Bash 和 stdio MCP 服务;CLAUDE_PID 会传递给钩子和 Bash)。仅提供会话的钩子代表该会话中的所有 Agent 发言(主 Agent 和子 Agent)。
sea-lite whoami 会显示 shell 解析出的身份;sea-lite board 会列出正在运行的服务。stdio 和 CLI 上的标签均由使用者自行声明:这能区分诚实的会话,但无法验证任何人的身份。经过身份验证的路径是通过 HTTP 使用每个参与者专属的令牌。请参阅
docs/THREAT-MODEL.md。
强制执行:什么机制能真正阻止编辑
| Layer | Stops | Misses |
|---|---|---|
sea_claim / sea_next 上的 scope_conflict | 两个持有者锁定存在重叠的路径 | 未先声明就进行的编辑 |
Claude Code PreToolUse 钩子(hooks/,或 cc-anchor 的范围保护) | 在操作发生前,阻止 Edit / Write / MultiEdit / NotebookEdit 修改他人正在生效的范围 | 通过 Bash 工具执行的所有操作(sed -i、> 重定向、格式化工具、代码生成、git checkout):钩子只能看到文件编辑工具 |
sea-lite precommit(git pre-commit) | 阻止提交包含他人正在生效声明范围内的暂存文件——适用于所有客户端(Cursor、Codex、Copilot 和人工用户),并会在提交时捕获上述 Bash 编辑 | 未提交的工作区编辑;git commit --no-verify |
使用 sea-lite precommit --install 为每个克隆安装 git 防护(如果现有 pre-commit 钩子不是它自己的,命令会拒绝覆盖;请将
sea-lite precommit 添加到你自己的钩子中)。它会像钩子一样解析提交者:通过 Bash 工具提交的 Agent 会被识别为其自身会话,因此它自己的声明不会阻止它。
记账与健康状况
sea_stats(MCP,只读)和 sea-lite stats [--since 24h|7d|ISO] [--for ACTOR] [--json] 会按角色报告:领取的认领(以及令牌轮换、范围限定的认领)、持有者释放的认领、因终止状态转换而释放的认领、过期的认领、强制释放的认领(以及作为操作员发出的强制释放)、持有租约的秒数、活动认领、状态转换和创建的工单。所有数据均源自链和实时投影,因此无需写入,并且重建后仍会得出相同结果。租约时间从每个 claimed 事件开始,直到结束该租约的事件为止(对于已过期的租约,使用记录的过期时间,其中包括未上链的续期),并限制在时间窗口内。scope_conflict 拒绝不会写入链,因此不会计入。通过带角色令牌的 serve --http 运行时,角色列与凭证关联;通过标准输入输出运行时,该列则显示各 Agent 自选的标签。
sea-lite doctor [--json] [--cwd DIR] [--actors-file F] 为只读(数据库以 readonly 模式打开;不会创建或清扫任何内容),如果任何检查失败就会以状态码 1 退出:Node ≥ 20、数据目录存在且权限为 0700、HTTP 令牌文件权限为 0600 且格式正确、数据库可以打开并通过 quick_check、sea_schema_version 与此构建版本相符、完整链验证通过且每条 sea 行的 chain_seq 都存在、认领(活动认领;超过一小时无人清扫的过期租约;位于已消失工作树上的认领),以及已提供的工具目录与此版本固定的指纹相符。
生命周期
stateDiagram-v2
[*] --> idea
idea --> triaged
triaged --> in_progress
in_progress --> resolved
state holding {
parked
blocked
needs_decision
}
idea --> holding
triaged --> holding
in_progress --> holding
holding --> idea
holding --> triaged
holding --> in_progress
state terminal {
resolved
wont_fix
duplicate
dropped
merged
}
idea --> terminal
triaged --> terminal
in_progress --> terminal
holding --> terminal
terminal --> [*]
工作轨道的状态每次向前推进一步。任何活动工单都可以进入等待状态或终止状态;等待状态只能恢复到 idea、triaged 或 in_progress;终止状态没有后续状态。可选的设计轨道(--tracks work,design)会添加 idea → in_design → ready → in_progress。
类型:feature research investigation refactor curiosity question other bug chore incident security epic spike log docs decision。优先级:urgent high medium low parked(parked 不会由 sea_next 分派)。
关联类型:parent_of relates_to inspired_by responds_to conflicts_with supersedes blocks depends_on condenses_into branches_from references duplicates。方向表示为 from <type> to。
与 kronicle-lite 组合使用
sea-lite 没有自己的链。它会打开 kronicle-lite 存储(或采用已打开的存储),并在与行变更相同的事务中,将 sea_ticket.* / sea_link.* /
sea.batch_applied 事件追加到同一条哈希链。这样你可以获得:
- 一个
get_system_state。 使用 kronicle-lite 获取上下文的 Agent,可以在增量中看到工单创建、状态变更和关联变更。常规认领变动(claimed、claim_released、claim_expired)默认汇总,不会挤占简报;强制释放则会逐项列出。 - 撤回。 被墓碑标记的关联会被 kronicle-lite 的分类器归类为安全撤回,与被墓碑标记的区块相同。
- 一次验证,一次导出。
kronicle-lite verify-chain、checkpoint和export会涵盖 sea 事件。执行kronicle-lite import后,运行sea-lite rebuild以重新投影数据表(导入操作只会重建文档)。 - 共享幂等性。
idempotency_key会以sea:为前缀写入链的唯一列;重放时会返回首次操作的结果。
你可以运行 sea-lite,而无需运行 kronicle-lite 的服务端。依赖项是该库。
审计:运行器的跟踪器(库 + CLI,无 MCP 工具)
由代码驱动的 MCP 审计(mcp-audit 运行器)。Agent 从不读取或写入这些工单,因此它们不会出现在 tools/list 中;每项结果都是 ID、版本和已更改的字段名称,而非实体。
const sea = new SeaLite({ actor: 'mcp-audit' });
sea.auditOpen({ audit_id: 'posthog-2', title: 'PostHog MCP audit 2', catalog_fingerprint: 'sha256:…' });
// One worker at a time on the sandbox. Journal every side effect.
const lease = sea.leaseAcquire({ name: 'sandbox:posthog', lease_seconds: 600 });
for (const u of lease.unresolved ?? []) { // a predecessor died mid-write
const landed = await readBack(u); // verify; never repeat blindly
sea.leaseResolve({ name: 'sandbox:posthog', token: lease.token, intent_id: u.intent_id, outcome: landed ? 'happened' : 'not_happened' });
}
const { intent_id } = sea.leaseIntent({ name: 'sandbox:posthog', token: lease.token, op: 'insight-create', idempotency_key: 'run7:L3' });
// … remote write …
sea.leaseResolve({ name: 'sandbox:posthog', token: lease.token, intent_id, outcome: 'happened', evidence_ref: 'kl:1234' });
// After refutation only: a finding, with five gate sub-tickets.
sea.auditFinding('posthog-2', { finding_id: 'L3', title: '500 still persists the insight', fields: { verdict: 'bug', severity: 'high' } });
sea.auditGate('posthog-2', { finding_id: 'L3', gate: 'repro', result: 'pass', evidence_ref: 'kl:1234' });
- 具名独占租约(
sandbox:<id>):同一时间只有一个有效持有者,持有者由操作者和 SeaLite 实例(每个实例都有一个随机 ID)共同标识,因此同一操作者启动的第二个进程(包括崩溃后重启的运行器)都会被拒绝,直到租约到期(或操作员强制释放)。每次获取都会生成新的epoch和令牌;旧令牌会失去围栏效力。任何获取操作(接管或持有者自行重新获取)以及leaseRelease({ abandon: true })(中止)都会将每个未关闭的意图转为may_have_happened;下一位持有者会在unresolved中看到这些意图,并且必须逐一核实并关闭,之后才能开启新意图。与声明一样,会防范时钟偏差。 - 类型化字段(
setFields/fields):采用封闭注册表,字段包括verdict、severity、confidence、submit_plan、pr_or_issue、submitted_date、tracker_status、catalog_fingerprint、evidence_ref(kronicle 事件 ID)、review以及跟踪器的文本列。未知名称和无效值会返回invalid_input;expect_version用于保护并发写入。身份字段、门禁结果和review由系统管理:只有审计调用才能设置这些字段。任何类似凭据的内容都不会进入仅追加链:自由文本(标题、备注、证据、租约意图的op/target)会经过脱敏处理——使用 kronicle-lite 的写入时检测器,另加不受单词边界限制的供应商密钥格式匹配(_ghp_…_、key_phx_…),并对 URL 编码或 Base64 字符串进行解码和检查;看起来像凭据的身份字符串(ID、引用、租约名称、幂等键)会被拒绝。 - 发现项和门禁:工单位于项目
audit:<id>中,因此不会出现在任何代码项目的sea_next/sea_list中。每个发现项都会获得repro、intent、docs、prior_reports、refuter门禁子工单(parent_of)。按发现项 ID 幂等地执行更新;如果没有任何变化,就不会写入任何内容。 - 人工审核队列:所有门禁都有结果(
pass | fail | n/a)后,发现项的状态为review: pending;使用sea-lite audit queue AUDIT,然后运行sea-lite audit review AUDIT FINDING approved|rejected|hold [--plan P]。当标题、发现项中除跟踪器管理字段(pr_or_issue、submitted_date、tracker_status、submit_plan、review_note)之外的字段,或门禁的结果 / 备注 / 证据发生变化时,审核决定(approved / rejected / hold)会重置为pending;门禁重置为pending后,该发现项也会从队列中移除。 - 导出 / 导入:
sea-lite audit export AUDIT [--format csv|xlsx] [--out F](模式为 0600)会按顺序写入 PostHog 跟踪器的列(门禁列包含门禁备注),然后为每个门禁写入一个<gate>_result列,接着是review、catalog_fingerprint、evidence_ref、ticket_id。CSV 单元格的第一个非空格字符若为= + - @(或全角= + - @),或单元格以制表符或 CR 开头,就会在前面添加'。写入 XLSX 无需依赖项。sea-lite audit import AUDIT findings.csv会在单个事务中按id执行更新;它会移除'防护字符(导出 → 导入可往返),只从<gate>_result列读取门禁结果;如果存在未终止的引号、单元格数量与表头不符的行、重复或未知列,或重复 ID,就会拒绝导入并指出行号。导入的门禁结果属于声明(claimed:pass),不会计入审核;只有使用--trusted导入时才会计入,但这需要操作员角色;Agent 编写的 CSV 不能让自己的门禁通过。--out会写入一个新的 0600 文件,并将其重命名到目标位置,因此已植入的链接会被替换,而不会被跟随写入。
局限性(请阅读)
- 协作式围栏。 声明令牌可阻止行为规范的 Agent 修改自己未持有的工单,但无法阻止能够访问 SQLite 文件的进程直接编辑行。链式记录可以检测到这种情况(
sea-lite verify),但无法阻止它。 - 行为主体和会话由调用方自行声明。 在 stdio、CLI 和使用共享 HTTP bearer 的情况下,
--actor、--session和每次调用的actor_id都是调用方自行选择的值。会话可将诚实的 Agent 区分开来,但无法阻止进程说谎。每个行为主体的 HTTP 令牌会在一个信任域内将行为主体绑定到凭据。请参阅docs/THREAT-MODEL.md。 - 编辑钩子无法监控 Bash。 请参阅强制执行表;提交前防护是最后一道防线。
- 惰性过期。 过期的租约会在下次读取或写入涉及该工单时被发现(
sea_list/sea_next也会进行清理)。租约会在活动期间续期;如果持有者在整个租约期间都没有活动(例如构建超过 15 min,期间没有调用工具),就会失去租约——此时应使用更长的lease_seconds(≤ 3600)来声明。 - 旧版工单:在 0.4.0 之前创建的工单不带项目,因此在每个项目中都可见;在 0.4.0 之前获取的声明不带会话(升级后 10 分钟内会过期)。
- 一个文件,一个信任域。 使用 stdio,或使用 bearer 令牌的环回 HTTP;不支持 TLS、逐工单权限或多租户隔离。
路线图
请参阅 ROADMAP.md。下一步:具备范围感知能力的分派、批量修改。
开发
npm install
npm run build && npm test && npm run lint && npm run typecheck
npm run catalog:check # tools/list etc. must match .mcp-catalog/sea-lite.json byte for byte
工具目录是经过审核的产物:.mcp-catalog/sea-lite.json 是规范快照(通过 mcp-catalog-snap 生成),CI 会在检测到偏差时失败。如果有意更改了描述或架构,请运行 npm run catalog:snapshot,将新的 sha256 复制到 src/catalog.ts 中的 CATALOG_FINGERPRINT(doctor 会用它检查运行中的构建;有测试确保两者相同),然后提交差异。CI 在 Linux、macOS 和 Windows 上运行,使用 Node 20 和 22。
许可证
Apache-2.0。请参阅 LICENSE 和 NOTICE。
在 AZ 科奇斯县离网构建 — https://theexiledentrepreneur.com
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。