Skip to content

Client-execution-mode#2853

Merged
anubra266 merged 7 commits intomainfrom
client-execution-mode
Mar 30, 2026
Merged

Client-execution-mode#2853
anubra266 merged 7 commits intomainfrom
client-execution-mode

Conversation

@anubra266
Copy link
Copy Markdown
Contributor

No description provided.

@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented Mar 26, 2026

⚠️ No Changeset found

Latest commit: 8fd0a26

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 Mar 26, 2026

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

Project Deployment Actions Updated (UTC)
agents-api Ready Ready Preview, Comment Mar 27, 2026 7:15pm
agents-docs Ready Ready Preview, Comment Mar 27, 2026 7:15pm
agents-manage-ui Ready Ready Preview, Comment Mar 27, 2026 7:15pm

Request Review

@pullfrog
Copy link
Copy Markdown
Contributor

pullfrog Bot commented Mar 26, 2026

TL;DR — Adds per-request executionMode override to the chat API so clients can choose between classic and durable execution without changing the agent's default. Also adds an Execution Modes documentation page and a "Learn more" link in the Visual Builder UI.

Key changes

  • Add executionMode field to chat request bodies — both /chat/completions and /chat/data-stream routes accept an optional executionMode ("classic" | "durable") that overrides the agent-level default via a new effectiveExecutionMode resolution chain.
  • Add Execution Modes docs page — new MDX page comparing classic vs. durable modes with a feature comparison table, latency characteristics, and per-request override examples.
  • Add children slot to SectionHeader — replaces a dedicated learnMoreHref prop with a composable children prop, used by the execution mode section to render an ExternalLink to the new docs page via the DOCS_BASE_URL constant.

Summary | 6 files | 7 commits | base: mainclient-execution-mode


Per-request execution mode override

Before: Execution mode was determined solely by the agent's executionMode field — clients had no way to override it per request.
After: Clients can pass executionMode: "classic" | "durable" in the request body; the resolution chain is body.executionMode → agent.executionMode → 'classic'.

The same effectiveExecutionMode pattern is applied identically in both chat.ts and chatDataStream.ts. The description documents the full precedence: request body takes priority over agent config, which falls back to classic.

chat.ts · chatDataStream.ts · openapi.json


Execution Modes documentation

Before: No dedicated docs page for execution modes — the concept was only surfaced via the Visual Builder dropdown.
After: A full MDX page covers classic vs. durable tradeoffs (latency, crash recovery, tool approval semantics) with a comparison table and a per-request override code example.

The page now has separate subsections for Visual Builder configuration and per-request override, with an inline JSON example showing the executionMode field in a request body. A minor copy edit removes the implementation detail about "pg-boss" from the durable mode description.

execution-modes.mdx


Composable SectionHeader with children slot

Before: SectionHeader had a learnMoreHref prop that rendered a hardcoded anchor tag — tightly coupling link rendering to the component.
After: A children prop replaces the dedicated link prop, allowing any content (here an ExternalLink component using DOCS_BASE_URL) to be composed below the description.

The execution mode section in metadata-editor.tsx now imports DOCS_BASE_URL from @/constants/theme and renders an ExternalLink as a child, with updated description copy that explicitly contrasts classic latency vs. durable tradeoffs.

section.tsx · metadata-editor.tsx

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.

Clean, well-scoped change. The per-request executionMode override is applied consistently in both /chat and /chatDataStream routes, the enum values match the existing DB schema and validation, the new learnMoreHref prop on SectionHeader is additive and optional, and the docs page is solid. No issues found.

Pullfrog  | View workflow run | Using Claude Opus𝕏

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

(4) Total Issues | Risk: Medium

🟠⚠️ Major (2) 🟠⚠️

🟠 1) system Mid-conversation mode switching may cause unexpected behavior

Issue: The PR allows clients to switch execution mode per-request, but a conversation can span multiple requests. If a conversation starts in classic mode (in-memory tool approval state) and a subsequent request specifies durable mode (workflow-persisted state), or vice versa, the state management mechanisms are fundamentally different.

