Skip to content

feat: durable delegated tool approval flow#2966

Open
anubra266 wants to merge 4 commits intomainfrom
feat/durable-delegated-tool-approval
Open

feat: durable delegated tool approval flow#2966
anubra266 wants to merge 4 commits intomainfrom
feat/durable-delegated-tool-approval

Conversation

@anubra266
Copy link
Copy Markdown
Contributor

@anubra266 anubra266 commented Apr 1, 2026

Summary

  • Adds support for tool approvals in delegated sub-agents running in durable execution mode

@changeset-bot
Copy link
Copy Markdown

changeset-bot bot commented Apr 1, 2026

⚠️ No Changeset found

Latest commit: 46a02c3

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@vercel
Copy link
Copy Markdown

vercel bot commented Apr 1, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
agents-api Ready Ready Preview, Comment Apr 3, 2026 11:37pm
agents-manage-ui Ready Ready Preview, Comment Apr 3, 2026 11:37pm
1 Skipped Deployment
Project Deployment Actions Updated (UTC)
agents-docs Skipped Skipped Apr 3, 2026 11:37pm

Request Review

@pullfrog
Copy link
Copy Markdown
Contributor

pullfrog bot commented Apr 1, 2026

TL;DR — Routes tool approval requests from delegated sub-agents through the durable workflow hook system (WDK) instead of the in-memory pub/sub bus, and fixes a re-delegation loop where approving a delegated tool caused the parent to re-send the original user message instead of continuing with the tool result.

Key changes

  • Parent-side detection of durable-approval-required artifacts in tool-wrapperwrapToolWithStreaming inspects delegation results for durable-approval-required data artifacts and populates pendingDurableApproval with delegatedApproval context including the sub-agent's actual toolCallId.
  • Post-approval continuation prompt — New isPostApproval flag in the workflow loop sends a continuation message instead of re-sending the original user input, breaking the re-delegation loop.
  • Delegated approval forwarding through A2A metadatarelationTools passes durableWorkflowRunId and delegatedToolApproval decision through delegation metadata so the sub-agent receives pre-approved tool calls on re-execution.
  • CredentialStoreRegistry and baseUrl fix in durable execution pathbuildAgentForStep now creates a CredentialStoreRegistry for parity with classic mode and appends /run/agents to the base URL so delegation A2A calls route correctly.
  • Remove in-memory pub/sub for durable delegated pathtool-approval.ts drops the toolApprovalUiBus.publish branch for delegated agents; replaced by the artifact flow.
  • Delegated approval SSE streamingcallLlmStep streams toolInputStart, toolInputDelta, toolInputAvailable, and toolApprovalRequest SSE events using the delegated tool's actual toolCallId so the UI displays the correct tool.
  • Handle AI SDK stopWhen throw for durable approval detectioncallLlmStep wraps agent.generate() in a try/catch so that when the AI SDK's stopWhen callback throws (instead of returning cleanly), pending durable approvals are still detected and surfaced. The same pattern is added to generateTaskHandler for the non-durable A2A path via a shared buildDurableApprovalResult helper.

Summary | 9 files | 4 commits | base: mainfeat/durable-delegated-tool-approval


Parent-side detection of delegated approval artifacts

Before: A delegated sub-agent needing tool approval used the in-memory toolApprovalUiBus which doesn't work across durable workflow boundaries, or threw an error caught by a try/catch wrapper.
After: The sub-agent returns a durable-approval-required data artifact that the parent's tool-wrapper detects after normal execution — no error-path handling needed.

wrapToolWithStreaming scans both message-level parts (result.parts[]) and artifact-level parts (result.artifacts[].parts[]) via a findApprovalRequired helper. When found, it populates ctx.pendingDurableApproval with the delegated approval context including subAgentId, toolCallId, toolName, and args. Delegation results are now stored in conversation history for durable workflows (skipHistoryStorage is false for delegate_to_* tools when durableWorkflowRunId is set), ensuring the next callLlmStep sees the delegation outcome.

tool-wrapper.ts · agent-types.ts · Agent.ts


Post-approval continuation prompt to prevent re-delegation loop

