feat(workflow-executor): add TriggerActionStepExecutor with confirmation flow#1501
Open
Scra3 wants to merge 23 commits intofeat/prd-219-update-record-step-executorfrom
Open
feat(workflow-executor): add TriggerActionStepExecutor with confirmation flow#1501Scra3 wants to merge 23 commits intofeat/prd-219-update-record-step-executorfrom
Scra3 wants to merge 23 commits intofeat/prd-219-update-record-step-executorfrom
Conversation
…ion flow
Implements TriggerActionStepExecutor following the UpdateRecordStepExecutor
pattern (branches A/B/C, confirmation flow, automaticExecution).
- Add TriggerActionStepExecutionData type with executionParams (actionDisplayName
+ actionName), executionResult ({ success } | { skipped }), and pendingAction
- Add NoActionsError for collections with no actions
- Implement selectAction via AI tool with displayName enum and technical name hints
- resolveAndExecute stores the technical actionName in executionParams for
traceability; action result discarded per privacy constraint
- Fix buildStepSummary in BaseStepExecutor to include trigger-action pendingAction
in prior-step AI context (parity with update-record pendingUpdate)
- Export TriggerActionStepExecutor, TriggerActionStepExecutionData, NoActionsError
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
ac706e8 to
c884d37
Compare
…t and pendingAction Resolve actionName once in handleFirstCall and store it in pendingAction, so resolveAndExecute receives it directly via ActionTarget without re-fetching the schema. Rename TriggerTarget → ActionTarget for consistency with English naming conventions. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
6 new issues
|
…f shapes to { name, displayName }
- Extract ActionRef { name, displayName } from inline types in TriggerActionStepExecutionData
- Extract FieldRef { name, displayName } replacing FieldReadBase in ReadRecord types
- UpdateRecordStepExecutionData executionParams/pendingUpdate now use FieldRef & { value }
- Rename actionDisplayName/actionName → displayName/name, fieldDisplayName/fieldName → displayName/name
- Move resolveFieldName to handleFirstCall (no re-resolution in resolveAndUpdate)
- Add missing tests: resolveActionName not-found path, saveStepExecution not-called assertions, trigger-action pendingAction in buildStepSummary
- Export ActionRef, FieldRef, NoActionsError from index; update CLAUDE.md
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…store display names in executionParams Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…rams for consistency Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…-executor Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…y" wording Use fieldNames instead of fieldDisplayNames and actionName instead of displayName in tool schemas exposed to the AI, to avoid confusion between property names and their semantics (values remain display names). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ize pending state - Make buildOutcomeResult abstract on BaseStepExecutor; each branch owns its outcome shape - Add RecordTaskStepExecutor intermediate class implementing buildOutcomeResult for 'record-task' - Add pendingData to BaseStepExecutionData, replacing pendingUpdate/pendingAction per-type fields; each executor sets it when saving, base class reads it directly with no type discrimination - Rename TriggerActionStepExecutor → TriggerRecordActionStepExecutor for naming consistency with ReadRecord/UpdateRecord Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…idate error handling - Introduce doExecute() as the abstract hook; execute() in the base class wraps it with the single try-catch that converts WorkflowExecutorErrors to error outcomes and rethrows infrastructure errors — removing all try-catch boilerplate from subclasses - Inline the former buildErrorOutcomeOrThrow helper directly into execute(), it no longer needs to be a named method - Add StepPersistenceError (extends WorkflowExecutorError) for post-side-effect persistence failures; these are now consistently converted to error outcomes - Extract handleConfirmationFlow into RecordTaskStepExecutor to DRY the confirmation pattern shared by update-record and trigger-record-action - Split the !execution?.pendingData guard into two distinct WorkflowExecutorErrors for clearer debug messages - Export BaseStepStatus from step-outcome; remove pendingData from BaseStepExecutionData (declared only on the concrete types that need it) - Fix extractToolCallArgs to throw MalformedToolCallError when args is null/undefined Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…error hierarchy, and AI-first Branch C - Add LoadRelatedRecordStepExecutor with Branch A/B/C confirmation flow - Replace candidates[] in LoadRelatedRecordPendingData with AI-selected suggestedRecordId/suggestedFields/relatedCollectionName - Extract selectBestFromRelatedData to share AI selection logic between Branch B and Branch C - Make WorkflowExecutorError abstract; introduce InvalidAIResponseError, RelationNotFoundError, FieldNotFoundError, ActionNotFoundError, StepStateError - Fix selectRelevantFields to use displayName in Zod enum and map back to fieldName - Add getRelatedData fields param forwarding in AgentClientAgentPort - Update CLAUDE.md with displayName-in-AI-tools and error hierarchy rules Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ser facing messages Separates technical error messages (dev logs) from user-facing messages (Forest Admin UI). WorkflowExecutorError now carries a readonly `userMessage` field; base-step-executor uses it instead of `message` when building the step outcome error. Each subclass declares its own user-oriented message. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
packages/workflow-executor/test/executors/load-related-record-step-executor.test.ts
Show resolved
Hide resolved
…nject logger into execution context Introduces a Logger interface (port) and ConsoleLogger (default implementation). Adds logger to ExecutionContext, masks raw error messages from the HTTP API (security), and logs unhandled HTTP errors with context instead. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…eLogger output Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ated-record executor, rename ids→id
- Add StepExecutionFormatters (static per-type formatters) and StepSummaryBuilder
(orchestrates step summary for AI context); decouple formatting from BaseStepExecutor
- Load-related-record executionResult is now self-contained: { relation: RelationRef; record: RecordRef }
- Rename ids → id in AgentPort and all callers (id is a composite key of one record, not multiple records)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ve summary to sub-folder - Replace QueryBase intersections with named query types (GetRecordQuery, UpdateRecordQuery, GetRelatedDataQuery, ExecuteActionQuery) for better DX and readability - Save executeAction return value in executionResult.actionResult instead of discarding it - Move StepSummaryBuilder and StepExecutionFormatters to executors/summary/ sub-folder Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…edData signature Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… into McpTaskStepExecutor remoteTools was a dependency of McpTaskStepExecutor only — 5 of 6 executors never used it. Inject it explicitly via the constructor so ExecutionContext stays focused on execution state, and remove the now-meaningless remoteTools: [] boilerplate from every other executor test. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…utor Any WorkflowExecutorError with a cause is now logged automatically by the base executor using error.message as the log message. Removes the manual logger.error call from McpTaskStepExecutor and the StepPersistenceError- specific guard. Moves cause? up to the base class, removing duplicate declarations from StepPersistenceError and McpToolInvocationError. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add AgentPortError and SafeAgentPort to centralise infra error handling for all agentPort operations (getRecord, updateRecord, getRelatedData, executeAction). - add AgentPortError extending WorkflowExecutorError with a user-friendly message and structured cause logging - add SafeAgentPort implementing AgentPort: wraps infra errors in AgentPortError, passes through WorkflowExecutorError subclasses unchanged - expose this.agentPort (SafeAgentPort) as a protected property in BaseStepExecutor; all executors use it instead of this.context.agentPort - export AgentPortError from index.ts - add unit tests for SafeAgentPort and one integration test per executor verifying userMessage and logger.error cause on infra failures Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ask dependencies Add McpToolRef, McpToolCall and McpTaskStepExecutionData to step-execution-data, required by mcp-task-step-executor. Update package.json to depend on @forestadmin/ai-proxy and align @langchain/core version. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Export RemoteTool default, BaseChatModel, BaseMessage, HumanMessage, SystemMessage, StructuredToolInterface and DynamicStructuredTool so consumers of ai-proxy do not need a direct @langchain/core dependency. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…restadmin/ai-proxy Replace all direct @langchain/core imports in src and tests with @forestadmin/ai-proxy re-exports. Add moduleNameMapper in jest.config.ts to resolve @anthropic-ai/sdk wildcard exports (Jest < 30 workaround, same pattern as ai-proxy). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@langchain/core is now consumed transitively via @forestadmin/ai-proxy. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Summary
TriggerActionStepExecutorfollowing theUpdateRecordStepExecutorpattern (branches A/B/C,automaticExecution, confirmation flow)select-actiontool (Zod enum of display names + technical names as hints);resolveActionNamemaps displayName → technical name with name fallbackTriggerActionStepExecutionDatawithexecutionParams: { actionDisplayName, actionName },executionResult: { success } | { skipped }, andpendingActionfor the awaiting-confirmation stateNoActionsErrorfor collections with no actionsbuildStepSummaryinBaseStepExecutorto includetrigger-actionpendingActionin prior-step AI context (parity withupdate-recordpendingUpdate)Test plan
automaticExecution: true) — triggers action, savesexecutionParamswith both display and technical name, returnssuccessautomaticExecution) — savespendingAction, returnsawaiting-inputpendingActionfor traceability{ skipped: true }NoActionsError— returnserroroutcomeWorkflowExecutorErrorfromexecuteAction→erroroutcome (branches A & B)resolveActionNamefailure (Branch A — action deleted between confirmation steps)erroroutcomegetCollectionSchemacall per collection)stepOutcomeshape🤖 Generated with Claude Code
Note
Add TriggerRecordActionStepExecutor with confirmation flow to workflow-executor
TriggerRecordActionStepExecutorfor a newtrigger-actionstep type that selects a record, picks an action via AI, and either executes immediately or persistspendingDatafor user confirmation before executing.LoadRelatedRecordStepExecutorandMcpTaskStepExecutorwith the same automatic/confirmation branching pattern.RecordTaskStepExecutoras a shared abstract base that standardizes confirmation flow, skipped-state persistence, andbuildOutcomeResultfor record-task steps.BaseStepExecutor: the new concreteexecute()wrapsdoExecute(), catchesWorkflowExecutorErrorto log with cause and return an error outcome, and converts unexpected throws into a generic error outcome.SafeAgentPortto wrap allAgentPortcalls and normalize non-domain errors intoAgentPortError; all executors now use this wrapper.errors.tswith ~15 new typed errors (e.g.StepPersistenceError,StepStateError,AgentPortError,McpToolInvocationError) each carrying a separateuserMessagefor end-user display.AgentPortmethods (getRecord,updateRecord,getRelatedData,executeAction) now accept typed query objects instead of positional arguments; all callers and tests updated accordingly.Macroscope summarized 369a398.