Agents and access

Handoffs between agents

Let two agents, say Codex and Claude Code, work on the same plan and pass work to each other, and set up a Stop hook so the other agent notices.

For agents: this page as markdown · llms.txt

Two agents can work on the same plan, and one can hand something to the other: Codex finishes a step and asks Claude Code to review it, say. A handoff is a comment thread addressed to an agent, so you see it in the plan like any other comment and can answer it yourself.

Only your own agents receive handoffs your agents send. Someone else's Claude Code never picks up work your Codex handed off, even on a plan you share.

How it works

  1. The sending agent calls hand_off with the plan, who it's for (codex, claude-code, or a specific session's id) and what it needs. It can anchor the handoff to a step or any other block.
  2. The receiving agent takes it with wait_for_handoff. Once taken, no other agent gets it, so two Claude Code windows never both start on it.
  3. The receiver does the work and answers in the thread with reply_comment, then resolve_comment when it's done. The sender reads the answer with get_feedback, or waits for it with wait_for_review.

When the other agent notices

planpage can't start an agent that isn't running. Nothing on a server can: an agent only works while it's in a turn. So a handoff reaches the other agent at one of these moments:

The other agent is… It sees the handoff…
Waiting for one As soon as it arrives, when it called wait_for_handoff. Ask it to "wait for handoffs from Codex".
Working On the result of its next planpage call, which lists handoffs waiting for it.
Finishing its turn If you set up the Stop hook below: it's told about the handoff and carries on instead of stopping.
Idle at the prompt, or closed Next time you prompt it, or it calls planpage.

Set up the Stop hook

The hook runs when the agent is about to finish its turn. If a handoff is waiting for it, the agent keeps going and picks it up. It tells the agent about each handoff once, so an agent that decides to leave one alone isn't stopped again for it.

The hook signs in with an API token, not the agent's own connection:

  1. Create a token in Your account → Agent connections. Read only is enough, for the workspaces your plans are in.
  2. Put it in an environment variable called PLANPAGE_TOKEN where the agent runs.
  3. Add the hook for your agent.

Claude Code. In ~/.claude/settings.json, merged into any hooks you already have:

{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "curl -fsS --max-time 10 -X POST -H \"Authorization: Bearer $PLANPAGE_TOKEN\" \"https://app.planpage.dev/api/v1/hooks/stop?client=claude-code\""
          }
        ]
      }
    ]
  }
}

Codex. In ~/.codex/config.toml:

[[hooks.Stop]]
[[hooks.Stop.hooks]]
type = "command"
command = "curl -fsS --max-time 10 -X POST -H \"Authorization: Bearer $PLANPAGE_TOKEN\" \"https://app.planpage.dev/api/v1/hooks/stop?client=codex\""

The hook is one request: POST https://app.planpage.dev/api/v1/hooks/stop?client=<client> answers {} when nothing is waiting, or a Stop hook decision that keeps the agent going. Use the same client name the other agent hands off to.