
Sea Lite
io.github.rafe-walker由 GitHub 账号 rafe-walker 发布v0.6.0更新于 Nov 3, 2026
用于并行编码代理的工作队列:带租约的路径范围声明、类型化链接、哈希链式历史记录
AI 翻译由 AI 模型根据下方的原文描述翻译,可能有误。
原文描述
Work queue for parallel coding agents: leased path-scoped claims, typed links, hash-chained history
概览
Sea Lite 的本地 MCP 服务;无需登录;STDIO。
暂无 AI 概览:以下信息来自注册中心条目和服务本身。
安装
在 SourceWeft 中
- 打开 控制台中的 Sea Lite,将其添加到工作区。
- 为需要使用其工具的对话启用该服务。
Desktop only,通过 STDIO。 STDIO 服务会启动本地进程,因此需要 SourceWeft 桌面宿主。
其他 MCP 客户端
参照 仓库 中的启动说明。
README
sea-lite
A work queue for parallel coding agents that can't collide.
Run three agents against one backlog and the failure mode is not that they are slow. It is that two of them pick up the same ticket, both "finish" it, and the one that finishes second overwrites the first. Or one of them dies holding a ticket and nobody else ever touches it.
sea-lite is a small MCP server where tickets are fenced:
sea_nextatomically picks the best eligible ticket and claims it. The caller gets a one-time claim token; every write (sea_update,sea_transition,sea_link) must present it. A write from anyone else is refused and says who holds the ticket and until when.- Claims are leases (default 15 min, up to 1 h) that renew while the
holder is active: every sea-lite tool call by the holder, and the
sea-lite heartbeata hook runs, push the expiry out again. An agent that dies stops renewing; the next reader sees the lapsed lease, treats the ticket as free, and records that it did. No sweeper, no cron. - A claim can carry a scope: the paths the holder will edit. Two
holders' live scopes may not overlap (
scope_conflictnames the holder, ticket, paths and expiry), andsea_scope_checklets an edit hook refuse a write inside someone else's scope. A holder is an actor label plus a session, so two Claude Code terminals that both run--actor claudeare still two holders (see Identity). The numbers that motivated it: PR pairs from different agents conflicted 41.7% of the time, pairs from one agent 19.8% (arXiv 2607.04697) — agents could lock tickets but not each other's files. - Enforcement outside the model: an edit hook for Claude Code, and a
git pre-commit guard (
sea-lite precommit --install) that refuses a commit staging files inside someone else's live claim, which works the same for Cursor, Codex, Copilot and humans. - Tickets belong to the project (git repository) they were created in, so one shared store serves many repositories without one repo's queue or locks leaking into another.
- Tickets carry typed links (
depends_on,blocks,parent_of, …).sea_nextwill not hand out a ticket whose predecessors are still open, and the precedence graph is kept acyclic on every write. sea_applylays down up to 50 tickets and 100 links in one all-or-nothing write, so an epic with its children and dependency edges lands as a unit or not at all — and its links may point at tickets that already exist. Templates let the model write a 20-ticket chain once (for_each+{i}/{value}/{ref}/{prev}); the server expands it deterministically. The result is a ref → id map, not an echo.- Lean on context:
serve --corepublishes the nine day-to-day tools in ~12 KB oftools/list(the full fifteen take ~32 KB; 0.4.0 took 59 KB), and results are compact JSON.sea_finishends work on a ticket — an outcome (done/blocked/abandoned), a note, an optional commit sha, claim released — in one call, for one ticket or many. - A claim can be bound to a git worktree; removing the worktree
(
sea-lite release-worktree, aWorktreeRemovehook) releases it. - Every change is an event on a hash chain — the one
kronicle-lite keeps. The
same SQLite file holds both.
sea_historyshows a ticket's record and whether the chain still verifies;sea-lite rebuildreplays the tables from the chain.
One process, one file, no accounts. Speaks MCP 2026-07-28 (per-request
metadata, server/discover, cacheable tools/list, outputSchema +
structuredContent on every tool, multi round-trip confirmation) and still
serves 2025-era clients that open with initialize, over stdio and over
HTTP — the same process answers both. Runs over stdio next to Claude Code, Cursor, Codex, Copilot,
Claude Desktop or any MCP client, or over Streamable HTTP
(serve --http, loopback, bearer tokens, one token per actor if you want
attributable claims). sea_stats / sea-lite stats account for who held
what for how long; sea-lite doctor checks the install. Several server processes (one per agent,
or one shared) can point at the same file; SQLite's write lock and
BEGIN IMMEDIATE make claims exclusive across processes.
Install
Requires Node ≥ 20. Data lives in ~/.kronicle-lite/kronicle-lite.db (or
--data-dir, or $KRONICLE_LITE_DATA_DIR), shared with kronicle-lite if
you run that too. One store serves every repository: tickets are keyed by
project. The --actor label is what humans see on the board; you do
not need a different label per terminal — each server process is its
own session (see Identity).
Claude Code
--core serves the tools an agent uses day to day (see
Profiles); drop it for all fifteen.
Each Claude Code session's server picks up CLAUDE_CODE_SESSION_ID and
CLAUDE_PROJECT_DIR, so two sessions in one repo are two holders and
their queue is that repo's. Copy skills/work-queue/ into
.claude/skills/work-queue/ so the agent knows the claim → scope → work →
release loop, wire the edit hook from hooks/README.md (or install the
cc-anchor plugin, which ships it) so an Edit/Write inside someone
else's scope is blocked before it happens, and add the git guard:
Heartbeat. sea-lite renews a holder's leases on every sea-lite tool call
it makes, which covers an agent that keeps using the queue. A long stretch of
plain edits and test runs makes no sea-lite call, so also wire a
PostToolUse heartbeat: cc-anchor's heartbeat hook is the recommended
one (it renews your claims as you work, rate-limited, in the background),
or sea-lite heartbeat --hook from hooks/README.md. Measured
(agent-memory-bench S13, lease 5 s, task 12 s): without a hook the lease
lapses mid-task and the holder learns it only at its next fenced write; with
cc-anchor's heartbeat the claim held 3/3.
Cursor
.cursor/mcp.json in the project (or ~/.cursor/mcp.json for everywhere):
Cursor asks before running MCP tools unless you allowlist them; the
read-only ones (sea_get, sea_list, sea_history, sea_scope_check,
sea_stats) are safe to allow. Put skills/work-queue/SKILL.md into a project rule so
the agent claims its paths before editing.
Codex (CLI, IDE extension, app)
~/.codex/config.toml (or .codex/config.toml in a trusted project):
Codex reads the server instructions; the loop is in them.
GitHub Copilot coding agent
Repository Settings → Copilot → MCP servers, "MCP configuration". The
coding agent runs in a fresh Actions sandbox, so point it at a store the
run can reach, and allowlist tools explicitly: GitHub recommends read-only
tools, and Copilot code review only loads tools whose
annotations.readOnlyHint is true. Ours that qualify: sea_get,
sea_list, sea_history, sea_scope_check, sea_stats.
Once a server is configured Copilot calls its tools without asking, so add
the write tools (sea_create, sea_apply, sea_claim, sea_next,
sea_update, sea_transition, sea_finish, sea_link, sea_refresh,
sea_release)
only if you want the agent taking and closing tickets on its own. Secrets
and variables for the env block must be named COPILOT_MCP_*.
Any other client
A 2026-07-28 client probes with server/discover; a 2025 client sends
initialize; both are served.
Over HTTP (serve --http)
For a client that cannot spawn a process, or several agents sharing one queue server:
- Identity per request. The shared bearer acts as
--actor(role--role), and a per-callactor_idis a self-asserted label, as on stdio. An actor token acts as its actor (and its entry'srole); anactor_idnaming anyone else is refused withforbidden. So claims, notes, chain events andsea_statsare attributable per token. - Both protocol eras, stateless. POST to one endpoint; no session id;
GET/DELETE→ 405. 2026-07-28 clients send per-request_meta; 2025-era clients (Cursor today) open withinitializeand are served by the SDK's stateless legacy fallback from the same tools and the same per-request identity.--legacy rejectmakes the endpoint modern-only (initialize→ 400 /-32022). Missing or wrong bearer → 401; foreignOriginorHost→ 403. - Projects. A team server sees every project unless started with
--project KEY(scopes are repo-relative, so run one server per repository or name it). - One trust domain. Every token reaches the whole store; there are no
per-ticket permissions, no read isolation, no TLS. Off-box, put TLS in
front. Read
docs/THREAT-MODEL.md→ "HTTP transport" before exposing it.
Sixty-second tour
No MCP client and no LLM needed: the CLI drives the same queue the agents use. Every line below is run by the test suite against a fresh store.
board prints:
Agents show up as claude#9b1d0c22 (actor#session); watch -n1 sea-lite board in a spare pane is the live view.
The agent's loop, in tool calls:
Also: sea-lite list, show ID, stats --since 7d, rebuild,
doctor, whoami, heartbeat. sea-lite help lists everything.
Tools
Plus a sea://tickets/{id} resource (same shape as sea_get).
Every tool publishes an outputSchema and returns the same JSON as
structuredContent and as compact text. The published schemas are lean
(top-level result fields in full, nested objects as a field list such as
{id,kind,title,…}, no $schema or string-length noise); the server still
validates every argument and every result against the full schemas. Every error is { error: <code>, message, details } with a closed code set (fence_invalid, fence_foreign_holder,
fence_expired, already_claimed, scope_conflict, version_conflict,
illegal_transition, cycle, batch_invalid, confirmation_required,
confirmation_declined, …) so an agent can branch on it.
Profiles: core and full
tools/list is paid for in every session's context, so serve has two
profiles:
core also gives shorter descriptions, does not advertise rarely-needed
inputs (actor_id, idempotency_key, expected_version, sea_next's
kind / tag filters, operator-only force, …; they are still accepted),
and publishes result schemas as field names and types only. The behaviour
of every tool is identical in both. serve --http --core works too.
core leaves out sea_create, sea_update, sea_link, sea_list,
sea_history and sea_stats, and its server instructions say so: to get
them, run serve without --core (or with --profile full /
SEA_LITE_PROFILE=full). The CLI (sea-lite list, show, stats)
covers the read side either way.
Templated batches
A template repeats one ticket, and its links, for each value of for_each
(an array of strings/numbers, or a count n meaning 1..n). In every string
of the template's ticket and links, and in its ref:
Anything else in braces is left as written. Expansion is deterministic
(explicit tickets first, then each template's items in order), happens on
the server before validation, and the batch caps (50 tickets, 100 links),
cycle check and idempotency all apply to the expanded batch. dry_run: true
returns the expanded plan without writing.
Errors name exactly what failed. Each batch_invalid problem carries
where (tickets[2] (a), links[4], templates[0] item 3 (t03),
templates[0] item 3 (t03) links[1], claims[0]) plus the structured
template, item (1-based), ref and link indexes, and its message
starts with the same location. A cycle error gives the cycle as a path of
refs (existing tickets by id) and closed_by, the link that closes it.
Nothing is written when anything fails.
An epic and a 20-step migration chain, every step a child of the epic and each depending on the one before (21 tickets, 39 links), in one call:
Result: {"created":{"epic":"…","m1":"…",…,"m20":"…"},"links":39,"idempotent_replay":false}.
Re-sending it with the same idempotency_key replays that result
(idempotent_replay: true) instead of creating 21 more tickets. A key is
bound to its request: re-using it with a different batch is
invalid_input ("idempotency_key reused with a different request"), never
a replay of the first one — the same holds for every keyed tool.
Waiting for a claim (wait_seconds)
sea_claim and sea_next take wait_seconds (≤ 300; CLI --wait S).
Instead of failing with already_claimed / scope_conflict at once, the
call waits for the ticket or scope to free up and then claims it. The
result carries waited: {seconds, attempts, blocked_by: {kind, ticket_id, title, holder, holder_label, expires_at}, freed}, where freed is
released, finished (the holder closed the ticket), lease_expired or
changed. If it never frees up the call fails with wait_timeout, naming
the holder, ticket and expiry that still block it (CLI exit 1). sea_next
also waits when nothing is free but someone else holds work that could free
up (an eligible ticket, or a predecessor of one); an empty queue still
answers nothing_eligible at once. Refusals waiting cannot fix (unknown
ticket, bad input) fail at once.
How it waits, without hammering the store: nothing is locked while waiting.
A waiter retries only when something changed: another process committed
(SQLite PRAGMA data_version, one counter read, polled with a 20 → 250 ms
backoff), this process committed (an in-process notifier wakes it
immediately), or the blocking lease reached its expiry. A release in
another process is picked up within about 250 ms; in the same process,
immediately.
Client timeouts. A wait is one MCP request, so it must fit the client's
request timeout. The TypeScript SDK's default is 60 s; other clients
differ. When the client sends a progressToken, sea-lite sends a progress
notification every 5 s naming who it is waiting on, and a client that
resets its timeout on progress (resetTimeoutOnProgress in the TypeScript
SDK) will then sit through the whole wait. Otherwise keep wait_seconds
below your client's timeout. Cancelling the request ends the wait
(wait_cancelled).
Scopes: locking the files, not just the ticket
A ticket lock stops two agents from doing the same work. It does not stop them from editing the same file for different tickets — and that is where most cross-agent merge conflicts come from. So a claim can carry the paths its holder is going to touch:
- An entry without glob characters is a prefix: the path and, when it
is a directory, everything beneath it.
src/handlers/claims.tsis one file;src/handlersis the subtree;next.js/(trailing slash) says a dotted name is a directory. A git worktree or a package directory is a natural scope. - An entry with
* ? [ ] { }is a glob (picomatch): exactly the paths it matches.*stays inside one segment,**crosses..is the whole repository. - Two scopes overlap when any entry of one can name the same file as any
entry of the other (
src↔src/**/*.ts,docs/x.md↔docs/*.md; notsrc↔src2, not*.ts↔*.md). The full rule is indocs/MODEL.md. - Paths are resolved through symlinks (stdio
serve, the CLI, the hooks and the pre-commit guard know the repository): the longest existing prefix of an entry (the literal part before any glob) is replaced by its realpath, made repo-relative again.hlink/claims.ts, wherehlink → src/handlers, issrc/handlers/claims.tsto every claim and every check, and results show that form. A link that resolves outside the repository is refused (invalid_input,reason: "outside_repo").serve --httpserves many machines and keeps paths as written. - Overlap with someone else's live, unexpired scope — another actor,
or the same actor in another session — is refused with
scope_conflictlisting every holder, ticket, overlapping pair and expiry. Your own scopes never conflict with each other. Only claims of the same project are compared. - The scope lives and dies with the claim: release, a terminal transition,
or lease expiry frees it. It is on the chain (
sea_ticket.claimed), sosea-lite rebuildrestores it. sea_scope_check { paths }/sea-lite scope-checkis the fast path for a hook: one read, no claim. See Enforcement.- Paths are repo-relative with
/; backslashes are accepted and normalized, so Windows agents and POSIX agents lock the same entries.
Worktree-bound claims
An agent working in a git worktree can bind its claim to it:
worktree is an absolute path (normalized: C:\Repo\wt\ → c:/Repo/wt),
may come with or without paths (alone it locks no paths), and shows up
as claim.worktree in sea_get / sea_list. When the worktree is
removed, sea-lite release-worktree <path> — or the WorktreeRemove hook
in hooks/ (release-worktree --hook) — releases every live claim bound
to it, whoever holds it, chained as claim_released with
via: release_worktree. sea-lite doctor warns about live claims bound
to a worktree that no longer exists.
Identity: who holds a claim
A claim belongs to an actor (a label: claude, alice, agent-a)
and a session. Two claims are the same holder's only when both match.
Every stdio server writes a small entry to <data dir>/sessions/
(0600: actor, session, pid, ancestor pids, project) and removes it on
exit; dead entries are pruned on read. That registry is how the
PreToolUse hook of a Claude Code session, which only knows the hook
input's session_id, finds the identity its own MCP server claims under —
including after /clear, when the hook's session id changes but the
server keeps the one it was spawned with (verified against the
Claude Code environment variable reference:
CLAUDE_CODE_SESSION_ID reaches hooks, Bash and stdio MCP servers;
CLAUDE_PID reaches hooks and Bash). A hook that gives only a session
speaks for every agent of that session (main agent and subagents).
sea-lite whoami shows what a shell resolves to; sea-lite board lists
the live servers. Labels are self-asserted on stdio and on the CLI:
this separates honest sessions, it does not authenticate anyone. The
authenticated path is a per-actor token over HTTP. See
docs/THREAT-MODEL.md.
Enforcement: what actually stops an edit
Install the git guard per clone with sea-lite precommit --install (it
refuses to overwrite a pre-commit hook that is not its own; add
sea-lite precommit to yours). It resolves the committer like a hook
does: an agent committing through its Bash tool is recognised as its own
session, so its own claims never block it.
Accounting and health
sea_stats (MCP, read-only) and sea-lite stats [--since 24h|7d|ISO] [--for ACTOR] [--json] report, per actor: claims taken (and token
rotations, scoped claims), released by the holder, released by a terminal
transition, expired, force-released (and force releases issued as
operator), lease-seconds held, live claims, status transitions and tickets
created. Everything is derived from the chain plus the live projection, so
it costs no writes and gives the same answer after rebuild. Lease time
runs from each claimed event to whatever ended it (the recorded expiry
for a lapsed lease, which includes unchained refreshes) and is clipped to
the window. scope_conflict refusals are not chained, so they are not
counted. Over serve --http with actor tokens the actor column is tied to
a credential; on stdio it is the label each agent chose.
sea-lite doctor [--json] [--cwd DIR] [--actors-file F] is read-only (the
database is opened readonly; nothing is created or swept) and exits 1 if
any check fails: Node ≥ 20, data dir present and 0700, HTTP token files
0600 and well-formed, the database opens and passes quick_check,
sea_schema_version vs this build, the full chain verifies and every sea
row's chain_seq exists, claims (live; lapsed leases nobody swept for an
hour; claims on vanished worktrees), and the served tool catalog matches
the fingerprint pinned for this release.
Lifecycle
Work track moves are one step forward at a time. Any active ticket may enter a
holding state or a terminal state; holding states resume only to idea,
triaged or in_progress; terminals have no exit. An optional design track
(--tracks work,design) adds idea → in_design → ready → in_progress.
Kinds: feature research investigation refactor curiosity question other bug chore incident security epic spike log docs decision. Priorities: urgent high medium low parked (parked is never dispatched by sea_next).
Link types: parent_of relates_to inspired_by responds_to conflicts_with supersedes blocks depends_on condenses_into branches_from references duplicates. Direction reads from <type> to.
Composes with kronicle-lite
sea-lite does not have its own chain. It opens the kronicle-lite store (or
adopts an already-open one) and appends sea_ticket.* / sea_link.* /
sea.batch_applied events to the same hash chain, inside the same
transaction as the row change. That buys you:
- One
get_system_state. An agent that orients with kronicle-lite sees ticket creations, status changes and link changes in its delta. Routine claim churn (claimed,claim_released,claim_expired) rolls up by default so it does not crowd the briefing; a force release is enumerated. - Retractions. A tombstoned link is classified as a safety retraction by kronicle-lite's classifier, like a tombstoned block.
- One verify, one export.
kronicle-lite verify-chain,checkpointandexportcover sea events. Afterkronicle-lite import, runsea-lite rebuildto re-project the tables (import rebuilds documents only). - Shared idempotency.
idempotency_keylands in the chain's unique column under asea:prefix; a replay returns the first result.
You can run sea-lite without ever running kronicle-lite's server. The dependency is the library.
Limitations (read these)
- Cooperative fencing. A claim token stops a well-behaved agent from
writing a ticket it does not hold. It does not stop a process with the
SQLite file from editing rows directly. The chain makes that detectable
(
sea-lite verify), not impossible. - Self-asserted actors and sessions. On stdio, on the CLI and with the
shared HTTP bearer,
--actor,--sessionand per-callactor_idare values the caller chooses. Sessions keep honest agents apart; they do not stop a process that lies. Per-actor HTTP tokens bind the actor to a credential, within one trust domain. Seedocs/THREAT-MODEL.md. - The edit hook does not see Bash. See the enforcement table; the pre-commit guard is the backstop.
- Lazy expiry. A lapsed lease is noticed by the next read or write that
touches the ticket (or by
sea_list/sea_next, which sweep). Leases renew on activity; a holder that goes quiet for the whole lease (one build longer than 15 min with no tool call) loses it — claim with a longerlease_seconds(≤ 3600) for that. - Legacy tickets created before 0.4.0 carry no project and stay visible in every project; claims taken before 0.4.0 carry no session (they lapse within 10 minutes of the upgrade).
- One file, one trust domain. stdio, or loopback HTTP with bearer tokens; no TLS, no per-ticket permissions, no multi-tenant isolation.
Roadmap
See ROADMAP.md. Next up: scope-aware dispatch, bulk mutate.
Development
The tool catalog is a reviewed artifact: .mcp-catalog/sea-lite.json is
the canonical snapshot (via mcp-catalog-snap)
and CI fails on drift. Changed a description or schema on purpose? Run
npm run catalog:snapshot, copy the new sha256 into
CATALOG_FINGERPRINT in src/catalog.ts (what doctor checks a
running build against; a test keeps the two equal), and commit the diff.
CI runs on Linux, macOS and Windows, Node 20 and 22.
License
Apache-2.0. See LICENSE and NOTICE.
Built off-grid in Cochise County, AZ — https://theexiledentrepreneur.com
来源:README.md,提交 831d143
工具
0版本历史
4- v0.6.0最新Nov 3, 2026
- v0.5.0Nov 1, 2026
- v0.4.0Nov 1, 2026
- v0.2.0Nov 1, 2026
评分
已满足适用检查项的 50%
该评分衡量此收录条目的完整程度,依据注册中心条目、端点与公开来源计算得出。它不是对服务器本身的评价,也不表示服务器安全或当前可用:可连通性是带日期的检查结果,评分也不包含安装量或用户评价。
- 近 30 天内可连通25 分
请保持端点能够响应 MCP 握手;SourceWeft 每 30 天探测一次。
- 已声明认证方式10 分
请在注册中心条目中声明凭据请求头,或保持端点能够响应探测,以便判定其认证方式。
- 已列出工具15 分
请让服务器在无凭据时响应 tools/list,或发布可在沙箱中无凭据启动的软件包。
- 描述必需15 分
- 图标10 分
- 文档15 分
- 许可证10 分