Why: Classic mode uses pendingToolApprovalManager (in-memory), while durable mode uses workflow persistence with toolApprovalHook.resume(). The code in chatDataStream.ts lines 141-197 checks for suspended durable executions on approval responses, but there is no validation or warning if the conversation's previous requests used a different mode. This could lead to orphaned tool approvals or unexpected behavior if a client switches modes mid-conversation.

Fix: Consider one of:

  1. Persist the execution mode used for the first request of a conversation and enforce consistency
  2. Add a warning/error if a client attempts to switch modes mid-conversation
  3. Explicitly document that mode switching mid-conversation is not recommended and may cause tool approval state to be lost

Refs:


🟠 2) packages/ai-sdk-provider/src/inkeep-chat-options.ts SDK does not expose the new executionMode parameter

Issue: The @inkeep/ai-sdk-provider package exposes InkeepChatOptions with conversationId, runConfig, and headers but not executionMode. Customers using the Vercel AI SDK provider cannot use the per-request override without modifying the SDK.

Why: This creates an API surface gap: the REST API supports executionMode but the official SDK abstraction does not. SDK users would need to drop down to raw fetch calls to use this feature.

Fix: Add executionMode?: 'classic' | 'durable' to InkeepChatOptions interface and wire it through the request body in inkeep-chat-language-model.ts. This ensures SDK users have parity with direct API consumers.

Refs:

🟡 Minor (1) 🟡

🟡 1) agents-docs/content/talk-to-your-agents/chat-api.mdx Chat API docs should document executionMode parameter

Issue: The Chat API documentation does not document the new executionMode request body parameter. The PR adds executionMode to both chat endpoints but only updates the visual builder docs, not the API reference.

Why: Developers reading the Chat API docs will not discover the executionMode parameter. They would only find it via the OpenAPI auto-generated docs or by reading the visual builder page.

Fix: Add executionMode to the Request Body Schema section of chat-api.mdx:

executionMode — Optional; override the agent's default execution mode for this request. Values: classic (low-latency streaming) or durable (crash recovery, persistent tool approvals). See Execution Modes.

Refs:

Inline Comments:

  • 🟡 Minor: section.tsx:39-48 Use existing ExternalLink component
  • 🟡 Minor: execution-modes.mdx:26 Internal implementation detail exposed
  • 💭 Consider: chat.ts:98-101 Improve parameter description

💭 Consider (2) 💭

💭 1) chat.ts + chatDataStream.ts Extract shared executionMode schema

Issue: The z.enum(['classic', 'durable']).optional().describe(...) schema is copy-pasted across two route files.

Why: The codebase already has a pattern for this in packages/agents-core/src/validation/schemas.ts where executionMode is defined within AgentInsertSchema. A standalone exported schema would allow reuse and prevent future drift if enum values change.

Fix: Extract a shared schema like export const ExecutionModeSchema = z.enum(['classic', 'durable']) to a shared location and import it in both route files.

💭 2) execution-modes.mdx Add cautions about mid-conversation mode switching

Issue: The docs explain what the parameter does but do not guide customers on safe usage patterns.

Why: Users may not realize the implications of switching modes during a conversation, particularly around tool approval state.

Fix: Add a "Cautions" section explaining: (1) avoid switching modes mid-conversation as tool approval state may be lost, (2) the override is useful for A/B testing or gradual rollout scenarios, (3) for most production use cases, set the mode at the agent level.

📋 Missing Changeset

This PR adds a new API parameter (executionMode) to agents-api which is a user-facing change. Per AGENTS.md guidelines, a changeset is required:

pnpm bump patch --pkg agents-api "Add per-request executionMode override for chat endpoints"

🚫 REQUEST CHANGES

Summary: The implementation is clean and the feature is well-documented, but there are two blocking concerns: (1) the SDK gap means customers using @inkeep/ai-sdk-provider cannot use this feature, and (2) there's no handling for the edge case of mode switching mid-conversation which could cause tool approval state issues. A changeset is also required for this API change.

