@forestadmin/workflow-executor 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (123) hide show
  1. package/LICENSE +674 -0
  2. package/README.md +141 -0
  3. package/dist/adapters/activity-log-drainer.d.ts +6 -0
  4. package/dist/adapters/activity-log-drainer.js +21 -0
  5. package/dist/adapters/agent-client-agent-port.d.ts +27 -0
  6. package/dist/adapters/agent-client-agent-port.js +211 -0
  7. package/dist/adapters/ai-client-adapter.d.ts +11 -0
  8. package/dist/adapters/ai-client-adapter.js +38 -0
  9. package/dist/adapters/always-error-ai-model-port.d.ts +8 -0
  10. package/dist/adapters/always-error-ai-model-port.js +23 -0
  11. package/dist/adapters/console-logger.d.ts +7 -0
  12. package/dist/adapters/console-logger.js +15 -0
  13. package/dist/adapters/forest-server-workflow-port.d.ts +25 -0
  14. package/dist/adapters/forest-server-workflow-port.js +163 -0
  15. package/dist/adapters/forestadmin-client-activity-log-port-factory.d.ts +12 -0
  16. package/dist/adapters/forestadmin-client-activity-log-port-factory.js +22 -0
  17. package/dist/adapters/forestadmin-client-activity-log-port.d.ts +15 -0
  18. package/dist/adapters/forestadmin-client-activity-log-port.js +78 -0
  19. package/dist/adapters/pretty-logger.d.ts +9 -0
  20. package/dist/adapters/pretty-logger.js +37 -0
  21. package/dist/adapters/record-id-serializer.d.ts +4 -0
  22. package/dist/adapters/record-id-serializer.js +20 -0
  23. package/dist/adapters/run-to-available-step-mapper.d.ts +4 -0
  24. package/dist/adapters/run-to-available-step-mapper.js +137 -0
  25. package/dist/adapters/server-ai-adapter.d.ts +16 -0
  26. package/dist/adapters/server-ai-adapter.js +60 -0
  27. package/dist/adapters/server-types.d.ts +181 -0
  28. package/dist/adapters/server-types.js +35 -0
  29. package/dist/adapters/step-definition-mapper.d.ts +4 -0
  30. package/dist/adapters/step-definition-mapper.js +68 -0
  31. package/dist/adapters/step-outcome-to-update-step-mapper.d.ts +4 -0
  32. package/dist/adapters/step-outcome-to-update-step-mapper.js +34 -0
  33. package/dist/adapters/with-retry.d.ts +6 -0
  34. package/dist/adapters/with-retry.js +40 -0
  35. package/dist/build-workflow-executor.d.ts +35 -0
  36. package/dist/build-workflow-executor.js +175 -0
  37. package/dist/cli-core.d.ts +26 -0
  38. package/dist/cli-core.js +228 -0
  39. package/dist/cli.d.ts +3 -0
  40. package/dist/cli.js +17 -0
  41. package/dist/defaults.d.ts +9 -0
  42. package/dist/defaults.js +12 -0
  43. package/dist/errors.d.ts +153 -0
  44. package/dist/errors.js +327 -0
  45. package/dist/executors/activity-log.d.ts +14 -0
  46. package/dist/executors/activity-log.js +33 -0
  47. package/dist/executors/agent-with-log.d.ts +33 -0
  48. package/dist/executors/agent-with-log.js +76 -0
  49. package/dist/executors/base-step-executor.d.ts +40 -0
  50. package/dist/executors/base-step-executor.js +267 -0
  51. package/dist/executors/condition-step-executor.d.ts +15 -0
  52. package/dist/executors/condition-step-executor.js +108 -0
  53. package/dist/executors/guidance-step-executor.d.ts +12 -0
  54. package/dist/executors/guidance-step-executor.js +39 -0
  55. package/dist/executors/load-related-record-step-executor.d.ts +38 -0
  56. package/dist/executors/load-related-record-step-executor.js +478 -0
  57. package/dist/executors/mcp-step-executor.d.ts +22 -0
  58. package/dist/executors/mcp-step-executor.js +188 -0
  59. package/dist/executors/read-record-step-executor.d.ts +10 -0
  60. package/dist/executors/read-record-step-executor.js +100 -0
  61. package/dist/executors/record-step-executor.d.ts +19 -0
  62. package/dist/executors/record-step-executor.js +108 -0
  63. package/dist/executors/step-executor-factory.d.ts +24 -0
  64. package/dist/executors/step-executor-factory.js +99 -0
  65. package/dist/executors/summary/step-execution-formatters.d.ts +8 -0
  66. package/dist/executors/summary/step-execution-formatters.js +49 -0
  67. package/dist/executors/summary/step-summary-builder.d.ts +7 -0
  68. package/dist/executors/summary/step-summary-builder.js +52 -0
  69. package/dist/executors/trigger-record-action-step-executor.d.ts +17 -0
  70. package/dist/executors/trigger-record-action-step-executor.js +169 -0
  71. package/dist/executors/update-record-step-executor.d.ts +13 -0
  72. package/dist/executors/update-record-step-executor.js +245 -0
  73. package/dist/http/executor-http-server.d.ts +25 -0
  74. package/dist/http/executor-http-server.js +170 -0
  75. package/dist/http/pending-data-validators.d.ts +25 -0
  76. package/dist/http/pending-data-validators.js +79 -0
  77. package/dist/http/step-serializer.d.ts +3 -0
  78. package/dist/http/step-serializer.js +47 -0
  79. package/dist/in-flight-run-registry.d.ts +9 -0
  80. package/dist/in-flight-run-registry.js +30 -0
  81. package/dist/index.d.ts +38 -0
  82. package/dist/index.js +88 -0
  83. package/dist/ports/activity-log-port.d.ts +24 -0
  84. package/dist/ports/activity-log-port.js +3 -0
  85. package/dist/ports/agent-port.d.ts +54 -0
  86. package/dist/ports/agent-port.js +3 -0
  87. package/dist/ports/ai-model-port.d.ts +7 -0
  88. package/dist/ports/ai-model-port.js +3 -0
  89. package/dist/ports/logger-port.d.ts +6 -0
  90. package/dist/ports/logger-port.js +3 -0
  91. package/dist/ports/run-store.d.ts +9 -0
  92. package/dist/ports/run-store.js +3 -0
  93. package/dist/ports/workflow-port.d.ts +30 -0
  94. package/dist/ports/workflow-port.js +3 -0
  95. package/dist/remote-tool-fetcher.d.ts +19 -0
  96. package/dist/remote-tool-fetcher.js +56 -0
  97. package/dist/runner.d.ts +50 -0
  98. package/dist/runner.js +317 -0
  99. package/dist/schema-cache.d.ts +11 -0
  100. package/dist/schema-cache.js +37 -0
  101. package/dist/schema-resolver.d.ts +11 -0
  102. package/dist/schema-resolver.js +24 -0
  103. package/dist/stores/build-run-store.d.ts +5 -0
  104. package/dist/stores/build-run-store.js +28 -0
  105. package/dist/stores/database-store.d.ts +17 -0
  106. package/dist/stores/database-store.js +119 -0
  107. package/dist/stores/in-memory-store.d.ts +11 -0
  108. package/dist/stores/in-memory-store.js +48 -0
  109. package/dist/types/execution-context.d.ts +37 -0
  110. package/dist/types/execution-context.js +3 -0
  111. package/dist/types/step-execution-data.d.ts +137 -0
  112. package/dist/types/step-execution-data.js +3 -0
  113. package/dist/types/validated/collection.d.ts +126 -0
  114. package/dist/types/validated/collection.js +96 -0
  115. package/dist/types/validated/execution.d.ts +362 -0
  116. package/dist/types/validated/execution.js +43 -0
  117. package/dist/types/validated/step-definition.d.ts +243 -0
  118. package/dist/types/validated/step-definition.js +128 -0
  119. package/dist/types/validated/step-outcome.d.ts +108 -0
  120. package/dist/types/validated/step-outcome.js +66 -0
  121. package/dist/validate-secrets.d.ts +5 -0
  122. package/dist/validate-secrets.js +14 -0
  123. package/package.json +50 -0