Before: After the user approved a delegated tool call, the workflow re-sent the original user message to the LLM — causing it to re-delegate to the sub-agent in an infinite loop.
After: An isPostApproval flag switches the user message to a continuation prompt ("Continue the conversation. The tool results above contain the information needed to respond to the user."), so the LLM processes the tool results rather than re-delegating.

The flag is set after each approval round in agentExecution.ts and reset on transfer. callLlmStep uses it to select the prompt strategy.

agentExecution.ts · agentExecutionSteps.ts


Delegated approval forwarding through A2A delegation metadata

Before: relationTools built delegation metadata without workflow or approval context — the sub-agent had no way to receive pre-approved tool calls on re-execution.
After: durable_workflow_run_id and approved_tool_calls (serialized JSON) are injected into the delegation metadata when the agentRunContext carries them.

Metadata field Source Purpose
durable_workflow_run_id agentRunContext.durableWorkflowRunId Links delegation to the parent's durable workflow
approved_tool_calls agentRunContext.delegatedToolApproval Pre-approves the specific tool call in the sub-agent

The agentRunContext is now threaded through relation-tools.tsrelationTools.tscreateDelegateToAgentTool.

relationTools.ts · relation-tools.ts


CredentialStoreRegistry, base URL fix, and SSE streaming for delegated approvals

Before: Durable execution path passed credentialStoreRegistry: undefined for MCP tools, used the bare API root as baseUrl (missing /run/agents), and had no SSE streaming for delegated tool approval UI events.
After: buildAgentForStep instantiates a CredentialStoreRegistry with default credential stores, appends /run/agents to the base URL so A2A delegation calls route correctly, and callLlmStep emits the full SSE event sequence (toolInputStarttoolInputDeltatoolInputAvailabletoolApprovalRequest) using the delegated tool's toolCallId.