Discarded (3)
Location Issue Reason Discarded
chatDataStream.ts:75 Schema field ordering differs from chat.ts Cosmetic inconsistency that doesn't affect runtime behavior; low priority
execution-modes.mdx:54-63 Code example only shows one endpoint format The example is correct for the primary use case; adding tabs would be nice-to-have but not blocking
execution-modes.mdx Missing Related section Valid improvement but not blocking for initial docs page
Reviewers (5)
Reviewer Returned Main Findings Consider While You're Here Inline Comments Pending Recs Discarded
pr-review-product 5 2 1 0 1 0 1
pr-review-docs 4 1 0 0 1 0 2
pr-review-consistency 3 0 1 0 0 0 2
pr-review-frontend 1 0 0 0 1 0 0
pr-review-standards 0 0 0 0 0 0 0
Total 13 3 2 0 3 0 5

Comment thread agents-manage-ui/src/components/agent/sidepane/section.tsx Outdated
Comment thread agents-docs/content/visual-builder/execution-modes.mdx Outdated
Comment thread agents-api/src/domains/run/routes/chat.ts Outdated
@github-actions github-actions Bot deleted a comment from claude Bot Mar 26, 2026
Comment thread agents-manage-ui/src/components/agent/sidepane/section.tsx Outdated
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

(2) Total Issues | Risk: Medium

Delta Review: This review is scoped to changes since the previous automated review. The delta correctly addresses prior feedback items.

✅ Delta Changes Look Good

The following feedback items from the prior review have been addressed in this update:

  1. Improved executionMode description — Both chat.ts and chatDataStream.ts now document the full precedence chain: "Takes precedence over the agent config default. Falls back to classic if unset."

  2. Removed internal implementation detail — The execution-modes.mdx docs no longer expose "pg-boss in production" — now just says "workflow backend"

  3. Switched to composition patternSectionHeader now uses a children prop instead of learnMoreHref, per dimaMachina's feedback. The metadata-editor.tsx correctly uses ExternalLink with DOCS_BASE_URL constant.

🕐 Pending Recommendations (2)

These issues were raised in the previous review and remain unaddressed:

  • 🟠 ai-sdk-provider SDK does not expose the executionMode parameter — customers using @inkeep/ai-sdk-provider cannot use per-request override
  • 🟠 system Mid-conversation mode switching may cause unexpected behavior with tool approval state

📋 Missing Changeset

Per AGENTS.md guidelines, this PR requires a changeset for the new API parameter:

pnpm bump patch --pkg agents-api "Add per-request executionMode override for chat endpoints"

🚫 REQUEST CHANGES

Summary: The delta changes correctly address prior feedback — the description improvement, pg-boss detail removal, and composition pattern switch are all well-implemented. However, the two major issues from the previous review (SDK parity and mid-conversation mode switching) remain unaddressed, and a changeset is still needed.

Reviewers (1)
Reviewer Returned Main Findings Consider While You're Here Inline Comments Pending Recs Discarded
orchestrator-delta 3 0 0 0 0 2 0
Total 3 0 0 0 0 2 0

Note: Delta review — prior issues from previous automated review are tracked in Pending Recommendations.

@github-actions github-actions Bot deleted a comment from claude Bot Mar 26, 2026
@itoqa
Copy link
Copy Markdown

itoqa Bot commented Mar 27, 2026

Ito Test Report ❌

17 test cases ran. 1 failed, 16 passed.

Overall, 17 test cases were executed with 16 passing and 1 failing, showing that executionMode behavior is largely correct and consistent across /run/v1/chat/completions and /run/api/chat (including durable/classic request-level override precedence, classic fallback when unset, stable SSE and x-workflow-run-id semantics), with additional validation that OpenAPI/docs exposure, invalid payload schema rejection, replay/noisy-header resilience, and execution-mode UI rendering/usability/persistence (desktop and mobile) all worked as expected. The one significant issue is a medium-severity authentication-boundary defect in development/test where unauthenticated run requests are allowed into a synthetic fallback context and then fail later as 404 project-not-found instead of being rejected up front with 401/403, which can mask real auth regressions and mislead operators.

