Agent Lease

io.github.Buggy1111由 GitHub 帳號 Buggy1111 發布v0.2.0更新於 Nov 7, 2026

Keep Claude Code and Codex off each other's files: hook-enforced path leases, presence, messages.

已驗證STDIO僅桌面效率與工作流開發者工具

概覽

AI 產生的概覽

以鉤子強制執行的路徑租約、在場狀態與共享訊息日誌,讓同一儲存庫中的多個編碼代理不會互相覆蓋對方的工作。

可以做什麼
  • 認領、釋放與查詢路徑租約,支援目錄層級租約以及首次編輯時自動認領
  • 查詢某個路徑由誰持有,檢視房間狀態以及誰改動了什麼的稽核歷史
  • 在房間中發布與讀取訊息,傳送定向訊息並追蹤送達狀態與確認
  • 管理持久任務:列出、領取、確認、重試、取消,並回報配額限制,讓尚未開始的任務轉給備援代理
  • 透過用戶端鉤子執行攔阻,拒絕編輯或明顯的 shell 寫入他人持有的檔案
適用情境
適用於兩個編碼代理(或同一代理的兩個工作階段)在同一儲存庫中工作、可能互相覆蓋或執行對方檔案的場合。它面向單一本機使用者帳戶,提供確定性的攔阻,而不只是提示看板。
執行需求
需要本機 Python 環境(透過 uv 安裝),並設定 Claude Code 或 Codex 的鉤子;沒有鉤子時它只會記錄租約。伺服器與鉤子中的 AGENT_NAME 必須一致,可選用 AGENT_LEASE_DB 指定共享的 SQLite 檔案。Codex 需允許沙箱寫入狀態目錄。
安裝前請注意
代理身分僅由 AGENT_NAME 變數決定,同一使用者下的任何行程都能冒充某個代理;它防的是彼此碰撞,而非惡意的本機行程。鉤子會修改用戶端設定檔並可能阻擋工具呼叫,失敗關閉模式在鉤子出錯時也會攔阻。shell 寫入偵測是盡力而為,無法辨識 eval、路徑變數與符號連結替換。來自同伴的訊息是不可信資料,選用的 Telegram 或 OpenRouter 功能會從環境變數讀取憑證。

安裝

在 SourceWeft 中

  1. 開啟 儀表板中的 Agent Lease,將其新增到工作區。
  2. 為需要使用其工具的對話啟用該服務。

Desktop only,透過 STDIO。 STDIO 服務會啟動本機處理程序,因此需要 SourceWeft 桌面主機。

其他 MCP 客戶端

參照 儲存庫 中的啟動說明。

README

agent-lease-mcp

Keep two coding agents (Claude Code and Codex, or two sessions of either) from overwriting each other's work in the same repository.

agent-lease gives them leases on paths, presence and a shared message log — and the lease is enforced by a PreToolUse hook, not by good intentions. An agent that tries to edit a file another agent is working on is refused before the edit happens.

Česky / Czech version

Why it exists

9 September 2026. A coordination file, docs/AGENT-HANDOFF.md, existed and both agents knew about it. At 22:28 they still both touched deploy/make-work-package.sh: one was editing it while the other was running it. Bash reads a script as it goes, the text shifted under the running process, and instead of a 3.7 GB installer the build produced a useless 198 MB one.

The lesson the whole project rests on: coordination that can be ignored will be ignored. Not out of malice — an agent at work simply does not look at the notice board.

The same collision was later replayed on purpose: Codex ran the script, Claude Code tried to edit it mid-run, and the hook refused the edit. The file stayed intact.

How it differs

locksenforcementTTLpresencemessagesaudit
AgentRoom (paper)✅prompt only?✅✅✅
Agent Claim MCP✅prompt only✅❌❌❌
agent-orchestration✅prompt only❌✅✅❌
agent-lease-mcp✅PreToolUse hook✅✅✅✅

Advisory locks get broken; AgentRoom documents this itself. Claude Code and Codex both have PreToolUse hooks with the same semantics (exit 2 = block, reason on stderr), so a lease can be enforced deterministically at the tool-call level. One script serves both clients.