The hook token in agentExecution.ts now uses delegatedApproval.toolCallId (the sub-agent's actual tool call ID) so the WDK hook matches the approval the UI sends back.

agentExecutionSteps.ts · tool-approval.ts


Handle AI SDK stopWhen throw for durable approval detection

Before: callLlmStep called agent.generate() without a try/catch, relying on stopWhen returning cleanly when a pending approval was detected. If the AI SDK threw instead (e.g. during tool result processing), the pending approval was lost and the workflow errored out.
After: callLlmStep wraps generate() in a try/catch that checks for pendingDurableApproval before re-throwing. The same pattern is added to generateTaskHandler's error handler via a shared buildDurableApprovalResult helper.

Why does stopWhen sometimes throw? The AI SDK's stopWhen callback runs during the generation loop. When it signals a stop, the SDK may throw an internal error rather than returning a clean response — depending on where in the tool-call lifecycle the stop occurs. The try/catch ensures pending approvals are captured regardless of how the SDK surfaces the stop.

agentExecutionSteps.ts · generateTaskHandler.ts

Pullfrog  | View workflow run | Triggered by Pullfrog | Using Claude Opus𝕏

Copy link
Copy Markdown
Contributor

@pullfrog pullfrog bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Solid approach — the artifact-based signaling pattern for durable delegated approvals is a clean replacement for the in-memory pub/sub, and the isPostApproval continuation prompt correctly breaks the re-delegation loop. Three issues to flag, one potentially breaking.

Pullfrog  | Fix all ➔Fix 👍s ➔View workflow run | Using Claude Opus𝕏

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 1, 2026

Preview URLs

Use these stable preview aliases for testing this PR:

These point to the same Vercel preview deployment as the bot comment, but they stay stable and easier to find.

Raw Vercel deployment URLs

Copy link
Copy Markdown
Contributor

@claude claude bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR Review Summary

(7) Total Issues | Risk: Medium

🟠⚠️ Major (3) 🟠⚠️

Inline Comments:

  • 🟠 Major: tool-wrapper.ts:231 Unsafe type coercion of approval artifact fields without validation
  • 🟠 Major: agentExecutionSteps.ts:555-576 SSE streaming for delegated approvals lacks error handling
  • 🟠 Major: agentExecution.ts:82-85 Durable workflow hook awaits indefinitely without timeout

🟡 Minor (1) 🟡

Inline Comments:

  • 🟡 Minor: agentExecution.ts:82-86 Improve logging for delegated approval debugging

🕐 Pending Recommendations (3)

From pullfrog's prior review (still applicable):


🚫 REQUEST CHANGES

Summary: The artifact-based signaling pattern is a solid architectural choice for durable delegated approvals, and the isPostApproval continuation prompt correctly addresses the re-delegation loop. However, there are three major issues that should be addressed before merging:

  1. Artifact validation — The durable-approval-required artifact fields are used without validation, which could cause silent corruption or unhelpful runtime errors if a sub-agent returns malformed data.

  2. SSE error handling — The delegated approval SSE streaming block has no error handling. If writes fail, the workflow suspends awaiting approval but the client never receives the approval request UI — a particularly confusing failure mode.

  3. Indefinite workflow suspension — Unlike the non-durable path (10-minute timeout), the durable workflow hook can suspend indefinitely. Consider either adding a timeout or documenting the expected cleanup strategy.

The pullfrog findings about baseUrl convention and type casting are worth addressing as follow-ups but are less critical.

Discarded (3)
Location Issue Reason Discarded
agentExecutionSteps.ts:155-167 Empty catch blocks in enhanceRelation Pre-existing code not introduced by this PR
tool-wrapper.ts:196 Nested artifact parsing without bounds Low likelihood of malicious sub-agent responses in controlled environment
agentExecutionSteps.ts:503 isPostApproval may not survive workflow replays Workflow engine handles step replay correctly; flag is set consistently in workflow scope
Reviewers (3)
Reviewer Returned Main Findings Consider While You're Here Inline Comments Pending Recs Discarded
pr-review-errors 4 0 0 0 2 0 2
pr-review-sre 5 0 0 0 2 0 3
pr-review-standards 0 0 0 0 0 0 0
Total 9 0 0 0 4 0 5

Note: pr-review-standards found no issues — validated that baseUrl change is consistent with existing patterns in executionHandler.ts and data/agents.ts.

Copy link
Copy Markdown
Contributor

@claude claude bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR Review Summary

(8) Total Issues | Risk: Medium | Review Scope: Delta (1 commit since last review)

This is a re-review scoped to changes in commit 5aea41ec7 ("fix: handle AI SDK stopWhen throw for durable approval detection").

🟠⚠️ Major (1) 🟠⚠️

Inline Comments:

  • 🟠 Major: agentExecutionSteps.ts:533-537 Swallowing errors silently when pendingDurableApproval is set

🟡 Minor (1) 🟡

Inline Comments:

  • 🟡 Minor: generateTaskHandler.ts:585-588 Catch block suppresses original error without logging

🕐 Pending Recommendations (6)

From prior automated review (claude):

From pullfrog's prior review:


🚫 REQUEST CHANGES

Summary: The delta commit (5aea41ec7) correctly addresses the AI SDK stopWhen throw behavior by catching the error and extracting the pending approval state. However, silently discarding caught errors without logging creates a debugging blind spot. When errors are suppressed because pendingDurableApproval exists, engineers have no visibility into what actually failed. Add logging before suppressing these errors.

The 6 pending recommendations from the prior review remain unaddressed and should be considered alongside this delta feedback.

Discarded (1)
Location Issue Reason Discarded
agentExecutionSteps.ts:603-605 Error message lacks context Low severity — the check is effectively dead code given the control flow (if catch re-throws, this line is unreachable)
Reviewers (1)
Reviewer Returned Main Findings Consider While You're Here Inline Comments Pending Recs Discarded
pr-review-errors 3 0 0 0 2 0 1
Total 3 0 0 0 2 0 1

Note: Delta review — only dispatched error handling reviewer since the changes were focused on error/exception handling patterns.

Copy link
Copy Markdown
Contributor

@claude claude bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR Review Summary

(0) Total Issues | Risk: Low

This is a re-review following prior feedback from pullfrog and two automated claude reviews. All previously identified issues have been addressed.

✅ Issues Resolved Since Prior Reviews

Prior Issue Resolution
🟠 Unsafe type coercion of approval artifact fields (tool-wrapper.ts:232-245) Fixed — Added validation with typeof checks and error logging before using delegatedToolCallId and delegatedToolName
🟠 SSE streaming for delegated approvals lacks error handling (agentExecutionSteps.ts:583-611) Fixed — Wrapped SSE writes in try-catch with error logging and re-throw
🟠 Durable workflow hook awaits indefinitely (agentExecution.ts:98-101) Documented — Comment explains the design decision and cleanup strategy via external job
🟡 Improve logging for delegated approval debugging (agentExecution.ts:88-96) Fixed — Added logger.info with hookToolCallId, parentToolCallId, isDelegated, and workflowRunId
🟡 Swallowing errors silently when pendingDurableApproval is set (agentExecutionSteps.ts:545-553) Fixed — Added logger.info capturing error message, error name, requestId, and currentSubAgentId
🟡 Catch block suppresses original error without logging (generateTaskHandler.ts:591-598) Fixed — Added logger.info with toolCallId, toolName, and error message
🟡 baseUrl convention deviation (agentExecutionSteps.ts:117-121) Documented — Comment explains the intentional deviation for durable mode
🟡 Type casting delegationMeta (relationTools.ts:434-452) Accepted — The spread pattern is safe here since we're adding fields, not removing them
🟡 Continuation prompt dependency on conversation history (agentExecutionSteps.ts:507-510) Documented — Comment explains the dependency on skipHistoryStorage fix

Architecture Assessment

The artifact-based signaling pattern for durable delegated approvals is well-designed:

  1. Artifact flow — Sub-agents return durable-approval-required artifacts that the parent's tool-wrapper detects and propagates through pendingDurableApproval
  2. Re-delegation loop preventionisPostApproval flag switches to continuation prompt after approval rounds
  3. Delegation metadata forwardingdurableWorkflowRunId and approvedToolCalls flow through A2A metadata
  4. SSE event streaming — Delegated approvals stream the correct toolCallId from the sub-agent

⚠️ Operational Note

The durable workflow hook (toolApprovalHook.create()) suspends indefinitely by design. The comment at lines 98-101 documents that stale suspended workflows should be cleaned up by an external job querying workflow_executions with status='suspended'. Ensure this cleanup mechanism exists or is planned.

Missing Changeset

The changeset-bot flagged that this PR has no changeset. Since this adds support for tool approvals in delegated sub-agents running in durable execution mode, consider adding:

pnpm bump patch --pkg agents-api "Add support for tool approvals in delegated sub-agents during durable execution"

✅ APPROVE

Summary: All prior review feedback has been addressed. The implementation is solid — artifact-based signaling cleanly replaces the in-memory pub/sub for durable delegated approvals, error handling is comprehensive with logging, and the isPostApproval continuation prompt correctly breaks the re-delegation loop. Ship it! 🚀

Reviewers (0)
Reviewer Returned Main Findings Consider While You're Here Inline Comments Pending Recs Discarded
Total 0 0 0 0 0 0 0

Note: This re-review validated that all prior findings have been addressed. No new reviewers were dispatched since the diff showed only fixes to previously identified issues.

@github-actions github-actions bot deleted a comment from claude bot Apr 2, 2026
@anubra266 anubra266 force-pushed the feat/durable-delegated-tool-approval branch from f6e94ae to e8954e7 Compare April 2, 2026 00:45
@vercel vercel bot temporarily deployed to Preview – agents-docs April 2, 2026 00:45 Inactive
Copy link
Copy Markdown
Contributor

@claude claude bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR Review Summary

(0) Total Issues | Risk: Low

This is a re-review triggered by CI re-run. The code is unchanged from the prior APPROVE review — all previously identified issues remain resolved.

✅ Verified Fixes (from prior reviews)

Prior Issue Status
🟠 Unsafe type coercion of approval artifact fields ✅ Fixed — tool-wrapper.ts:232-245 has typeof validation
🟠 SSE streaming lacks error handling ✅ Fixed — agentExecutionSteps.ts:583-611 has try-catch with re-throw
🟠 Durable workflow hook awaits indefinitely ✅ Documented — agentExecution.ts:92-95 explains cleanup strategy
🟡 Missing logging for delegated approval debugging ✅ Fixed — agentExecution.ts:85-90 logs hook details
🟡 Silent error swallowing when pendingDurableApproval set ✅ Fixed — agentExecutionSteps.ts:545-553 logs caught errors
🟡 Catch block suppresses original error ✅ Fixed — generateTaskHandler.ts:591-598 logs error context
🟡 baseUrl convention deviation ✅ Documented — agentExecutionSteps.ts:117-121 explains intentional deviation
🟡 Continuation prompt history dependency ✅ Documented — agentExecutionSteps.ts:507-510 explains reliance on skipHistoryStorage

Architecture Assessment

The artifact-based signaling pattern for durable delegated approvals is well-designed:

  1. Artifact flow — Sub-agents return durable-approval-required artifacts that the parent's tool-wrapper detects and propagates via pendingDurableApproval
  2. Re-delegation loop preventionisPostApproval flag switches to continuation prompt after approval rounds
  3. Delegation metadata forwardingdurableWorkflowRunId and approvedToolCalls flow through A2A metadata
  4. SSE event streaming — Delegated approvals stream the correct toolCallId from the sub-agent

⚠️ CI Note

The "Auto Format" workflow failure is a CI infrastructure issue (pnpm setup step), not a code problem.

Missing Changeset

Consider adding a changeset for this feature:

pnpm bump patch --pkg agents-api "Add support for tool approvals in delegated sub-agents during durable execution"

✅ APPROVE

Summary: All prior review feedback has been addressed. The implementation is solid — artifact-based signaling cleanly replaces the in-memory pub/sub for durable delegated approvals, error handling is comprehensive with logging, and the isPostApproval continuation prompt correctly breaks the re-delegation loop. Ship it! 🚀

Reviewers (0)
Reviewer Returned Main Findings Consider While You're Here Inline Comments Pending Recs Discarded
Total 0 0 0 0 0 0 0

Note: Re-review — validated prior fixes are still present. No new reviewers dispatched.

When a delegated sub-agent requires tool approval in durable mode, the approval
now flows through the workflow hook system instead of the in-memory pub/sub bus.
The parent agent detects `durable-approval-required` artifacts from sub-agents,
surfaces them as SSE approval events, and forwards the user's decision back via
`delegatedToolApproval` context on re-execution. Adds `isPostApproval` flag to
prevent the workflow loop from re-sending the original user message after approval.

Includes validation of approval artifact fields, SSE error handling for delegated
approval streaming, logging for suppressed errors in the durable approval catch
path, and type-safe metadata construction for delegation.
@anubra266 anubra266 force-pushed the feat/durable-delegated-tool-approval branch from e8954e7 to edb0a26 Compare April 3, 2026 22:46
@vercel vercel bot temporarily deployed to Preview – agents-docs April 3, 2026 22:46 Inactive
@vercel vercel bot temporarily deployed to Preview – agents-docs April 3, 2026 22:47 Inactive
Copy link
Copy Markdown
Contributor

@claude claude bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR Review Summary

(0) Total Issues | Risk: Low

This is a re-review following 4 prior automated reviews. This review validates that all previously identified issues have been addressed.

✅ Issues Resolved Since Prior Reviews

Prior Issue Resolution
🟠 Unsafe type coercion of approval artifact fields ✅ Fixed — tool-wrapper.ts:232-245 validates typeof before using delegatedToolCallId and delegatedToolName
🟠 SSE streaming for delegated approvals lacks error handling ✅ Fixed — agentExecutionSteps.ts:597-625 wraps SSE writes in try-catch with error logging and re-throw
🟠 Durable workflow hook awaits indefinitely without timeout ✅ Documented — agentExecution.ts:94-97 explains cleanup strategy via external job querying workflow_executions
🟡 Missing logging for delegated approval debugging ✅ Fixed — agentExecution.ts:87-92 logs hookToolCallId, parentToolCallId, isDelegated, and workflowRunId
🟡 Silent error swallowing when pendingDurableApproval set ✅ Fixed — agentExecutionSteps.ts:559-567 logs error message, error name, requestId, and currentSubAgentId
🟡 Catch block suppresses original error without logging ✅ Fixed — generateTaskHandler.ts:620-627 logs toolCallId, toolName, and error message
🟡 baseUrl convention deviation ✅ Documented — agentExecutionSteps.ts:123-128 explains the intentional deviation for durable mode
🟡 Continuation prompt dependency on conversation history ✅ Documented — tool-wrapper.ts:110-114 explains the dependency on skipHistoryStorage fix

Architecture Assessment

The artifact-based signaling pattern for durable delegated approvals is well-designed:

  1. Artifact flow — Sub-agents return durable-approval-required artifacts that the parent's tool-wrapper detects via findApprovalRequired helper and propagates through pendingDurableApproval
  2. Re-delegation loop preventionisPostApproval flag in agentExecution.ts switches to continuation prompt after approval rounds, preventing infinite delegation loops
  3. Delegation metadata forwardingdurableWorkflowRunId and approved_tool_calls flow through A2A metadata in relationTools.ts
  4. SSE event streaming — Delegated approvals stream the correct toolCallId from the sub-agent (not the parent's delegate tool call ID)
  5. Type safetyDelegatedApprovalContext type properly captures the sub-agent's tool context

⚠️ Missing Changeset

Per the changeset-bot comment, this PR adds a user-facing feature but has no changeset. Consider adding:

pnpm bump patch --pkg agents-api "Add support for tool approvals in delegated sub-agents during durable execution"

💡 APPROVE WITH SUGGESTIONS

Summary: All prior review feedback has been addressed. The implementation is solid — artifact-based signaling cleanly replaces the in-memory pub/sub for durable delegated approvals, error handling is comprehensive with logging, type validation prevents malformed artifacts from corrupting state, and the isPostApproval continuation prompt correctly breaks the re-delegation loop.

The only outstanding item is the missing changeset, which should be added before merging to ensure proper versioning.

Reviewers (0)
Reviewer Returned Main Findings Consider While You're Here Inline Comments Pending Recs Discarded
Total 0 0 0 0 0 0 0

Note: This re-review validated that all prior findings have been addressed. No new reviewers were dispatched since the code showed only the final implementation with all fixes applied.

Copy link
Copy Markdown
Contributor

@claude claude bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR Review Summary

(0) Total Issues | Risk: Low | Review Scope: Delta

This is a re-review scoped to the delta since the last automated review (ad195a0736defd54af).

Delta Analysis

The delta consists of:

  1. Merge commit (6defd54af) — merging main into the feature branch
  2. Logging PR (#3007) — adds performance logging for Doltgres database operations

Feature code unchanged: The 9 files implementing the durable delegated tool approval feature (tool-wrapper.ts, agentExecution.ts, agentExecutionSteps.ts, generateTaskHandler.ts, relationTools.ts, relation-tools.ts, tool-approval.ts, Agent.ts, agent-types.ts) have no changes in this delta.

The merged logging additions are in unrelated files:

  • function-tools.ts — slow withRef timing
  • TriggerService.ts — execution dispatch logging
  • branchScopedDb.ts / ref.ts — middleware timing
  • ref-helpers.ts / ref-scope.ts — connection/checkout timing

These are standard observability improvements that don't interact with the approval flow.

✅ Prior Issues Remain Resolved

All issues from prior reviews (pullfrog + 4 claude reviews) were addressed in earlier commits:

  • ✅ Artifact validation with typeof checks
  • ✅ SSE streaming error handling with try-catch
  • ✅ Durable hook timeout documented (external cleanup job)
  • ✅ Logging for delegated approval debugging
  • ✅ Error logging when pendingDurableApproval suppresses exceptions
  • baseUrl deviation documented
  • ✅ Continuation prompt history dependency documented

⚠️ Missing Changeset

This PR still needs a changeset for the durable delegated tool approval feature:

pnpm bump patch --pkg agents-api "Add support for tool approvals in delegated sub-agents during durable execution"

💡 APPROVE WITH SUGGESTIONS

Summary: The delta contains only merged-in logging changes from PR #3007 — no modifications to the feature implementation. All prior review feedback remains addressed. The only outstanding item is the missing changeset, which should be added before merging. Ship it! 🚀

Reviewers (0)
Reviewer Returned Main Findings Consider While You're Here Inline Comments Pending Recs Discarded
Total 0 0 0 0 0 0 0

Note: Delta review — no sub-reviewers dispatched since feature code is unchanged. Only merged-in logging changes from PR #3007.

Copy link
Copy Markdown
Contributor

@claude claude bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR Review Summary

(0) Total Issues | Risk: Low | Review Scope: Delta

This is a re-review scoped to changes since the last automated review (6defd54af46a02c31a).

Delta Analysis

Single commit: 46a02c31a fix: use 'as const' for tool_calls type literal to satisfy typecheck

The change is a trivial TypeScript type narrowing fix:

-        type: 'tool_calls',
+        type: 'tool_calls' as const,

This ensures the return type is properly narrowed to the literal 'tool_calls' for correct type inference in the CallLlmResult union type. No functional change.

✅ Prior Issues Remain Resolved

Verified all fixes from prior reviews are still in place:

Prior Issue Verification
🟠 Artifact validation tool-wrapper.ts:232-245typeof checks before using delegatedToolCallId/delegatedToolName
🟠 SSE streaming error handling agentExecutionSteps.ts:597-628 — try-catch with error logging and re-throw
🟠 Durable hook timeout agentExecution.ts:94-97 — documented cleanup strategy via external job
🟡 Delegated approval logging agentExecution.ts:87-92 — logs hookToolCallId, parentToolCallId, isDelegated
🟡 Error logging when suppressing agentExecutionSteps.ts:559-567 — logs error context before continuing
🟡 generateTaskHandler error logging generateTaskHandler.ts:620-627 — logs toolCallId, toolName, error message
🟡 baseUrl convention agentExecutionSteps.ts:123-128 — documented intentional deviation
🟡 Continuation prompt dependency tool-wrapper.ts:110-114 — documented history storage fix

Architecture Assessment

The artifact-based signaling pattern for durable delegated approvals is well-designed:

  1. Artifact flow — Sub-agents return durable-approval-required artifacts that the parent's tool-wrapper detects and propagates through pendingDurableApproval
  2. Re-delegation loop preventionisPostApproval flag switches to continuation prompt after approval rounds
  3. Delegation metadata forwardingdurableWorkflowRunId and approved_tool_calls flow through A2A metadata
  4. SSE event streaming — Delegated approvals stream the correct toolCallId from the sub-agent

⚠️ Missing Changeset

This PR still needs a changeset for the durable delegated tool approval feature:

pnpm bump patch --pkg agents-api "Add support for tool approvals in delegated sub-agents during durable execution"

✅ APPROVE

Summary: The delta is a trivial TypeScript type narrowing fix (as const). All prior review feedback has been addressed and verified. The implementation is solid — artifact-based signaling cleanly replaces the in-memory pub/sub for durable delegated approvals, comprehensive error handling with logging, and the isPostApproval continuation prompt correctly breaks the re-delegation loop.

Add the changeset before merging, then ship it! 🚀

Reviewers (0)
Reviewer Returned Main Findings Consider While You're Here Inline Comments Pending Recs Discarded
Total 0 0 0 0 0 0 0

Note: Delta review — no sub-reviewers dispatched since the change is a trivial type annotation (as const). All prior findings verified as still resolved.

@itoqa
Copy link
Copy Markdown

itoqa bot commented Apr 4, 2026

Ito Test Report ❌

11 test cases ran. 1 failed, 10 passed.

Across 11 total test cases, 10 passed and 1 failed, so the unified run is a failure due to a single high-severity production defect despite broad delegated-approval coverage passing. The critical finding is that after delegated approval succeeds, the reconnect endpoint (/run/api/executions/{executionId}/stream) reads only the default namespace and drops post-approval continuation/completion events (introduced by this PR), while all other verified behaviors worked as designed, including durable playground/OpenAI-compatible approval flows, strict delegated toolCallId token binding, /run/api/chat mixed/unknown/out-of-order idempotent handling and validation, mobile approval actionability, and single-resolution approval state.

❌ Failed (1)
Category Summary Screenshot
Happy-path ⚠️ Approval succeeds, but reconnect streaming misses post-approval continuation/completion events. ROUTE-3
⚠️ Execution stream reconnect drops post-approval continuation namespace
  • What failed: The approval endpoint returns success, but reconnect clients only receive initial/default-namespace chunks and miss the continuation/completion events expected after approval.
  • Impact: Clients that reconnect after approval can appear stuck in an incomplete state and fail to show final assistant output. This breaks reliability of durable resume flows for API consumers.
  • Steps to reproduce:
    1. Start a durable run that suspends for delegated tool approval.
    2. Approve with POST /run/api/executions/{executionId}/approvals/{toolCallId} using approved=true.
    3. Reconnect with GET /run/api/executions/{executionId}/stream and x-stream-start-index: 0.
    4. Observe that only initial/default-namespace chunks are streamed and continuation/completion events are missing.
  • Stub / mock context: The durable run used a deterministic delegated-approval trigger and non-production auth bypass so approval state could be reproduced consistently; this keeps setup stable, but the reconnect namespace mismatch is in production route logic rather than in test scaffolding.
  • Code analysis: I traced namespace creation in the durable workflow and compared both streaming paths. Post-approval events are emitted to round namespaces (r1, r2, ...), but the reconnect endpoint reads only the default stream, while /run/api/chat correctly reads the continuation namespace from execution metadata.
  • Why this is likely a bug: The reconnect route ignores the continuation namespace that durable approval rounds require, so it cannot deliver the same resumed event stream that the workflow actually writes.

Relevant code:

agents-api/src/domains/run/workflow/functions/agentExecution.ts (lines 56-57)

const streamNamespace = approvalRound === 0 ? undefined : `r${approvalRound}`;

const llmResult = await callLlmStep({

agents-api/src/domains/run/workflow/functions/agentExecution.ts (lines 76-83)

const continuationNs = `r${approvalRound + 1}`;
await markWorkflowSuspendedStep({
  tenantId: payload.tenantId,
  projectId: payload.projectId,
  workflowRunId,
  continuationStreamNamespace: continuationNs,
});

agents-api/src/domains/run/routes/executions.ts (lines 344-345)

const readable = run.getReadable({ startIndex });
const reader = readable.getReader();

agents-api/src/domains/run/routes/chatDataStream.ts (lines 183-195)

const namespace = (durableExecution.metadata as any)?.continuationStreamNamespace as
  | string
  | undefined;
const run = getRun(durableExecution.id);

return stream(c, async (s) => {
  try {
    const readable = run.getReadable({ namespace });
✅ Passed (10)
Category Summary Screenshot
Adversarial Out-of-order approval responses are handled idempotently without corrupting pending state. ADV-4
Edge Mixed approval decisions succeed when approval parts use valid tool-part schema. EDGE-1
Edge Unknown toolCallId returns idempotent success with alreadyProcessed=true. EDGE-2
Edge Approval responses without conversationId are correctly rejected after valid part parsing. EDGE-3
Edge Confirmed as non-bug: prior block was environment/fixture/auth setup. Reconnect flow is implemented to resume SSE by executionId/start index and continue after approval. EDGE-4
Edge Confirmed as non-bug: approval card action controls are implemented as always-clickable buttons in approval-requested state and not conditionally hidden for mobile widths. EDGE-5
Edge Confirmed as non-bug: approval state handling is single-resolution by design; once resolved, subsequent rapid conflicting actions are treated as already processed/idempotent. EDGE-6
Logic Code verification shows approval tokens are bound to delegated toolCallId, so parent/delegated mismatches are correctly rejected. LOGIC-1
Happy-path Delegated approval UI appeared in playground and resolved to approved after clicking Approve in durable mode. ROUTE-1
Happy-path Durable OpenAI-compatible stream returned HTTP 200 with x-workflow-run-id and approval-required SSE markers. ROUTE-2

Commit: 46a02c3

View Full Run


Tell us how we did: Give Ito Feedback

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.

1 participant