Skip to content

feat(sdk): give parents a resume_agent tool - #6

Merged
matej21 merged 1 commit into
mainfrom
feat/parent-resume-tool
Jul 30, 2026
Merged

feat(sdk): give parents a resume_agent tool#6
matej21 merged 1 commit into
mainfrom
feat/parent-resume-tool

Conversation

@matej21

@matej21 matej21 commented Jul 29, 2026

Copy link
Copy Markdown
Member

Closes #5.

The gap

The parent's system prompt (plugins/agents/plugin.ts) told it:

If a child pauses early it sends you a <child-paused agent="…">reason</child-paused> message (e.g. it hit a cost/limit budget). Decide what to do: resume it (after addressing the cause), reassign or drop the work, or stop.

Nothing backed that. The notification path is fine — I traced it end to end:

limits guard → { action: 'pause' }
  → emitHandlerPause()      core/agents/agent.ts:791
  → executeOnPause()        core/agents/agent.ts:1031
  → mailbox onPause hook    plugins/mailbox/plugin.ts:231
  → self.send(parentId, '<child-paused …>')

self.send dispatches through pluginMethodCaller('mailbox', 'send', …), the same path as any inter-agent message, so the parent really is woken. But resume existed only as a plugin method — the external RPC surface agents.resume — and .tools() handed the parent nothing but start_<agent_name>. grep -rn "name: 'resume" packages/sdk/src returned nothing.

So a paused child stayed paused: the parent was notified, was instructed to resume it, and had no tool to call. Same shape as a prompt telling an agent to run git push where a handler blocks it and a separate git_push tool exists.

What this adds

resume_agent, wrapping the existing method the way start_<agent_name> wraps spawn. It:

  • is only offered to agents that can spawn — an agent with no children has nothing to resume;
  • refuses an agent that is not the caller's own child. The method itself doesn't check parentage because it is also the external RPC surface, so the check belongs on the tool;
  • refuses an agent that isn't paused or errored;
  • states in both the tool description and the prompt that resuming does not grant budget. costSpent / tokensUsed are deliberately not reset on resume (so the cap can't be bypassed by pause/resume), which means a child that hit a cost limit stops again immediately. Saying so keeps the parent from retrying in a loop.

Why now

contember/webmaster#512 just registered limitsGuardPlugin on the customer-facing edit, preview and coding-only presets — it had been on redo only. That widened the set of ways a child can pause in front of a paying customer, which is what surfaced this.

Still open, out of scope here

On the webmaster side budget_exceeded / limit_warning are surfaced nowhere in the worker or the app, so a tripped limit still reads to the end user as "the agent stopped answering". Tracked in that repo's packages/agent/STATUS.md.

Verification

bun run ts:build clean, bun run lint clean (one pre-existing unrelated warning), full suite 907 pass / 8 skip / 0 fail.

Three new integration tests: the tool is offered to a spawning agent and not to a leaf; an orchestrator resumes its own paused child; and resuming a grandchild is refused with not your child while the grandchild stays paused.

🤖 Generated with Claude Code

https://claude.ai/code/session_018yiScAyGwDx3A3DqaUasEA

The parent's system prompt already told it that a paused child sends a
<child-paused> message and that it may "resume it (after addressing the
cause)". Nothing backed that instruction: resume existed only as a plugin
method, reachable from outside the session via agents.resume, and the agents
plugin handed the parent nothing but start_<agent_name> tools.

So a child that paused stayed paused. The parent was notified, was told to
resume it, and had no way to do so — the same shape as a prompt telling an
agent to run `git push` where a handler blocks it and a separate tool exists.

resume_agent wraps the existing method the way start_<agent_name> wraps spawn,
and only appears for agents that can spawn. It refuses agents that are not the
caller's own children, which the method itself does not check because it is
also the external RPC surface.

Resuming deliberately does not grant budget — costSpent/tokensUsed are not
reset on resume, so a child that hit a cost limit stops again immediately.
Both the tool description and the prompt now say so, so the parent does not
retry it in a loop.

Closes #5

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018yiScAyGwDx3A3DqaUasEA
@matej21
matej21 merged commit e3aabb6 into main Jul 30, 2026
1 check passed
@matej21
matej21 deleted the feat/parent-resume-tool branch July 30, 2026 11:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Parent is told to resume a paused child but has no resume tool

1 participant