Skip to content

[EPIC-D][LOU-D21] createAgent agents can pause for approval and resume - #75

Merged
LinuxDevil merged 2 commits into
mainfrom
lou-d21-create-agent-approvals
Oct 1, 2026
Merged

LinuxDevil merged 2 commits into
mainfrom
lou-d21-create-agent-approvals

Conversation

@LinuxDevil

Copy link
Copy Markdown
Owner

What

  • createAgent() now runs every agent with an ApprovalStore: a new InMemoryApprovalStore per agent (added in src/execution/InMemoryApprovalStore.ts, exported from the root) or the new approvalStore option. A needsApproval tool pauses the run instead of throwing "requires approval but no approvalStore".
  • SimpleAgent.approvals:
    • list() returns the PendingApprovals this agent is paused on.
    • resolve({ id, approved, note? }) takes the existing ApprovalDecision shape, calls resumeAfterApproval() with the agent's store, registry, provider and run options (skills, subagents, maxSteps, toolConcurrency), and returns the continued run's ExecutionResult.
  • approve?: (call: PendingApproval) => boolean | Promise<boolean> option (open-harness style). It is a thin loop on top of the same pause/resume path, with no new executor code path. When a run pauses, the callback decides the call and the run resumes with that answer: true runs the tool, false sends the model the existing structured rejection. It applies to send(), sessions and approvals.resolve().
  • Sessions: a turn that pauses inside agent.session() is continued in that session when its approval is resolved. A new protected AgentSession.continueTurn() queues the continuation as the session's next turn and records its transcript.
  • Paused result contract is unchanged: finishReason: 'awaiting-approval' plus approvalId.
  • Docs:
    • README approval-gates section: new type-checked createAgent snippet.
    • API list, docs/api-overview.md (new Approvals section), docs/sessions.md and docs/sub-agents.md updated.
    • llms files regenerated; CHANGELOG Added entry.

Why

createAgent() is the zero-config path, but any needsApproval tool made its runs fail because no approval store was ever passed. eve pauses durably on needsApproval and open-harness has an approve callback. This brings the simple API to parity.

Acceptance criteria

  • createAgent({ approvalStore?: ApprovalStore }). When it is omitted, each agent gets its own InMemoryApprovalStore; none existed before, so it was added in the execution/approvals module, not in createAgent.ts.
  • approve callback auto-decides with no pause, built on top of the store/resume path.
  • A paused send() resolves with the existing awaiting-approval / approvalId shape.
  • agent.approvals.list() / agent.approvals.resolve({ id, approved, note? }) work, mirroring ApprovalDecision and delegating to resumeAfterApproval.
  • Sessions keep working: resolving continues the same session thread.
  • Tests in src/createAgentApprovals.test.ts (mockModel) cover:
    • pause then approve
    • pause then reject (structured rejection)
    • approve auto-approve and auto-deny
    • each agent's default store is isolated
    • a custom approvalStore
    • continuing a session
    • a sub-agent pause resumed through the lead
    • stream() pausing
  • Docs (README, api-overview, sessions, sub-agents) updated with one type-checked snippet; llms files regenerated; CHANGELOG entry.

Notes:

  • src/execution/AgentExecutor.ts, resume.ts, toolCallExecution.ts and ApprovalGate.ts are not touched.
  • serveMcp.test.ts had one test that relied on the old throw. It now expects the MCP server's existing "needs human approval ... cannot be approved over MCP" error.
  • stream() pauses but does not apply approve. A paused stream is continued with agent.approvals.resolve(), which does not stream.
  • list() covers pauses made by this agent in this process. With a durable store, resolve() by id still works after a restart, but it cannot re-attach the result to a session.

How verified

Ran after merging the latest origin/main (including #68):

  • npx tsc --noEmit: no errors
  • npm run lint: ✖ 433 problems (0 errors, 433 warnings). Same count as before the change; the changed files have 0 warnings.
  • npm run test:types: Type Errors no errors
  • npx vitest run: Test Files 134 passed (134), Tests 1986 passed | 3 skipped (1989)
  • npm run build && npm run build --workspace=packages/create-loushy-agent: exit 0
  • npm run docs:verify-snippets -- --skip-build: all 89 snippet(s) type-check against src/; 6 also run cleanly
  • npm run docs:llms:check: exit 0
  • npm run test:coverage: exit 0 (134 files, 1986 passed)
  • npm run fallow: exit 0. Dead code: ✓ No issues found; 0 above threshold. The only duplication reported is the existing Anthropic/OpenAI provider clone.

🤖 Generated with Claude Code

https://claude.ai/code/session_016bY29ibQgv3MDdjAbUT4YR


Generated by Claude Code

claude added 2 commits October 1, 2026 11:33
A needsApproval tool no longer fails a createAgent() run with "requires
approval but no approvalStore". Each agent now runs with an approval store
(a new InMemoryApprovalStore per agent, or the `approvalStore` option), so
the run pauses with finishReason 'awaiting-approval' and an approvalId.

- agent.approvals.list() returns the calls the agent is paused on;
  agent.approvals.resolve({ id, approved, note? }) calls
  resumeAfterApproval() with the agent's store, registry, provider and
  options and returns the continued run's result. A pause inside
  agent.session() continues in that session (AgentSession.continueTurn).
- `approve` option: decides each pause at once by resuming with the
  callback's answer (send(), sessions and approvals.resolve()).
- Executor, resume and ApprovalGate are untouched.
- serveMcp test updated: a createAgent() agent now pauses (and MCP reports
  the pause) instead of throwing.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016bY29ibQgv3MDdjAbUT4YR
Resolve docs/api-overview.md (keep both new table rows) and regenerate
llms files. Note createAgent approvals in docs/sessions.md.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016bY29ibQgv3MDdjAbUT4YR
@LinuxDevil
LinuxDevil merged commit 2e99668 into main Oct 1, 2026
1 check passed
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.

2 participants