lazy documentationv0.91

Pairing

lazy pair <task> drops you into an interactive agent session on a task — the same session its supervised turns run, with the same history, on the same branch. You talk to the agent directly, it edits the worktree, and when you exit lazy records a summary turn and puts the task back to blocked.

Where the session runs

In the task's container, alongside the task's own turns. Pairing joins the existing container with docker exec rather than starting a new one, so whatever else is running in it — dev servers, watchers, published ports — keeps running. If the container is gone (daemon restart, lazy upgrade, a manual docker rm), pairing starts one, the same way a supervised turn would.

This is a change from earlier versions, where pairing launched the agent as a host process against the task worktree. That had three problems, and moving into the container fixes all three:

The --host escape hatch

Branchless mode — lazy pair with no task, on a non-task branch — has no container to join. It requires an explicit --host, and prints what that means before launching.

On a normal task, --host is an optional claude-code-only opt-in to run the agent on your machine instead of in the task's container. There is deliberately no automatic fallback: if the container path fails, pairing fails loudly rather than quietly running an agent on your machine instead.

lazy pair <task>          # in the task's container (the normal case)
lazy pair <task> --host   # claude-code only: on your machine instead of the container
lazy pair --host          # branchless: no task, no worktree, no container
lazy pair --host --no-task  # same, on ANY branch — even a lazy/* branch with no task in this store

If you are on a lazy/* branch whose task this store does not know (a branch from another machine, or a task since closed), lazy pair refuses. Add --no-task (with --host) to skip task detection and pair in the current directory on your machine. It cannot be combined with a task argument, --unlock or --no-summary; --resume <session> works as in branchless mode.

Tools the paired session gets

A paired session is wired with this task's lazy_* MCP tools — the same scoped toolset the task's own agent gets, not the broader builder toolset. That is deliberate: the session is operating on one task, in that task's worktree, so it gets that task's tool channel or none. Tool registration fails loudly; a paired session with no lazy tools is broken, not degraded.

Cursor and Codex pairing

Cursor and Codex tasks pair the same in-container way: the interactive CLI runs inside the task's container on the same mounted home (~/.cursor / ~/.codex) the supervised turns used, resuming the task's own session. For Cursor this was previously a hard refusal, for a security reason that in-container pairing removed: a container-written Cursor chat lives in the worktree sandbox, a host cursor-agent reads your real ~/.cursor and cannot see it, and lazy will not close that gap by copying agent-authored history into your host home — that turns a task's chat log into a prompt-injection channel into a session running as you. Since the session now runs on the same side of the boundary as the agent that wrote the history, nothing is copied and there is nothing to escalate.

Honest gap: conversation capture and the AI end-of-session summary need session files lazy can read back, and Cursor's and Codex's are not that — so pairing on a Cursor or Codex task records the commits you made and skips the transcript and summary. Pairing says so rather than silently omitting them.

Pi pairing

Pi tasks pair the same in-container way: lazy pair launches pi interactively in the task container. When the task already has a session, the paired one resumes it with its history and continues the supervised work; otherwise a fresh session starts, and pairing says which it did.

Pi's session files are in Pi's own documented format, which lazy reads back: when the session ends, every session file the pairing wrote is captured into lazy's conversation store (searchable with lazy search), and the end-of-session summary is synthesized from that transcript — the same as for Claude Code. If saving a conversation fails, pairing says so and still writes the summary; the session file is untouched on disk either way.

Ending a session

Exit the agent. Lazy releases the pairing lock, restores the task's status, and (for Claude Code and Pi) captures the conversation and synthesizes a summary turn from it.

If the host side is killed — a daemon restart, lazy upgrade — the in-container process is signalled through a pidfile in the shared sandbox directory rather than left holding the session, because docker exec does not forward signals to the process it started.

lazy pair <task> --unlock

recovers a session that outlived its host command. It signals the in-container agent through that same pidfile, removes the pairing lock, and restores the task to blocked. If no pidfile is present there is nothing running to signal, and --unlock just clears the lock and status — it will not start a stopped container to go looking.

--host sessions have no pidfile: they are ordinary child processes of your shell, so exiting or killing the terminal is what ends them.