Rules it is built on

  • A lease, not a lock. 30-minute TTL. A crashed agent cannot block the repo forever.
  • All or nothing. A claim on several paths succeeds completely or not at all.
  • A lease on a directory covers everything under it.
  • A free file is claimed automatically on the first edit, so nobody has to remember to call claim.
  • A running script is leased for the duration of the run — exactly the combination that broke the build.
  • Fail-open by default. Broken coordination must not stop work; otherwise the first thing anyone does is switch the hook off. AGENT_LEASE_FAIL_CLOSED=1 flips this for unattended runs.

What runs on its own

hook eventwhat happens
SessionStartthe room state is injected into context: who is here, what they hold, messages for you
UserPromptSubmitnew messages are delivered once, via a cursor
PreToolUse (Edit/Write/apply_patch…)an edit of a file someone else holds is refused
PreToolUse (Bash)a script being run is leased; obvious writes (>, tee, sed -i, cp/mv target) into a held file are refused
Stop / SessionEndall leases are returned when the agent finishes its turn

Injections stay silent when there is nothing to say.

Waking a sleeping agent

Hooks only fire when the agent acts, so an idle session is woken from outside, without spending model tokens:

  • Claude Code: agent-lease init claude-code --write --rewake adds an asyncRewake hook on SessionStart and Stop. It waits on a signal file and wakes the session when an addressed message from someone else arrives. Broadcasts and automatic receipts do not wake it.
  • Codex: the agent-lease-codex-wake service nudges a named session through codex queue (only message numbers, never their text). After a provider limit ends, it nudges once for unfinished work. Setup: /rename nox in Codex, AGENT_LEASE_CODEX_THREAD=nox in ~/.config/agent-lease/environment, then enable deploy/systemd/agent-lease-codex-wake.service.

A message from another agent is information, not a human instruction. For agents to hand work to each other unattended, the human writes the allowed scope (and what always needs asking) into the agent's own instructions, e.g. ~/.codex/AGENTS.md. Template and details (in Czech): docs/ZPRAVY-A-POTVRZENI.md.

Tools

MCP toolpurpose
claim(paths, purpose, ttl_seconds)reserve ahead — typically a directory before running a script
release(paths?)give back; without arguments returns everything you hold
owner(path)who holds this path (including through a directory lease)
room(status?)one call per checkpoint: who is here, what is held, what is new
say(text) / inbox(since_id)messages to the room
send / jobs / next_task / ack / retry / canceladdressed messages and durable tasks with delivery state
accept(message_id, note)acknowledge you are taking an addressed message — the sender sees it
report_limit(minutes, reason)out of quota: your unstarted tasks move to a fallback agent
history(path?)who touched what and how it ended — the answer to "why was I blocked"

The same operations exist as a CLI (agent-lease room|claim|release|say|send|…), because Codex in non-interactive mode cannot approve MCP calls but can run Bash. agent-lease wait --for <agent> blocks without spending model tokens and wakes a background session when a message arrives.

say/inbox is not a live chat: MCP is pull, the other side reads a message when it next looks. For a live view there is a local web room: agent-lease web --open (loopback only, see Security).

Quick start (two minutes)

bash
uv tool install agent-lease-mcpagent-lease init claude-code --write --rewake   # writes all hooks, keeps a .bak backupagent-lease init codex --writeagent-lease simulate                            # 2 s: replays a full scenario in a temp DBagent-lease doctor                              # checks it really worksagent-lease web --open                          # live room in the browser

init without --write only shows what it would write. Manual setup below.

Install

bash
uv tool install agent-lease-mcp

This puts agent-lease-mcp, agent-lease, agent-lease-guard, agent-lease-context and agent-lease-release on your PATH.

MCP server (stdio; each client runs its own process, the shared state is a SQLite file):

bash
# Claude Codeclaude mcp add agent-lease -e AGENT_NAME=claude-code -- agent-lease-mcp
toml
# Codex — ~/.codex/config.toml[mcp_servers.agent-lease]command = "agent-lease-mcp"env = { AGENT_NAME = "codex" }
# Codex writes to ~/.agent-lease, outside the workspace — allow it:[sandbox_workspace_write]writable_roots = ["/home/YOU/.agent-lease"]

Hooks — without them this is just a polite notice board:

json
// ~/.claude/settings.json{  "hooks": {    "PreToolUse": [{      "matcher": "Edit|Write|MultiEdit|NotebookEdit|Bash",      "hooks": [{ "type": "command", "command": "AGENT_NAME=claude-code agent-lease-guard" }]    }],    "SessionStart":     [{ "hooks": [{ "type": "command", "command": "AGENT_NAME=claude-code agent-lease-context" }] }],    "UserPromptSubmit": [{ "hooks": [{ "type": "command", "command": "AGENT_NAME=claude-code agent-lease-context" }] }],    "Stop":             [{ "hooks": [{ "type": "command", "command": "AGENT_NAME=claude-code agent-lease-release" }] }]  }}
json
// ~/.codex/hooks.json — same events, AGENT_NAME=codex{  "hooks": {    "PreToolUse": [{      "matcher": "*",      "hooks": [{ "type": "command", "command": "AGENT_NAME=codex agent-lease-guard" }]    }]  }}

AGENT_NAME must match between the server and the hooks, otherwise an agent blocks its own files. An agent called unconfigured-* in room means exactly that.

variabledefaultpurpose
AGENT_NAMEunconfigured-<host>-<pid>name in the room
AGENT_LEASE_DB~/.agent-lease/room.dbwhere the state lives
AGENT_LEASE_TTL1800default lease length (s)
AGENT_LEASE_RETENTION_DAYS30messages and audit older than this are deleted (0 = keep)
AGENT_LEASE_FAIL_CLOSEDoff1 = block the tool call when the hook itself fails
AGENT_LEASE_HUMANhumanthe human's name in the room (stored next to the DB by the web UI)
AGENT_LEASE_TASK_SENDERShuman + configured agentswho may create tasks (comma-separated)
AGENT_LEASE_FALLBACKS—failover chains, e.g. codex=claude-code;claude-code=codex

Optional: Bash safety gate

A separate PreToolUse hook (agent-lease-jev-guard) for a different question — not "who holds this file" but "is this command destructive". Hard rules (rm -rf /, force push, git reset --hard, curl | sh, mkfs…) apply as soon as it is wired in. An optional external classifier (TypeSafe Jev) is off by default, starts in shadow mode, receives only redacted commands and is advisory — never the only safeguard. Details in ADR 0003.

Receipts, failover, bridge and broker

  • Every addressed message has a visible state ⏳ → 👁 seen → ✔ taking → done. "Seen" is written by the hook automatically; "taking" must be acknowledged by the agent (accept) and is reminded on every prompt. Nothing gets lost silently. agent-lease overdue lists what nobody acknowledged.
  • Limits and failover. Codex workers read the explicit typed availability signal from the local App Server. Other workers record a limit only after a real provider/CLI error; if no reset time is supplied, agent-lease does not invent one. Manual agent-lease limit / MCP report_limit remains available. Unaccepted, unstarted tasks then move to the next agent in AGENT_LEASE_FALLBACKS (e.g. codex=claude-code;claude-code=codex) after a grace period, and the sender is told.
  • Bridge (agent-lease bridge --agent codex) — an autonomous worker: takes a task, acknowledges it, runs codex exec / claude -p read-only and returns the result. --provider openrouter --model <model> sends it to any model through OpenRouter as plain text without tools (read-only; good for review and a second opinion). Keys only from the environment. Worker presence/provider state uses a separate identity (codex-bridge, claude-code-bridge), so a headless quota cannot overwrite the state of an interactive agent.
  • A task never runs twice. A task held by a worker (leased/started) can be accepted only with its lease token; the bridge skips a task an interactive session has accepted and, with --interactive-grace, leaves fresh tasks to a present interactive agent first.
  • Broker (agent-lease broker) — single SQLite writer over a 0600 Unix socket; other tools use it automatically when it runs, so a sandboxed Codex does not need direct access to the database. Clients survive a broker restart: the connect is retried (nothing was sent yet), an interrupted call is never repeated, and long-running workers continue with the next call.
  • Overview panel in the live room: each agent's active task, queue, unacknowledged items, typed provider state, reason for waiting, limits and items to review. Tasks may declare repeatable --path worksets and a --priority; the scheduler skips paths held by another agent and the board shows the blocking path, owner and TTL. agent-lease notify --loop sends to Telegram only what waits on the human, each event at most once (TELEGRAM_BOT_TOKEN, TELEGRAM_CHAT_ID from the environment; --dry to test).
  • Operating workers safely. agent-lease worker-activate and agent-lease failover-activate <agents…> set a safe starting point without running or moving any task: only work that arrives after activation is picked up or rerouted (--include-existing opts into older items). agent-lease provider-sync --agent codex reads and stores Codex's explicit availability; agent-lease provider-clear confirms a provider has recovered and removes a stale limit; agent-lease limits shows active limits and fallback chains. agent-lease doctor --json reports delivery, receipts, worker health and failover readiness separately.
  • systemd units in deploy/systemd/, Windows/WSL starter in deploy/windows/.