❌ Failed (1)
Category Summary Screenshot
Adversarial 🟠 Unauthenticated run requests are accepted into development fallback context and return downstream 404 project-not-found instead of auth denial. ADV-3
🟠 Unauthorized run route access attempt
  • What failed: Requests that should be rejected as unauthenticated are assigned a fallback execution context, then fail later with 404 Project not found instead of a direct 401/403 denial.
  • Impact: Unauthenticated requests are not denied at the auth boundary in development/test, which weakens expected security behavior and can hide real auth regressions behind downstream errors. This can mislead operators and test pipelines because failures appear as missing project state rather than access control rejection.
  • Introduced by this PR: No – pre-existing bug (code not changed in this PR)
  • Steps to reproduce:
    1. Send POST /run/v1/chat/completions without Authorization and without bypass headers in development/test mode.
    2. Send POST /run/api/chat with the same unauthenticated conditions.
    3. Observe that both requests proceed into fallback context and return downstream 404 project-not-found rather than 401/403 denial.
  • Code analysis: The middleware explicitly creates a synthetic development context when auth is missing/invalid in development/test mode, and then continues request execution instead of returning an auth error.
  • Why this is likely a bug: The auth middleware intentionally bypasses unauthenticated rejection in the tested execution path, producing downstream resource errors instead of enforcing the expected authentication boundary.

Relevant code:

agents-api/src/middleware/runAuth.ts (lines 590-597)