@@ -0,0 +1,188 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const ai_proxy_1 = require("@forestadmin/ai-proxy");
7
+ const zod_1 = require("zod");
8
+ const errors_1 = require("../errors");
9
+ const base_step_executor_1 = __importDefault(require("./base-step-executor"));
10
+ const step_definition_1 = require("../types/validated/step-definition");
11
+ const MCP_TASK_SYSTEM_PROMPT = `You are an AI agent selecting and executing a tool to fulfill a user request.
12
+ Select the most appropriate tool and fill in its parameters precisely.
13
+
14
+ Important rules:
15
+ - Select only the tool directly relevant to the request.
16
+ - Final answer is definitive, you won't receive any other input from the user.`;
17
+ class McpStepExecutor extends base_step_executor_1.default {
18
+ constructor(context, remoteTools, mcpServerName) {
19
+ super(context);
20
+ this.remoteTools = remoteTools;
21
+ this.mcpServerName = mcpServerName;
22
+ }
23
+ getExtraLogContext() {
24
+ return {
25
+ mcpServerId: this.context.stepDefinition.mcpServerId,
26
+ mcpServerName: this.mcpServerName,
27
+ };
28
+ }
29
+ buildOutcomeResult(outcome) {
30
+ return {
31
+ stepOutcome: {
32
+ type: 'mcp',
33
+ stepId: this.context.stepId,
34
+ stepIndex: this.context.stepIndex,
35
+ ...outcome,
36
+ },
37
+ };
38
+ }
39
+ async checkIdempotency() {
40
+ const existing = await this.findPendingExecution('mcp');
41
+ if (existing?.idempotencyPhase === 'done') {
42
+ return this.buildOutcomeResult({ status: 'success' });
43
+ }
44
+ if (existing?.idempotencyPhase === 'executing') {
45
+ throw new errors_1.StepStateError('Step execution was interrupted. Please retry the step manually.');
46
+ }
47
+ return null;
48
+ }
49
+ async doExecute() {
50
+ // Branch A -- Re-entry after pending execution found in RunStore
51
+ const pending = await this.patchAndReloadPendingData(this.context.incomingPendingData);
52
+ if (pending) {
53
+ return this.handleConfirmationFlow(pending, execution => this.executeToolAndPersist(execution.pendingData, execution));
54
+ }
55
+ // Branches B & C -- First call
56
+ const tools = this.requireTools();
57
+ const { toolName, args } = await this.selectTool(tools);
58
+ const selectedTool = tools.find(t => t.base.name === toolName);
59
+ if (!selectedTool)
60
+ throw new errors_1.McpToolNotFoundError(toolName);
61
+ const target = { name: toolName, sourceId: selectedTool.sourceId, input: args };
62
+ if (this.context.stepDefinition.executionType === step_definition_1.StepExecutionMode.FullyAutomated) {
63
+ // Branch B -- direct execution
64
+ return this.executeToolAndPersist(target);
65
+ }
66
+ // Branch C -- Awaiting confirmation
67
+ await this.context.runStore.saveStepExecution(this.context.runId, {
68
+ type: 'mcp',
69
+ stepIndex: this.context.stepIndex,
70
+ pendingData: target,
71
+ });
72
+ return this.buildOutcomeResult({ status: 'awaiting-input' });
73
+ }
74
+ async executeToolAndPersist(target, existingExecution) {
75
+ const tools = this.requireTools();
76
+ const tool = tools.find(t => t.base.name === target.name && t.sourceId === target.sourceId);
77
+ if (!tool)
78
+ throw new errors_1.McpToolNotFoundError(target.name);
79
+ const toolResult = await this.context.activityLog.track({
80
+ action: 'action',
81
+ type: 'write',
82
+ label: this.context.stepDefinition.mcpServerId,
83
+ collectionId: this.context.collectionId,
84
+ recordId: this.context.baseRecordRef.recordId,
85
+ }, {
86
+ operation: async () => {
87
+ try {
88
+ return await tool.base.invoke(target.input);
89
+ }
90
+ catch (cause) {
91
+ throw new errors_1.McpToolInvocationError(target.name, cause);
92
+ }
93
+ },
94
+ beforeCall: () => this.context.runStore.saveStepExecution(this.context.runId, {
95
+ ...existingExecution,
96
+ type: 'mcp',
97
+ stepIndex: this.context.stepIndex,
98
+ idempotencyPhase: 'executing',
99
+ }),
100
+ });
101
+ // 1. Persist raw result immediately — safe state before any further network calls
102
+ const baseExecutionResult = { success: true, toolResult };
103
+ const baseData = {
104
+ ...existingExecution,
105
+ type: 'mcp',
106
+ stepIndex: this.context.stepIndex,
107
+ executionParams: { name: target.name, sourceId: target.sourceId, input: target.input },
108
+ executionResult: baseExecutionResult,
109
+ idempotencyPhase: 'done',
110
+ };
111
+ await this.context.runStore.saveStepExecution(this.context.runId, baseData);
112
+ // 2. AI formatting — non-blocking; errors are logged but do not fail the step
113
+ let formattedResponse = null;
114
+ try {
115
+ formattedResponse = await this.formatToolResult(target, toolResult);
116
+ }
117
+ catch (cause) {
118
+ this.context.logger.error('Failed to format MCP tool result, persisting raw result without summary', {
119
+ runId: this.context.runId,
120
+ stepIndex: this.context.stepIndex,
121
+ toolName: target.name,
122
+ cause: cause instanceof Error ? cause.message : String(cause),
123
+ });
124
+ }
125
+ if (formattedResponse) {
126
+ try {
127
+ await this.context.runStore.saveStepExecution(this.context.runId, {
128
+ ...baseData,
129
+ executionResult: { ...baseExecutionResult, formattedResponse },
130
+ });
131
+ }
132
+ catch (cause) {
133
+ this.context.logger.error('MCP tool result formatted but enriched state could not be persisted', {
134
+ runId: this.context.runId,
135
+ stepIndex: this.context.stepIndex,
136
+ toolName: target.name,
137
+ cause: cause instanceof Error ? cause.message : String(cause),
138
+ });
139
+ }
140
+ }
141
+ return this.buildOutcomeResult({ status: 'success' });
142
+ }
143
+ async formatToolResult(tool, toolResult) {
144
+ if (toolResult === null || toolResult === undefined)
145
+ return null;
146
+ const resultStr = typeof toolResult === 'string' ? toolResult : JSON.stringify(toolResult);
147
+ const truncatedResult = resultStr.length > 20000 ? `${resultStr.slice(0, 20000)}\n... [truncated]` : resultStr;
148
+ const summaryTool = new ai_proxy_1.DynamicStructuredTool({
149
+ name: 'summarize-result',
150
+ description: 'Provides a human-readable summary of the tool execution result.',
151
+ schema: zod_1.z.object({
152
+ summary: zod_1.z.string().min(1).describe('Concise human-readable summary of the tool result.'),
153
+ }),
154
+ func: undefined,
155
+ });
156
+ const messages = [
157
+ this.buildContextMessage(),
158
+ new ai_proxy_1.SystemMessage('You are summarizing the result of a workflow tool execution for the end user. ' +
159
+ 'Be concise and factual. Do not include raw JSON or technical identifiers.'),
160
+ new ai_proxy_1.HumanMessage(`Tool "${tool.name}" was executed with input: ${JSON.stringify(tool.input)}.\n` +
161
+ `Result: ${truncatedResult}\n\n` +
162
+ `Provide a concise human-readable summary.`),
163
+ ];
164
+ const { summary } = await this.invokeWithTool(messages, summaryTool);
165
+ return summary || null;
166
+ }
167
+ async selectTool(tools) {
168
+ const messages = [
169
+ this.buildContextMessage(),
170
+ ...(await this.buildPreviousStepsMessages()),
171
+ new ai_proxy_1.SystemMessage(MCP_TASK_SYSTEM_PROMPT),
172
+ new ai_proxy_1.HumanMessage(`**Request**: ${this.context.stepDefinition.prompt ?? 'Execute the relevant tool.'}`),
173
+ ];
174
+ return this.invokeWithTools(messages, tools.map(t => t.base));
175
+ }
176
+ // Tools are pre-scoped to step.mcpServerId upstream. An empty list means either no config
177
+ // matched, or the per-server connection failed at load time (McpClient swallows per-server
178
+ // errors). RemoteToolFetcher emits the diagnostic upstream; here we just surface the empty
179
+ // case as a domain error so BaseStepExecutor turns it into a step outcome.
180
+ requireTools() {
181
+ if (this.remoteTools.length === 0) {
182
+ throw new errors_1.NoMcpToolsError(this.context.stepDefinition.mcpServerId);
183
+ }
184
+ return [...this.remoteTools];
185
+ }
186
+ }
187
+ exports.default = McpStepExecutor;
188
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWNwLXN0ZXAtZXhlY3V0b3IuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvZXhlY3V0b3JzL21jcC1zdGVwLWV4ZWN1dG9yLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7O0FBTUEsb0RBQTJGO0FBQzNGLDZCQUF3QjtBQUV4QixzQ0FLbUI7QUFDbkIsOEVBQW9EO0FBQ3BELHdFQUF1RTtBQUV2RSxNQUFNLHNCQUFzQixHQUFHOzs7OzsrRUFLZ0QsQ0FBQztBQUVoRixNQUFxQixlQUFnQixTQUFRLDRCQUFtQztJQUs5RSxZQUNFLE9BQTRDLEVBQzVDLFdBQWtDLEVBQ2xDLGFBQXNCO1FBRXRCLEtBQUssQ0FBQyxPQUFPLENBQUMsQ0FBQztRQUNmLElBQUksQ0FBQyxXQUFXLEdBQUcsV0FBVyxDQUFDO1FBQy9CLElBQUksQ0FBQyxhQUFhLEdBQUcsYUFBYSxDQUFDO0lBQ3JDLENBQUM7SUFFa0Isa0JBQWtCO1FBQ25DLE9BQU87WUFDTCxXQUFXLEVBQUUsSUFBSSxDQUFDLE9BQU8sQ0FBQyxjQUFjLENBQUMsV0FBVztZQUNwRCxhQUFhLEVBQUUsSUFBSSxDQUFDLGFBQWE7U0FDbEMsQ0FBQztJQUNKLENBQUM7SUFFUyxrQkFBa0IsQ0FBQyxPQUc1QjtRQUNDLE9BQU87WUFDTCxXQUFXLEVBQUU7Z0JBQ1gsSUFBSSxFQUFFLEtBQUs7Z0JBQ1gsTUFBTSxFQUFFLElBQUksQ0FBQyxPQUFPLENBQUMsTUFBTTtnQkFDM0IsU0FBUyxFQUFFLElBQUksQ0FBQyxPQUFPLENBQUMsU0FBUztnQkFDakMsR0FBRyxPQUFPO2FBQ1g7U0FDRixDQUFDO0lBQ0osQ0FBQztJQUVrQixLQUFLLENBQUMsZ0JBQWdCO1FBQ3ZDLE1BQU0sUUFBUSxHQUFHLE1BQU0sSUFBSSxDQUFDLG9CQUFvQixDQUF1QixLQUFLLENBQUMsQ0FBQztRQUU5RSxJQUFJLFFBQVEsRUFBRSxnQkFBZ0IsS0FBSyxNQUFNLEVBQUUsQ0FBQztZQUMxQyxPQUFPLElBQUks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
@@ -0,0 +1,10 @@
1
+ import type { StepExecutionResult } from '../types/execution-context';
2
+ import type { ReadRecordStepDefinition } from '../types/validated/step-definition';
3
+ import RecordStepExecutor from './record-step-executor';
4
+ export default class ReadRecordStepExecutor extends RecordStepExecutor<ReadRecordStepDefinition> {
5
+ protected doExecute(): Promise<StepExecutionResult>;
6
+ private selectFields;
7
+ private buildReadFieldTool;
8
+ private formatFieldResults;
9
+ }
10
+ //# sourceMappingURL=read-record-step-executor.d.ts.map
@@ -0,0 +1,100 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const ai_proxy_1 = require("@forestadmin/ai-proxy");
7
+ const zod_1 = require("zod");
8
+ const errors_1 = require("../errors");
9
+ const record_step_executor_1 = __importDefault(require("./record-step-executor"));
10
+ const READ_RECORD_SYSTEM_PROMPT = `You are an AI agent reading fields from a record to answer a user request.
11
+ Select the field(s) that best answer the request. You can read one field or multiple fields at once.
12
+
13
+ Important rules:
14
+ - Be precise: only read fields that are directly relevant to the request.
15
+ - Final answer is definitive, you won't receive any other input from the user.
16
+ - Do not refer to yourself as "I" in the response, use a passive formulation instead.`;
17
+ class ReadRecordStepExecutor extends record_step_executor_1.default {
18
+ async doExecute() {
19
+ const { stepDefinition: step } = this.context;
20
+ const { preRecordedArgs } = step;
21
+ const records = await this.getAvailableRecordRefs();
22
+ const selectedRecordRef = await this.resolveRecordRef(records, step.prompt, preRecordedArgs?.selectedRecordStepIndex);
23
+ const schema = await this.getCollectionSchema(selectedRecordRef.collectionName);
24
+ const fieldNames = preRecordedArgs?.fieldNames ?? (await this.selectFields(schema, step.prompt));
25
+ const selectedFields = fieldNames.map(requested => ({
26
+ requested,
27
+ field: this.findFieldByTechnicalName(schema, requested),
28
+ }));
29
+ const resolvedFieldNames = selectedFields
30
+ .map(s => s.field?.fieldName)
31
+ .filter((name) => name !== undefined);
32
+ if (resolvedFieldNames.length === 0) {
33
+ throw new errors_1.NoResolvedFieldsError(selectedFields.map(s => s.requested));
34
+ }
35
+ const recordData = await this.context.agent.getRecord({
36
+ collection: selectedRecordRef.collectionName,
37
+ id: selectedRecordRef.recordId,
38
+ fields: resolvedFieldNames,
39
+ });
40
+ const fieldResults = this.formatFieldResults(recordData.values, selectedFields);
41
+ await this.context.runStore.saveStepExecution(this.context.runId, {
42
+ type: 'read-record',
43
+ stepIndex: this.context.stepIndex,
44
+ executionParams: {
45
+ fields: fieldResults.map(({ name, displayName }) => ({ name, displayName })),
46
+ },
47
+ executionResult: { fields: fieldResults },
48
+ selectedRecordRef,
49
+ });
50
+ return this.buildOutcomeResult({ status: 'success' });
51
+ }
52
+ async selectFields(schema, prompt) {
53
+ const tool = this.buildReadFieldTool(schema);
54
+ const messages = [
55
+ this.buildContextMessage(),
56
+ ...(await this.buildPreviousStepsMessages()),
57
+ new ai_proxy_1.SystemMessage(READ_RECORD_SYSTEM_PROMPT),
58
+ new ai_proxy_1.SystemMessage(`The selected record belongs to the "${schema.collectionDisplayName}" collection.`),
59
+ new ai_proxy_1.HumanMessage(`**Request**: ${prompt ?? 'Read the relevant fields.'}`),
60
+ ];
61
+ const { fieldNames } = await this.invokeWithTool(messages, tool);
62
+ return fieldNames.map(name => this.resolveAiFieldName(schema, name));
63
+ }
64
+ buildReadFieldTool(schema) {
65
+ const nonRelationFields = schema.fields.filter(f => !f.isRelationship);
66
+ if (nonRelationFields.length === 0) {
67
+ throw new errors_1.NoReadableFieldsError(schema.collectionName);
68
+ }
69
+ const displayNames = nonRelationFields.map(f => f.displayName);
70
+ return new ai_proxy_1.DynamicStructuredTool({
71
+ name: 'read-selected-record-fields',
72
+ description: 'Read one or more fields from the selected record.',
73
+ schema: zod_1.z.object({
74
+ // z.string() (not z.enum) intentionally: an invalid field name in the array
75
+ // does not fail the whole tool call — per-field errors are handled in formatFieldResults.
76
+ // This matches the frontend implementation (ISO frontend).
77
+ fieldNames: zod_1.z
78
+ .array(zod_1.z.string())
79
+ .describe(`Names of the fields to read, possible values are: ${displayNames
80
+ .map(n => `"${n}"`)
81
+ .join(', ')}`),
82
+ }),
83
+ func: undefined,
84
+ });
85
+ }
86
+ formatFieldResults(values, selected) {
87
+ return selected.map(({ requested, field }) => {
88
+ if (!field) {
89
+ return { error: `Field not found: ${requested}`, name: requested, displayName: requested };
90
+ }
91
+ return {
92
+ value: values[field.fieldName],
93
+ name: field.fieldName,
94
+ displayName: field.displayName,
95
+ };
96
+ });
97
+ }
98
+ }
99
+ exports.default = ReadRecordStepExecutor;
100
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmVhZC1yZWNvcmQtc3RlcC1leGVjdXRvci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9leGVjdXRvcnMvcmVhZC1yZWNvcmQtc3RlcC1leGVjdXRvci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7OztBQUtBLG9EQUEyRjtBQUMzRiw2QkFBd0I7QUFFeEIsc0NBQXlFO0FBQ3pFLGtGQUF3RDtBQUV4RCxNQUFNLHlCQUF5QixHQUFHOzs7Ozs7c0ZBTW9ELENBQUM7QUFFdkYsTUFBcUIsc0JBQXVCLFNBQVEsOEJBQTRDO0lBQ3BGLEtBQUssQ0FBQyxTQUFTO1FBQ3ZCLE1BQU0sRUFBRSxjQUFjLEVBQUUsSUFBSSxFQUFFLEdBQUcsSUFBSSxDQUFDLE9BQU8sQ0FBQztRQUM5QyxNQUFNLEVBQUUsZUFBZSxFQUFFLEdBQUcsSUFBSSxDQUFDO1FBQ2pDLE1BQU0sT0FBTyxHQUFHLE1BQU0sSUFBSSxDQUFDLHNCQUFzQixFQUFFLENBQUM7UUFFcEQsTUFBTSxpQkFBaUIsR0FBRyxNQUFNLElBQUksQ0FBQyxnQkFBZ0IsQ0FDbkQsT0FBTyxFQUNQLElBQUksQ0FBQyxNQUFNLEVBQ1gsZUFBZSxFQUFFLHVCQUF1QixDQUN6QyxDQUFDO1FBQ0YsTUFBTSxNQUFNLEdBQUcsTUFBTSxJQUFJLENBQUMsbUJBQW1CLENBQUMsaUJBQWlCLENBQUMsY0FBYyxDQUFDLENBQUM7UUFDaEYsTUFBTSxVQUFVLEdBQ2QsZUFBZSxFQUFFLFVBQVUsSUFBSSxDQUFDLE1BQU0sSUFBSSxDQUFDLFlBQVksQ0FBQyxNQUFNLEVBQUUsSUFBSSxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUM7UUFDaEYsTUFBTSxjQUFjLEdBQUcsVUFBVSxDQUFDLEdBQUcsQ0FBQyxTQUFTLENBQUMsRUFBRSxDQUFDLENBQUM7WUFDbEQsU0FBUztZQUNULEtBQUssRUFBRSxJQUFJLENBQUMsd0JBQXdCLENBQUMsTUFBTSxFQUFFLFNBQVMsQ0FBQztTQUN4RCxDQUFDLENBQUMs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
@@ -0,0 +1,19 @@
1
+ import type { StepExecutionResult } from '../types/execution-context';
2
+ import type { CollectionSchema, FieldSchema, RecordRef } from '../types/validated/collection';
3
+ import type { StepDefinition } from '../types/validated/step-definition';
4
+ import type { RecordStepStatus } from '../types/validated/step-outcome';
5
+ import BaseStepExecutor from './base-step-executor';
6
+ export default abstract class RecordStepExecutor<TStep extends StepDefinition = StepDefinition> extends BaseStepExecutor<TStep> {
7
+ protected buildOutcomeResult(outcome: {
8
+ status: RecordStepStatus;
9
+ error?: string;
10
+ }): StepExecutionResult;
11
+ protected resolveRecordRef(records: RecordRef[], prompt: string | undefined, preRecordedStepIndex?: number): Promise<RecordRef>;
12
+ protected getAvailableRecordRefs(): Promise<RecordRef[]>;
13
+ protected getCollectionSchema(collectionName: string): Promise<CollectionSchema>;
14
+ protected findFieldByTechnicalName(schema: CollectionSchema, name: string | undefined): FieldSchema | undefined;
15
+ protected resolveAiFieldName(schema: CollectionSchema, name: string): string;
16
+ private toRecordIdentifier;
17
+ private selectRecordRef;
18
+ }
19
+ //# sourceMappingURL=record-step-executor.d.ts.map
@@ -0,0 +1,108 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const ai_proxy_1 = require("@forestadmin/ai-proxy");
7
+ const zod_1 = require("zod");
8
+ const errors_1 = require("../errors");
9
+ const base_step_executor_1 = __importDefault(require("./base-step-executor"));
10
+ const step_definition_1 = require("../types/validated/step-definition");
11
+ class RecordStepExecutor extends base_step_executor_1.default {
12
+ buildOutcomeResult(outcome) {
13
+ return {
14
+ stepOutcome: {
15
+ type: 'record',
16
+ stepId: this.context.stepId,
17
+ stepIndex: this.context.stepIndex,
18
+ ...outcome,
19
+ },
20
+ };
21
+ }
22
+ async resolveRecordRef(records, prompt, preRecordedStepIndex) {
23
+ if (preRecordedStepIndex !== undefined) {
24
+ const match = records.find(r => r.stepIndex === preRecordedStepIndex);
25
+ if (!match) {
26
+ throw new errors_1.InvalidPreRecordedArgsError(`No record found at step index ${preRecordedStepIndex}`);
27
+ }
28
+ return match;
29
+ }
30
+ return this.selectRecordRef(records, prompt);
31
+ }
32
+ // Candidate sources for the AI: the base record plus the record each live prior
33
+ // load-related step resolved — own stepIndex first, falling back to a clone's
34
+ // originalStepIndex.
35
+ async getAvailableRecordRefs() {
36
+ const stepExecutions = await this.context.runStore.getStepExecutions(this.context.runId);
37
+ const relatedRecords = this.context.previousSteps.flatMap(step => {
38
+ if (step.stepDefinition.type !== step_definition_1.StepType.LoadRelatedRecord)
39
+ return [];
40
+ const execution = this.resolveStepExecution(step, stepExecutions);
41
+ if (execution?.type === 'load-related-record' &&
42
+ execution.executionResult !== undefined &&
43
+ 'record' in execution.executionResult) {
44
+ return [execution.executionResult.record];
45
+ }
46
+ return [];
47
+ });
48
+ return [this.context.baseRecordRef, ...relatedRecords];
49
+ }
50
+ getCollectionSchema(collectionName) {
51
+ return this.context.schemaResolver.resolve(collectionName);
52
+ }
53
+ findFieldByTechnicalName(schema, name) {
54
+ if (name === undefined)
55
+ return undefined;
56
+ return schema.fields.find(f => f.fieldName === name);
57
+ }
58
+ // Map an AI-returned displayName back to its technical fieldName. LLMs occasionally return
59
+ // formatting variants (e.g. "first_name" for "firstname", "full-name" for "Full Name"), so fall
60
+ // back to a normalized comparison. On a miss, returns the raw name — the exact lookup downstream
61
+ // turns it into a loud error.
62
+ resolveAiFieldName(schema, name) {
63
+ const exact = schema.fields.find(f => f.displayName === name) ??
64
+ schema.fields.find(f => f.fieldName === name);
65
+ if (exact)
66
+ return exact.fieldName;
67
+ const normalize = (s) => s.toLowerCase().replace(/[\s_-]/g, '');
68
+ const normalized = normalize(name);
69
+ const fuzzy = schema.fields.filter(f => normalize(f.displayName) === normalized || normalize(f.fieldName) === normalized);
70
+ return fuzzy.length === 1 ? fuzzy[0].fieldName : name;
71
+ }
72
+ async toRecordIdentifier(record) {
73
+ const schema = await this.getCollectionSchema(record.collectionName);
74
+ return `Step ${record.stepIndex} - ${schema.collectionDisplayName} #${record.recordId}`;
75
+ }
76
+ async selectRecordRef(records, prompt) {
77
+ if (records.length === 0)
78
+ throw new errors_1.NoRecordsError();
79
+ if (records.length === 1)
80
+ return records[0];
81
+ const identifiers = await Promise.all(records.map(r => this.toRecordIdentifier(r)));
82
+ const identifierTuple = identifiers;
83
+ const tool = new ai_proxy_1.DynamicStructuredTool({
84
+ name: 'select-record',
85
+ description: 'Select the most relevant record for this workflow step.',
86
+ schema: zod_1.z.object({
87
+ recordIdentifier: zod_1.z.enum(identifierTuple),
88
+ }),
89
+ func: undefined,
90
+ });
91
+ const messages = [
92
+ this.buildContextMessage(),
93
+ ...(await this.buildPreviousStepsMessages()),
94
+ new ai_proxy_1.SystemMessage('You are an AI agent selecting the most relevant record for a workflow step.\n' +
95
+ 'Choose the record whose collection best matches the user request.\n' +
96
+ 'Pay attention to the collection name of each record.'),
97
+ new ai_proxy_1.HumanMessage(prompt ?? 'Select the most relevant record.'),
98
+ ];
99
+ const { recordIdentifier } = await this.invokeWithTool(messages, tool);
100
+ const selectedIndex = identifiers.indexOf(recordIdentifier);
101
+ if (selectedIndex === -1) {
102
+ throw new errors_1.InvalidAIResponseError(`AI selected record "${recordIdentifier}" which does not match any available record`);
103
+ }
104
+ return records[selectedIndex];
105
+ }
106
+ }
107
+ exports.default = RecordStepExecutor;
108
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmVjb3JkLXN0ZXAtZXhlY3V0b3IuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvZXhlY3V0b3JzL3JlY29yZC1zdGVwLWV4ZWN1dG9yLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7O0FBS0Esb0RBQTJGO0FBQzNGLDZCQUF3QjtBQUV4QixzQ0FBZ0c7QUFDaEcsOEVBQW9EO0FBQ3BELHdFQUE4RDtBQUU5RCxNQUE4QixrQkFFNUIsU0FBUSw0QkFBdUI7SUFDckIsa0JBQWtCLENBQUMsT0FHNUI7UUFDQyxPQUFPO1lBQ0wsV0FBVyxFQUFFO2dCQUNYLElBQUksRUFBRSxRQUFRO2dCQUNkLE1BQU0sRUFBRSxJQUFJLENBQUMsT0FBTyxDQUFDLE1BQU07Z0JBQzNCLFNBQVMsRUFBRSxJQUFJLENBQUMsT0FBTyxDQUFDLFNBQVM7Z0JBQ2pDLEdBQUcsT0FBTzthQUNYO1NBQ0YsQ0FBQztJQUNKLENBQUM7SUFFUyxLQUFLLENBQUMsZ0JBQWdCLENBQzlCLE9BQW9CLEVBQ3BCLE1BQTBCLEVBQzFCLG9CQUE2QjtRQUU3QixJQUFJLG9CQUFvQixLQUFLLFNBQVMsRUFBRSxDQUFDO1lBQ3ZDLE1BQU0sS0FBSyxHQUFHLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUMsU0FBUyxLQUFLLG9CQUFvQixDQUFDLENBQUM7WUFFdEUsSUFBSSxDQUFDLEtBQUssRUFBRSxDQUFDO2dCQUNYLE1BQU0sSUFBSSxvQ0FBMkIsQ0FDbkMsaUNBQWlDLG9CQUFvQixFQUFFLENBQ3hELENBQUM7WUFDSixDQUFDO1lBRUQsT0FBTyxLQUFLLENBQUM7UUFDZixDQUFDO1FBRUQsT0FBTyxJQUFJLENBQUMsZUFBZSxDQUFDLE9BQU8sRUFBRSxNQUFNLENBQUMsQ0FBQztJQUMvQyxDQUFDO0lBRUQsZ0ZBQWdGO0lBQ2hGLDhFQUE4RTtJQUM5RSxxQkFBcUI7SUFDWCxLQUFLLENBQUMsc0JBQXNCO1FBQ3BDLE1BQU0s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
@@ -0,0 +1,24 @@
1
+ import type { ActivityLogPort } from '../ports/activity-log-port';
2
+ import type { AgentPort } from '../ports/agent-port';
3
+ import type { AiModelPort } from '../ports/ai-model-port';
4
+ import type { Logger } from '../ports/logger-port';
5
+ import type { RunStore } from '../ports/run-store';
6
+ import type { WorkflowPort } from '../ports/workflow-port';
7
+ import type { FetchRemoteToolsResult } from '../remote-tool-fetcher';
8
+ import type SchemaCache from '../schema-cache';
9
+ import type { AvailableStepExecution, IStepExecutor } from '../types/execution-context';
10
+ export interface StepContextConfig {
11
+ aiModelPort: AiModelPort;
12
+ agentPort: AgentPort;
13
+ workflowPort: WorkflowPort;
14
+ runStore: RunStore;
15
+ schemaCache: SchemaCache;
16
+ logger: Logger;
17
+ stepTimeoutMs?: number;
18
+ aiInvokeTimeoutMs?: number;
19
+ }
20
+ export default class StepExecutorFactory {
21
+ static create(step: AvailableStepExecution, contextConfig: StepContextConfig, activityLogPort: ActivityLogPort, fetchRemoteTools: (mcpServerId: string) => Promise<FetchRemoteToolsResult>, incomingPendingData?: unknown): Promise<IStepExecutor>;
22
+ private static buildContext;
23
+ }
24
+ //# sourceMappingURL=step-executor-factory.d.ts.map
@@ -0,0 +1,99 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const errors_1 = require("../errors");
7
+ const schema_resolver_1 = __importDefault(require("../schema-resolver"));
8
+ const activity_log_1 = __importDefault(require("./activity-log"));
9
+ const agent_with_log_1 = __importDefault(require("./agent-with-log"));
10
+ const condition_step_executor_1 = __importDefault(require("./condition-step-executor"));
11
+ const guidance_step_executor_1 = __importDefault(require("./guidance-step-executor"));
12
+ const load_related_record_step_executor_1 = __importDefault(require("./load-related-record-step-executor"));
13
+ const mcp_step_executor_1 = __importDefault(require("./mcp-step-executor"));
14
+ const read_record_step_executor_1 = __importDefault(require("./read-record-step-executor"));
15
+ const trigger_record_action_step_executor_1 = __importDefault(require("./trigger-record-action-step-executor"));
16
+ const update_record_step_executor_1 = __importDefault(require("./update-record-step-executor"));
17
+ const step_definition_1 = require("../types/validated/step-definition");
18
+ const step_outcome_1 = require("../types/validated/step-outcome");
19
+ class StepExecutorFactory {
20
+ static async create(step, contextConfig, activityLogPort, fetchRemoteTools, incomingPendingData) {
21
+ try {
22
+ const context = StepExecutorFactory.buildContext(step, contextConfig, activityLogPort, incomingPendingData);
23
+ switch (step.stepDefinition.type) {
24
+ case step_definition_1.StepType.Condition:
25
+ return new condition_step_executor_1.default(context);
26
+ case step_definition_1.StepType.ReadRecord:
27
+ return new read_record_step_executor_1.default(context);
28
+ case step_definition_1.StepType.UpdateRecord:
29
+ return new update_record_step_executor_1.default(context);
30
+ case step_definition_1.StepType.TriggerAction:
31
+ return new trigger_record_action_step_executor_1.default(context);
32
+ case step_definition_1.StepType.LoadRelatedRecord:
33
+ return new load_related_record_step_executor_1.default(context);
34
+ case step_definition_1.StepType.Mcp: {
35
+ const mcpContext = context;
36
+ const { tools, mcpServerName } = await fetchRemoteTools(mcpContext.stepDefinition.mcpServerId);
37
+ return new mcp_step_executor_1.default(mcpContext, tools, mcpServerName);
38
+ }
39
+ case step_definition_1.StepType.Guidance:
40
+ return new guidance_step_executor_1.default(context);
41
+ default:
42
+ throw new errors_1.StepStateError(`Unknown step type: ${step.stepDefinition.type}`);
43
+ }
44
+ }
45
+ catch (error) {
46
+ contextConfig.logger.error('Step execution failed unexpectedly', {
47
+ runId: step.runId,
48
+ stepId: step.stepId,
49
+ stepIndex: step.stepIndex,
50
+ error: (0, errors_1.extractErrorMessage)(error),
51
+ cause: (0, errors_1.causeMessage)(error),
52
+ stack: error instanceof Error ? error.stack : undefined,
53
+ });
54
+ return {
55
+ execute: async () => ({
56
+ stepOutcome: {
57
+ type: (0, step_outcome_1.stepTypeToOutcomeType)(step.stepDefinition.type),
58
+ stepId: step.stepId,
59
+ stepIndex: step.stepIndex,
60
+ status: 'error',
61
+ error: error instanceof errors_1.WorkflowExecutorError
62
+ ? error.userMessage
63
+ : 'An unexpected error occurred.',
64
+ },
65
+ }),
66
+ };
67
+ }
68
+ }
69
+ static buildContext(step, cfg, activityLogPort, incomingPendingData) {
70
+ const schemaResolver = new schema_resolver_1.default(cfg.schemaCache, cfg.workflowPort, step.runId);
71
+ const activityLog = new activity_log_1.default(activityLogPort, step.user);
72
+ return {
73
+ runId: step.runId,
74
+ stepId: step.stepId,
75
+ stepIndex: step.stepIndex,
76
+ collectionId: step.collectionId,
77
+ baseRecordRef: step.baseRecordRef,
78
+ stepDefinition: step.stepDefinition,
79
+ previousSteps: step.previousSteps,
80
+ user: step.user,
81
+ model: cfg.aiModelPort.getModel(step.stepDefinition.aiConfigName),
82
+ agent: new agent_with_log_1.default({
83
+ agentPort: cfg.agentPort,
84
+ schemaResolver,
85
+ user: step.user,
86
+ activityLog,
87
+ }),
88
+ activityLog,
89
+ runStore: cfg.runStore,
90
+ schemaResolver,
91
+ logger: cfg.logger,
92
+ incomingPendingData,
93
+ stepTimeoutMs: cfg.stepTimeoutMs,
94
+ aiInvokeTimeoutMs: cfg.aiInvokeTimeoutMs,
95
+ };
96
+ }
97
+ }
98
+ exports.default = StepExecutorFactory;
99
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic3RlcC1leGVjdXRvci1mYWN0b3J5LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2V4ZWN1dG9ycy9zdGVwLWV4ZWN1dG9yLWZhY3RvcnkudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7QUF3QkEsc0NBS21CO0FBQ25CLHlFQUFnRDtBQUNoRCxrRUFBeUM7QUFDekMsc0VBQTRDO0FBQzVDLHdGQUE4RDtBQUM5RCxzRkFBNEQ7QUFDNUQsNEdBQWdGO0FBQ2hGLDRFQUFrRDtBQUNsRCw0RkFBaUU7QUFDakUsZ0hBQW9GO0FBQ3BGLGdHQUFxRTtBQUNyRSx3RUFBOEQ7QUFDOUQsa0VBQXdFO0FBYXhFLE1BQXFCLG1CQUFtQjtJQUN0QyxNQUFNLENBQUMsS0FBSyxDQUFDLE1BQU0sQ0FDakIsSUFBNEIsRUFDNUIsYUFBZ0MsRUFDaEMsZUFBZ0MsRUFDaEMsZ0JBQTBFLEVBQzFFLG1CQUE2QjtRQUU3QixJQUFJLENBQUM7WUFDSCxNQUFNLE9BQU8sR0FBRyxtQkFBbUIsQ0FBQyxZQUFZLENBQzlDLElBQUksRUFDSixhQUFhLEVBQ2IsZUFBZSxFQUNmLG1CQUFtQixDQUNwQixDQUFDO1lBRUYsUUFBUSxJQUFJLENBQUMsY0FBYyxDQUFDLElBQUksRUFBRSxDQUFDO2dCQUNqQyxLQUFLLDBCQUFRLENBQUMsU0FBUztvQkFDckIsT0FBTyxJQUFJLGlDQUFxQixDQUFDLE9BQW9ELENBQUMsQ0FBQztnQkFDekYsS0FBSywwQkFBUSxDQUFDLFVBQVU7b0JBQ3RCLE9BQU8sSUFBSSxtQ0FBc0IsQ0FBQyxPQUFxRCxDQUFDLENBQUM7Z0JBQzNGLEtBQUssMEJBQVEsQ0FBQyxZQUFZO29CQUN4QixPQUFPLElBQUkscUNBQXdCLENBQ2pDLE9BQXVELENBQ3hELENBQUM7Z0JBQ0osS0FBSywwQkFBUSxDQUFDLGFBQWE7b0JBQ3pCLE9BQU8sSUFBSSw2Q0FBK0IsQ0FDeEMsT0FBd0QsQ0FDekQsQ0FBQztnQkFDSixLQUFLLDBCQUFRLENBQUMs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
@@ -0,0 +1,8 @@
1
+ import type { StepExecutionData } from '../../types/step-execution-data';
2
+ export default class StepExecutionFormatters {
3
+ static format(execution: StepExecutionData): string | null;
4
+ private static formatMcp;
5
+ private static formatGuidance;
6
+ private static formatLoadRelatedRecord;
7
+ }
8
+ //# sourceMappingURL=step-execution-formatters.d.ts.map