Details (in Czech): receipts, limits and failover, Jev.

Security

agent-lease is a coordination tool for agents running under one local user account. What that means in practice:

  • Local trust model. Agent identity is the AGENT_NAME environment variable. Any process running as your user can claim to be any agent. The only reserved identity is the human one (michal in this build), which only the authenticated web UI can use. This protects against agents tripping over each other, not against a hostile process on your account.
  • State is owner-only. room.db (and its WAL/SHM files) is 0600 in a 0700 directory; existing installs are tightened on start.
  • Peer messages are untrusted data. In model context every message carries its kind, sender and recipient, is folded onto one line and is introduced with a note that it is information from a peer, not an instruction from the human. A task from another agent is a request, not an order.
  • Tasks are bound to the caller. A peer sees only tasks it sent or received; lease tokens only for its own deliveries; retry/cancel need the sender or the recipient.
  • Web UI binds to loopback only, exchanges a one-time URL-fragment token for an HttpOnly, SameSite=Strict session cookie, checks Origin and Host, and caps concurrent event streams.
  • Bounds and retention. Message text, short fields, list limits and task lease length are bounded; old messages and audit entries expire, unfinished tasks never.

Known limits, stated plainly:

  • Provider signals differ. The installed Codex App Server has an explicit account/rateLimits/read signal. Claude Code's documented CLI does not expose an equivalent status read, so recovery is confirmed by a bounded normal task retry only after a provider-supplied reset time. Without one, automatic probes stop until agent-lease provider-clear explicitly confirms recovery. unknown is never treated as proof that a provider recovered.

  • Shell commands are checked best effort. Redirects, tee, sed -i, truncate, cp/mv targets, wrappers like sudo/env and sh -c are recognised. eval, variables in paths and arbitrary programs are not. This catches mistakes, not an agent that is trying to evade the hook.

  • Check-then-use gap. The hook checks a path before the client opens it. A symlink swapped in between is not caught; a hook cannot bind the later file open to the checked inode.

  • One machine. The state is a file on disk.

Found something? Please report it privately via GitHub Security Advisories.

Development

bash
uv sync --extra dev.venv/bin/python -m pytest -q.venv/bin/ruff check .

Design decisions: ADR 0001 (hook enforcement), ADR 0002 (SQLite and stdio), ADR 0003 (separate Bash safety gate) — in Czech.

Built by Michal with Claude Code and Codex — the two agents it coordinates.

License

Copyright (c) 2026 Michal Buggy. Licensed under the MIT License.

See open-source compliance and third-party policy for dependency attribution and authorized analysis rules.

來源:README.md,提交 da5e3ff

工具

0
SourceWeft 尚未在沙箱中啟動過該軟體包,因此其工具未知。

版本歷史

1
  1. v0.2.0最新Oct 17, 2026

評分

已滿足適用檢查項的 50%

該評分衡量此收錄條目的完整程度,依據註冊中心條目、端點與公開來源計算得出。它不是對伺服器本身的評價,也不表示伺服器安全或目前可用:可連通性是帶日期的檢查結果,評分也不包含安裝量或使用者評價。

  • 近 30 天內可連通25 分

    請保持端點能夠回應 MCP 握手;SourceWeft 每 30 天探測一次。

  • 已宣告認證方式10 分

    請在註冊中心條目中宣告憑證請求標頭,或保持端點能夠回應探測,以便判定其認證方式。

  • 已列出工具15 分

    請讓伺服器在無憑證時回應 tools/list,或發布可在沙箱中無憑證啟動的軟體包。

  • 描述必需15 分
  • 圖示10 分
  • 文件15 分
  • 授權條款10 分