Early preview · v0.1.0 · under construction Status & limitations

Sealmesh

Agents & providers

Sealmesh reads Claude Code natively. Any other agentic tool (Codex, Qwen, Ollama, something you wrote) gets in one of two ways, depending on whether the tool already writes its state to disk.

Claude Code

Sealmesh looks in every ~/.claude* directory that has jobs/ or sessions/, plus SEALMESH_CLAUDE_CONFIG_DIR and [claude] extra_roots. Background jobs come from jobs/*/state.json and interactive sessions from sessions/*.json. A session counts only while its process is alive with a matching start time, so a recycled pid can't impersonate one.

Install the hook for honest states: sealmesh install-claude-hook. It subscribes to UserPromptSubmit, Stop, PreToolUse (for AskUserQuestion only) and SessionEnd. That gives idle when a turn ends and NEEDS INPUT when a session asks a question, rather than guessing from silence. The hook reads its payload through a typed struct with no field for prose.

  • Subagents run inside the coordinator's process and write nothing to disk. Add SubagentStart and SubagentStop hooks by hand to give each worker its own row. They aren't installed by default because they fire on every Agent-tool call.
  • claude --bare skips hooks, so a bare session shows only through its session file.

Push: sealmesh declare

If you can run a command when your agent starts, changes state and stops, nothing needs configuring:

ID=$(echo -n "$RUN" | shasum | cut -c1-8)
sealmesh declare --id "$ID" --tool codex --worktree "$PWD" --state working

# when it exits, in a trap so a crash still fires it:
sealmesh declare --id "$ID" --end
  • --worktree must be the git repository the agent works in. A directory that doesn't resolve to a repository is dropped.
  • A record nobody ends keeps claiming a running agent until its TTL ([declared] ttl_secs, default 900 s) runs out, so refresh it periodically.

Pull: a [[provider]] block

If the tool writes one JSON file per run, describe where, and Sealmesh reads it. No code and no release. For a tool that writes ~/.qwen/runs/<id>.json:

[[provider]]
name   = "qwen"                    # the tool label on every row
root   = "/Users/you/.qwen/runs"
layout = "file_per_agent"          # the only layout v0.1 reads
priority = 150

[provider.time]
format = "rfc3339"                 # or "unix_ms", "unix_s"

[provider.fields]
work_dir   = "dir"                 # required
run_state  = "status"              # required
started_at = "started"             # required
branch     = "git.branch"          # optional, dotted path
pr         = "pull_request"        # optional

[provider.run_state]
working     = ["running", "busy"]
idle        = ["sleeping"]
needs_input = ["waiting", "blocked"]
done        = ["finished", "exited"]

[provider.liveness]
kind     = "ttl"                   # the only kind built so far
ttl_secs = 900

A provider block says where to look, never what is allowed. Every value goes through the same validation as a native provider's. Your allow.repos and allow.paths gate it exactly like Claude Code, and there are no predicates, regexes or arithmetic.

Ids you see in who are a hash of the tool name and the tool's own id, because some tools name a run after the prompt that started it. sealmesh doctor prints both ids side by side.

Who wins when two sources see one agent

SourcePriority
Claude Code job files300
sealmesh declare records200
[[provider]] blocks150 (configurable)
Claude Code session files100

Higher wins. Ties go to the record updated most recently, then to the provider name, so the result never depends on config order.

Not built yet

  • Native Codex reading. Codex can already mount the MCP server.
  • kind = "pid" liveness. The key is reserved, but only ttl works.
  • One file listing many agents.
  • Computed fields, and providers that shell out to a vendor CLI. Those need a native provider, which is a pull request.

Something wrong or unclear? Open an issue.