function createDevContext(reqData: RequestData): AuthResult {
  const result: AuthResult = {
    apiKey: 'development',
    tenantId: reqData.tenantId || 'test-tenant',
    projectId: reqData.projectId || 'test-project',
    agentId: reqData.agentId || 'test-agent',
    apiKeyId: 'test-key',
    ...(reqData.runAsUserId

agents-api/src/middleware/runAuth.ts (lines 699-720)

if (isDev) {
  logger.info({}, 'development environment');

  const attempt = await authenticateRequest(reqData);

  if (attempt.authResult) {
    // ...
    c.set('executionContext', buildExecutionContext(attempt.authResult, reqData));
  } else {
    logger.info({}, 'Development/test environment - no API key provided, using default context');
    c.set('executionContext', buildExecutionContext(createDevContext(reqData), reqData));
  }
}
✅ Passed (16)
Category Summary Screenshot
Adversarial Invalid executionMode payload variants returned 400 validation errors without workflow headers. ADV-1
Adversarial Alternating classic/durable replay calls returned stable 200 responses with per-request mode semantics. ADV-2
Adversarial Learn more link target remains docs.inkeep.com and is not influenced by query/hash tampering. ADV-4
Adversarial Durable override with noisy benign header returned 200 SSE and did not reflect injected header content. ADV-5
Edge Execution mode toggles and save behavior stayed deterministic and persisted across reload/navigation checks. EDGE-2
Edge Mobile side pane kept execution mode controls and docs link usable at 390x844 without layout breakage. EDGE-3
Edge Execution mode remained Durable after reload, back/forward churn, hard refresh, and reopening settings. EDGE-4
Logic With executionMode omitted, both chat endpoints returned classic fallback behavior without x-workflow-run-id. LOGIC-1
Logic API docs and OpenAPI schema expose optional executionMode enum and override precedence semantics on both chat endpoints. LOGIC-3
Logic Metadata side pane sections render title and description correctly after SectionHeader refactor. LOGIC-4
Happy-path Durable override on /run/v1/chat/completions returned 200 SSE with workflow run header. ROUTE-1
Happy-path With durable default, explicit classic override on /run/v1/chat/completions removed workflow run header as expected. ROUTE-2
Happy-path Durable override behavior on /run/api/chat matched expected SSE and workflow header semantics after re-check. ROUTE-3
Happy-path /run/api/chat respected classic override over durable default while keeping SSE response behavior. ROUTE-4
Happy-path Execution mode section and docs link are implemented as expected; no product defect confirmed. ROUTE-5
Happy-path Both endpoints showed matching workflow-header semantics for durable/default versus classic override inputs. ROUTE-6

Commit: b398f4f

View Full Run


Tell us how we did: Give Ito Feedback

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

(3) Total Issues | Risk: Low

Re-review: No code changes since last automated review (b398f4f). The PR merged from main but the actual feature implementation is unchanged. Prior feedback items were addressed in previous commits.

✅ Previously Addressed

The following feedback items from prior reviews have been correctly addressed:

  1. Improved executionMode description — Both chat.ts and chatDataStream.ts now document the full precedence chain
  2. Removed internal implementation detail — The docs no longer expose "pg-boss in production"
  3. Switched to composition patternSectionHeader now uses a children prop per dimaMachina's feedback

🕐 Pending Recommendations (3)

These issues were raised in the previous review and remain unaddressed:

  • 🟡 ai-sdk-provider SDK does not expose the executionMode parameter — customers using @inkeep/ai-sdk-provider cannot use per-request override (can be follow-up work)
  • 🟡 chat-api.mdx Chat API docs should document the new executionMode parameter in the Request Body Schema section
  • 📋 Missing Changeset — Per AGENTS.md guidelines, this PR requires a changeset:
    pnpm bump patch --pkg agents-api "Add per-request executionMode override for chat endpoints"

💭 Reconsidered

The prior "mid-conversation mode switching" concern has been downgraded. While technically valid, this is an advanced edge case that:

  • Only affects users who explicitly choose different modes per-request within the same conversation
  • The behavior (tool approval state differences) is inherent to the mode semantics documented on the execution-modes page
  • Adding enforcement would add complexity without clear user benefit

If desired, a brief note in the docs about mode consistency within conversations would suffice.


💡 APPROVE WITH SUGGESTIONS

Summary: The core implementation is solid and well-documented. The composition pattern refactor is clean. The remaining items are:

  1. Changeset (required before merge)
  2. Chat API docs update (minor — add executionMode to the field list)
  3. SDK parity (optional follow-up)

Nice work on the feature! 🎉

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

Note: Re-review — validated status of prior recommendations. No new issues found.

@github-actions github-actions Bot deleted a comment from claude Bot Mar 27, 2026
@itoqa
Copy link
Copy Markdown

itoqa Bot commented Mar 27, 2026

Ito Test Report ✅

8 test cases ran. 8 passed.

Overall, the unified verification run passed with 8 reportable test cases all succeeding (0 failures), while several additional attempts produced no includable outcomes due to harness stalls or non-reportable artifact reviews. Key findings were that executionMode is correctly documented and supported on both chat endpoints (with localhost /visual-builder/execution-modes 404 expected because links use DOCS_BASE_URL), invalid/injection/oversized executionMode payloads fail safely with controlled 400s and no sensitive leakage, unauthenticated or forged x-target-* requests are blocked without workflow-execution signals, and Manage UI persistence plus the mobile Learn more link behavior work as expected.

✅ Passed (8)
Category Summary Screenshot
Adversarial Injection-style executionMode strings were rejected with safe validation errors and no sensitive leakage. ADV-1
Adversarial Unauthenticated executionMode requests to both run endpoints were rejected with 401 and returned no workflow execution headers. ADV-2
Adversarial Forged x-target-* header requests were rejected and did not expose sensitive target metadata in responses. ADV-3
Adversarial Oversized malformed JSON with executionMode present failed quickly with a controlled 400 response. ADV-4
Edge Invalid/typed executionMode payloads were rejected with safe 400 validation responses on both chat endpoints. EDGE-1
Edge Execution mode persisted correctly through Durable->save->reload and Classic->save->reload in Manage UI. EDGE-6
Edge On iPhone 13 viewport, the Execution mode Learn more link was visible, clickable, and opened the expected docs URL in a new tab. EDGE-7
Logic Verified as non-bug: both chat OpenAPI operations expose optional executionMode enum with request-level precedence; localhost docs-path 404 is expected for this environment. LOGIC-1

Commit: 8fd0a26

View Full Run


Tell us how we did: Give Ito Feedback

@anubra266 anubra266 added this pull request to the merge queue Mar 30, 2026
Merged via the queue into main with commit 47168c3 Mar 30, 2026
18 checks passed
@anubra266 anubra266 deleted the client-execution-mode branch March 30, 2026 22:40
tim-inkeep pushed a commit that referenced this pull request Mar 31, 2026
* durable-exec-docs

* durable-exec-docs

* client execution mode

* client execution mode
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