@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,478 @@
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 step_definition_1 = require("../types/validated/step-definition");
11
+ const SELECT_RELATION_SYSTEM_PROMPT = `You are an AI agent loading a related record based on a user request.
12
+ You are given relations to follow, each shown as "<source record> → <relation> (→ <target collection>)".
13
+ Choose the relation that LEADS TO the collection the user wants to load — decide from each
14
+ relation's target collection, NOT from which source record happens to resemble the request.
15
+
16
+ Important rules:
17
+ - Pick the relation whose target collection matches the requested record.
18
+ - Final answer is definitive, you won't receive any other input from the user.
19
+ - Do not refer to yourself as "I" in the response, use a passive formulation instead.`;
20
+ const SELECT_FIELDS_SYSTEM_PROMPT = `You are an AI agent selecting the most relevant fields to identify a related record.
21
+ Choose the fields that are most useful for determining which record best matches the user request.`;
22
+ const SELECT_RECORD_SYSTEM_PROMPT = `You are an AI agent selecting the most relevant related record from a list of candidates.
23
+ Choose the record that best matches the user request based on the provided field values.`;
24
+ // Bound only what is sent to the AI in selectBestRecordIndex — the full candidate list is
25
+ // always returned to the front via availableRecordIds. These cap the prompt size, not the data.
26
+ const MAX_RELEVANT_FIELDS = 6;
27
+ const MAX_FIELD_VALUE_LENGTH = 80; // per-field serialized length before truncation
28
+ const MAX_AI_CANDIDATES_CHARS = 16000; // global budget for the `Candidates:` block (~4k tokens)
29
+ function clampFieldValue(value) {
30
+ const serialized = typeof value === 'string' ? value : JSON.stringify(value) ?? '';
31
+ if (serialized.length <= MAX_FIELD_VALUE_LENGTH)
32
+ return value;
33
+ return `${serialized.slice(0, MAX_FIELD_VALUE_LENGTH)}… (truncated)`;
34
+ }
35
+ class LoadRelatedRecordStepExecutor extends record_step_executor_1.default {
36
+ async doExecute() {
37
+ // Branch A -- Re-entry after pending execution found in RunStore
38
+ const pending = await this.patchAndReloadPendingData(this.context.incomingPendingData);
39
+ if (pending) {
40
+ const conf = pending.userConfirmation;
41
+ if (conf?.userConfirmed === undefined && conf?.fieldName !== undefined) {
42
+ return this.refreshCandidatesForField(pending, conf.fieldName);
43
+ }
44
+ return this.handleConfirmationFlow(pending, async (exec) => this.resolveFromSelection(exec));
45
+ }
46
+ // Branches B & C -- First call
47
+ return this.handleFirstCall();
48
+ }
49
+ async refreshCandidatesForField(execution, fieldName) {
50
+ if (!execution.pendingData) {
51
+ throw new errors_1.StepStateError(`Step at index ${this.context.stepIndex} has no pending data`);
52
+ }
53
+ const schema = await this.getCollectionSchema(execution.selectedRecordRef.collectionName);
54
+ const target = this.buildTarget(schema, fieldName, execution.selectedRecordRef);
55
+ const { availableRecordIds, suggestedRecord } = await this.collectCandidateIds(target);
56
+ await this.context.runStore.saveStepExecution(this.context.runId, {
57
+ ...execution,
58
+ userConfirmation: undefined,
59
+ pendingData: {
60
+ ...execution.pendingData,
61
+ suggestedField: { name: target.name, displayName: target.displayName },
62
+ availableRecordIds,
63
+ suggestedRecord,
64
+ },
65
+ });
66
+ return this.buildOutcomeResult({ status: 'awaiting-input' });
67
+ }
68
+ async handleFirstCall() {
69
+ const { stepDefinition: step } = this.context;
70
+ const target = await this.resolveTarget();
71
+ // Branch B -- fully automated execution
72
+ if (step.executionType === step_definition_1.StepExecutionMode.FullyAutomated) {
73
+ return this.resolveAndLoadAutomatic(target);
74
+ }
75
+ // Branch C -- pre-fetch candidates, await user confirmation
76
+ const sourceSchema = await this.getCollectionSchema(target.selectedRecordRef.collectionName);
77
+ return this.saveAndAwaitInput(target, sourceSchema);
78
+ }
79
+ // Picks the (record, relation) pair to follow. Unlike a separate record-then-relation choice,
80
+ // this lets the AI decide by what each relation LEADS TO — so "load the dvd" follows
81
+ // store→dvds rather than latching onto a previously-loaded dvd whose collection just matches.
82
+ async resolveTarget() {
83
+ const { preRecordedArgs } = this.context.stepDefinition;
84
+ const records = await this.getAvailableRecordRefs();
85
+ const sourceRecords = preRecordedArgs?.selectedRecordStepIndex !== undefined
86
+ ? [this.requireRecordAtStepIndex(records, preRecordedArgs.selectedRecordStepIndex)]
87
+ : records;
88
+ const candidates = await this.buildRelationCandidates(sourceRecords);
89
+ if (candidates.length === 0) {
90
+ throw new errors_1.NoRelationshipFieldsError(sourceRecords[0]?.collectionName ?? 'unknown');
91
+ }
92
+ // Pre-recorded relations are pinned by their stable technical name (PRD-426), matched exactly.
93
+ const pinned = preRecordedArgs?.relationName;
94
+ const eligible = pinned ? candidates.filter(c => c.field.fieldName === pinned) : candidates;
95
+ if (eligible.length === 0) {
96
+ // Relations exist, but the pre-recorded one doesn't match any of them.
97
+ throw new errors_1.InvalidPreRecordedArgsError(`No relation matching "${pinned}" on the selected record`);
98
+ }
99
+ const chosen = eligible.length === 1 ? eligible[0] : await this.selectRelationToFollow(eligible);
100
+ return this.targetFromCandidate(chosen);
101
+ }
102
+ targetFromCandidate(candidate) {
103
+ const { record, field } = candidate;
104
+ return {
105
+ selectedRecordRef: record,
106
+ displayName: field.displayName,
107
+ name: field.fieldName,
108
+ relationType: field.relationType,
109
+ relatedCollectionName: field.relatedCollectionName,
110
+ };
111
+ }
112
+ requireRecordAtStepIndex(records, stepIndex) {
113
+ const match = records.find(r => r.stepIndex === stepIndex);
114
+ if (!match) {
115
+ throw new errors_1.InvalidPreRecordedArgsError(`No record found at step index ${stepIndex}`);
116
+ }
117
+ return match;
118
+ }
119
+ async buildRelationCandidates(records) {
120
+ const candidates = [];
121
+ for (const record of records) {
122
+ // eslint-disable-next-line no-await-in-loop
123
+ const schema = await this.getCollectionSchema(record.collectionName);
124
+ for (const field of schema.fields) {
125
+ if (field.isRelationship && field.relatedCollectionName) {
126
+ candidates.push({
127
+ record,
128
+ schema,
129
+ field: { ...field, relatedCollectionName: field.relatedCollectionName },
130
+ });
131
+ }
132
+ }
133
+ }
134
+ return candidates;
135
+ }
136
+ buildTarget(schema, relationName, selectedRecordRef) {
137
+ const field = this.findFieldByTechnicalName(schema, relationName);
138
+ if (!field) {
139
+ throw new errors_1.RelationNotFoundError(relationName, schema.collectionName);
140
+ }
141
+ if (!field.relatedCollectionName) {
142
+ throw new errors_1.StepStateError(`Step at index ${this.context.stepIndex} could not resolve relatedCollectionName for relation "${relationName}"`);
143
+ }
144
+ return {
145
+ selectedRecordRef,
146
+ displayName: field.displayName,
147
+ name: field.fieldName,
148
+ relationType: field.relationType,
149
+ relatedCollectionName: field.relatedCollectionName,
150
+ };
151
+ }
152
+ // Branch C: AI suggests the best candidate, then awaits user confirmation. Save errors
153
+ // propagate directly — the relation-load hasn't run yet, so the step can be safely retried.
154
+ async saveAndAwaitInput(target, sourceSchema) {
155
+ const { selectedRecordRef, name, displayName } = target;
156
+ const { availableRecordIds, suggestedRecord } = await this.collectCandidateIds(target);
157
+ const availableFields = sourceSchema.fields
158
+ .filter(f => f.isRelationship)
159
+ .map(f => ({ name: f.fieldName, displayName: f.displayName }));
160
+ await this.context.runStore.saveStepExecution(this.context.runId, {
161
+ type: 'load-related-record',
162
+ stepIndex: this.context.stepIndex,
163
+ pendingData: {
164
+ availableFields,
165
+ suggestedField: { name, displayName },
166
+ availableRecordIds,
167
+ suggestedRecord,
168
+ },
169
+ selectedRecordRef,
170
+ });
171
+ return this.buildOutcomeResult({ status: 'awaiting-input' });
172
+ }
173
+ async collectCandidateIds(target) {
174
+ if (target.relationType === 'BelongsTo' || target.relationType === 'HasOne') {
175
+ const candidate = await this.fetchXToOneCandidate(target);
176
+ return candidate
177
+ ? { availableRecordIds: [candidate], suggestedRecord: candidate }
178
+ : { availableRecordIds: [] };
179
+ }
180
+ const { relatedData, bestIndex, relatedSchema } = await this.selectBestFromRelatedData(target, 50);
181
+ if (relatedData.length === 0) {
182
+ return { availableRecordIds: [] };
183
+ }
184
+ const referenceField = relatedSchema.referenceField ?? null;
185
+ const toCandidate = (r) => ({
186
+ recordId: r.recordId,
187
+ referenceFieldValue: referenceField
188
+ ? this.extractReferenceFieldValue(r.values, referenceField)
189
+ : null,
190
+ });
191
+ return {
192
+ availableRecordIds: relatedData.map(toCandidate),
193
+ suggestedRecord: toCandidate(relatedData[bestIndex]),
194
+ };
195
+ }
196
+ extractReferenceFieldValue(values, referenceField) {
197
+ const v = values[referenceField];
198
+ return v === undefined || v === null ? null : String(v);
199
+ }
200
+ /** Branch B: fully automated. xToOne loads the linked record; HasMany ranks candidates via AI; BelongsToMany takes the first. */
201
+ async resolveAndLoadAutomatic(target) {
202
+ const record = await this.fetchRecordForRelation(target);
203
+ return this.persistAndReturn(record, target, undefined);
204
+ }
205
+ async fetchRecordForRelation(target) {
206
+ if (target.relationType === 'BelongsTo' || target.relationType === 'HasOne') {
207
+ return this.fetchXToOneRecordRef(target);
208
+ }
209
+ if (target.relationType === 'HasMany') {
210
+ return this.selectBestRelatedRecord(target);
211
+ }
212
+ return this.fetchFirstCandidate(target);
213
+ }
214
+ async fetchXToOneRecordRef(target) {
215
+ const candidate = await this.fetchXToOneCandidate(target);
216
+ if (!candidate) {
217
+ throw new errors_1.RelatedRecordNotFoundError(target.selectedRecordRef.collectionName, target.name);
218
+ }
219
+ return {
220
+ collectionName: target.relatedCollectionName,
221
+ recordId: candidate.recordId,
222
+ stepIndex: this.context.stepIndex,
223
+ };
224
+ }
225
+ async fetchXToOneCandidate(target) {
226
+ const relatedSchema = await this.getCollectionSchema(target.relatedCollectionName);
227
+ const referenceField = relatedSchema.referenceField ?? null;
228
+ const candidate = await this.context.agent.getSingleRelatedData({
229
+ collection: target.selectedRecordRef.collectionName,
230
+ id: target.selectedRecordRef.recordId,
231
+ relation: target.name,
232
+ relatedSchema,
233
+ ...(referenceField && { fields: [referenceField] }),
234
+ });
235
+ if (!candidate)
236
+ return null;
237
+ return {
238
+ recordId: candidate.recordId,
239
+ referenceFieldValue: referenceField
240
+ ? this.extractReferenceFieldValue(candidate.values, referenceField)
241
+ : null,
242
+ };
243
+ }
244
+ // Branch A: builds RecordRef from the user-confirmed selection without a new getRelatedData call.
245
+ async resolveFromSelection(execution) {
246
+ const { selectedRecordRef, pendingData, userConfirmation } = execution;
247
+ if (!pendingData) {
248
+ throw new errors_1.StepStateError(`Step at index ${this.context.stepIndex} has no pending data`);
249
+ }
250
+ const relationRef = userConfirmation?.fieldName
251
+ ? pendingData.availableFields.find(f => f.name === userConfirmation.fieldName)
252
+ : pendingData.suggestedField;
253
+ if (!relationRef) {
254
+ throw new errors_1.StepStateError(`Step at index ${this.context.stepIndex} could not resolve relation "${userConfirmation?.fieldName}" from available fields`);
255
+ }
256
+ const { name, displayName } = relationRef;
257
+ const selectedRecordId = userConfirmation?.selectedRecordId ?? pendingData.suggestedRecord?.recordId;
258
+ if (!selectedRecordId) {
259
+ throw new errors_1.RelatedRecordNotFoundError(selectedRecordRef.collectionName, name);
260
+ }
261
+ // Re-derive relatedCollectionName from the live schema — frontend never sends it.
262
+ const schema = await this.getCollectionSchema(selectedRecordRef.collectionName);
263
+ const field = schema.fields.find(f => f.fieldName === name);
264
+ if (!field?.relatedCollectionName) {
265
+ throw new errors_1.StepStateError(`Step at index ${this.context.stepIndex} could not resolve relatedCollectionName for relation "${name}"`);
266
+ }
267
+ const record = {
268
+ collectionName: field.relatedCollectionName,
269
+ recordId: selectedRecordId,
270
+ stepIndex: this.context.stepIndex,
271
+ };
272
+ return this.persistAndReturn(record, { selectedRecordRef, name, displayName }, execution);
273
+ }
274
+ async selectBestFromRelatedData(target, limit) {
275
+ const relatedSchema = await this.getCollectionSchema(target.relatedCollectionName);
276
+ const relatedData = await this.fetchRelatedData(target, relatedSchema, limit);
277
+ // Empty (bestIndex unused — callers guard on length) or single → no ranking needed.
278
+ if (relatedData.length <= 1) {
279
+ return { relatedData, bestIndex: 0, suggestedFields: [], relatedSchema };
280
+ }
281
+ const { preRecordedArgs } = this.context.stepDefinition;
282
+ if (preRecordedArgs?.selectedRecordIndex !== undefined) {
283
+ if (!Number.isInteger(preRecordedArgs.selectedRecordIndex) ||
284
+ preRecordedArgs.selectedRecordIndex < 0 ||
285
+ preRecordedArgs.selectedRecordIndex >= relatedData.length) {
286
+ throw new errors_1.InvalidPreRecordedArgsError(`Record index ${preRecordedArgs.selectedRecordIndex} is out of range (0-${relatedData.length - 1})`);
287
+ }
288
+ return {
289
+ relatedData,
290
+ bestIndex: preRecordedArgs.selectedRecordIndex,
291
+ suggestedFields: [],
292
+ relatedSchema,
293
+ };
294
+ }
295
+ const suggestedFields = await this.selectRelevantFields(relatedSchema, this.context.stepDefinition.prompt);
296
+ const bestIndex = await this.selectBestRecordIndex(relatedData, suggestedFields, this.context.stepDefinition.prompt);
297
+ return { relatedData, bestIndex, suggestedFields, relatedSchema };
298
+ }
299
+ /** HasMany + fully automated execution: fetch top 50, then AI calls to select the best record. */
300
+ async selectBestRelatedRecord(target) {
301
+ const { relatedData, bestIndex } = await this.selectBestFromRelatedData(target, 50);
302
+ if (relatedData.length === 0) {
303
+ throw new errors_1.RelatedRecordNotFoundError(target.selectedRecordRef.collectionName, target.name);
304
+ }
305
+ return this.toRecordRef(relatedData[bestIndex]);
306
+ }
307
+ async fetchFirstCandidate(target) {
308
+ const candidates = await this.fetchCandidates(target, 1);
309
+ return candidates[0];
310
+ }
311
+ async fetchCandidates(target, limit) {
312
+ const { selectedRecordRef, name } = target;
313
+ const relatedSchema = await this.getCollectionSchema(target.relatedCollectionName);
314
+ const relatedData = await this.fetchRelatedData(target, relatedSchema, limit);
315
+ if (relatedData.length === 0) {
316
+ throw new errors_1.RelatedRecordNotFoundError(selectedRecordRef.collectionName, name);
317
+ }
318
+ return relatedData.map(r => this.toRecordRef(r));
319
+ }
320
+ async fetchRelatedData(target, relatedSchema, limit) {
321
+ return this.context.agent.getRelatedData({
322
+ collection: target.selectedRecordRef.collectionName,
323
+ id: target.selectedRecordRef.recordId,
324
+ relation: target.name,
325
+ relatedSchema,
326
+ limit,
327
+ });
328
+ }
329
+ /** Persists the loaded record ref and returns a success outcome. */
330
+ async persistAndReturn(record, target, existingExecution) {
331
+ const { selectedRecordRef, name, displayName } = target;
332
+ await this.context.runStore.saveStepExecution(this.context.runId, {
333
+ ...existingExecution,
334
+ type: 'load-related-record',
335
+ stepIndex: this.context.stepIndex,
336
+ executionParams: { displayName, name },
337
+ executionResult: { relation: { name, displayName }, record },
338
+ selectedRecordRef,
339
+ });
340
+ return this.buildOutcomeResult({ status: 'success' });
341
+ }
342
+ relationOptionLabel(candidate) {
343
+ const { record, schema, field } = candidate;
344
+ return `Step ${record.stepIndex} - ${schema.collectionDisplayName} #${record.recordId} → ${field.displayName} (→ ${field.relatedCollectionName})`;
345
+ }
346
+ async selectRelationToFollow(candidates) {
347
+ const labels = candidates.map(c => this.relationOptionLabel(c));
348
+ const labelTuple = labels;
349
+ const tool = new ai_proxy_1.DynamicStructuredTool({
350
+ name: 'select-relation-to-follow',
351
+ description: 'Select the relation to follow to load the requested related record.',
352
+ schema: zod_1.z.object({
353
+ relation: zod_1.z
354
+ .enum(labelTuple)
355
+ .describe('The relation to follow, chosen by the collection it leads to'),
356
+ reasoning: zod_1.z.string().describe('Why this relation leads to the requested record'),
357
+ }),
358
+ func: undefined,
359
+ });
360
+ const messages = [
361
+ this.buildContextMessage(),
362
+ ...(await this.buildPreviousStepsMessages()),
363
+ new ai_proxy_1.SystemMessage(SELECT_RELATION_SYSTEM_PROMPT),
364
+ new ai_proxy_1.HumanMessage(`**Request**: ${this.context.stepDefinition.prompt ?? 'Load the relevant related record.'}`),
365
+ ];
366
+ const { relation } = await this.invokeWithTool(messages, tool);
367
+ const index = labels.indexOf(relation);
368
+ if (index === -1) {
369
+ throw new errors_1.InvalidAIResponseError(`AI selected relation "${relation}" which does not match any available option`);
370
+ }
371
+ return candidates[index];
372
+ }
373
+ /** AI call 1 for HasMany: selects the most relevant fields to compare candidates. */
374
+ async selectRelevantFields(schema, prompt) {
375
+ const nonRelationFields = schema.fields.filter(f => !f.isRelationship);
376
+ if (nonRelationFields.length === 0)
377
+ return [];
378
+ // Use displayName in both the enum and the prompt for consistency — the AI sees human-readable
379
+ // names throughout. Results are mapped back to technical fieldNames before returning.
380
+ const displayNames = nonRelationFields.map(f => f.displayName);
381
+ const tool = new ai_proxy_1.DynamicStructuredTool({
382
+ name: 'select-fields',
383
+ description: 'Select the most relevant fields to identify the right record.',
384
+ schema: zod_1.z.object({
385
+ fieldNames: zod_1.z
386
+ .array(zod_1.z.enum(displayNames))
387
+ .min(1)
388
+ .max(MAX_RELEVANT_FIELDS)
389
+ .describe(`The ${MAX_RELEVANT_FIELDS} fields most useful for identifying the relevant record`),
390
+ }),
391
+ func: undefined,
392
+ });
393
+ const messages = [
394
+ this.buildContextMessage(),
395
+ new ai_proxy_1.SystemMessage(SELECT_FIELDS_SYSTEM_PROMPT),
396
+ new ai_proxy_1.SystemMessage(`The related records are from the "${schema.collectionDisplayName}" collection. ` +
397
+ `Available fields: ${nonRelationFields.map(f => f.displayName).join(', ')}.`),
398
+ new ai_proxy_1.HumanMessage(`**Request**: ${prompt ?? 'Select the most relevant record.'}`),
399
+ ];
400
+ const { fieldNames: selectedDisplayNames } = await this.invokeWithTool(messages, tool);
401
+ // Zod's .min(1) shapes the prompt but is NOT validated against the AI response.
402
+ // Guard explicitly to avoid silently passing all fields to selectBestRecordIndex.
403
+ if (selectedDisplayNames.length === 0) {
404
+ throw new errors_1.InvalidAIResponseError(`AI returned no field names for field selection in collection "${schema.collectionName}"`);
405
+ }
406
+ // Map display names back to technical field names — values in RecordData are keyed by fieldName.
407
+ // .max() shapes the prompt only (not validated against the response), so cap explicitly.
408
+ return selectedDisplayNames
409
+ .slice(0, MAX_RELEVANT_FIELDS)
410
+ .map(dn => nonRelationFields.find(f => f.displayName === dn)?.fieldName ?? dn);
411
+ }
412
+ /** AI call 2 for HasMany: selects the best record by index from the candidate list. */
413
+ async selectBestRecordIndex(candidates, fieldNames, prompt) {
414
+ const filteredCandidates = candidates.map((c, i) => {
415
+ const entries = Object.entries(c.values).filter(([k]) => fieldNames.length === 0 || fieldNames.includes(k));
416
+ return {
417
+ index: i,
418
+ values: Object.fromEntries(entries.map(([k, v]) => [k, clampFieldValue(v)])),
419
+ };
420
+ });
421
+ // Bound the prompt only — the full candidate list is still returned upstream. Accumulate
422
+ // lines until the global budget is reached (always keep at least the first candidate).
423
+ const lines = [];
424
+ let usedChars = 0;
425
+ for (const c of filteredCandidates) {
426
+ const line = `[${c.index}] ${JSON.stringify(c.values)}`;
427
+ if (lines.length > 0 && usedChars + line.length > MAX_AI_CANDIDATES_CHARS)
428
+ break;
429
+ lines.push(line);
430
+ usedChars += line.length + 1;
431
+ }
432
+ const shown = lines.length;
433
+ if (shown < candidates.length) {
434
+ this.context.logger.warn('load-related-record: candidate list truncated for AI prompt', {
435
+ ...this.logCtx,
436
+ shown,
437
+ total: candidates.length,
438
+ });
439
+ }
440
+ const maxIndex = shown - 1;
441
+ const tool = new ai_proxy_1.DynamicStructuredTool({
442
+ name: 'select-record-by-content',
443
+ description: 'Select the most relevant related record by its index.',
444
+ schema: zod_1.z.object({
445
+ recordIndex: zod_1.z
446
+ .number()
447
+ .int()
448
+ .min(0)
449
+ .max(maxIndex)
450
+ .describe(`0-based index of the most relevant record (0 to ${maxIndex})`),
451
+ reasoning: zod_1.z.string().describe('Why this record was chosen'),
452
+ }),
453
+ func: undefined,
454
+ });
455
+ const messages = [
456
+ this.buildContextMessage(),
457
+ new ai_proxy_1.SystemMessage(SELECT_RECORD_SYSTEM_PROMPT),
458
+ new ai_proxy_1.SystemMessage(`Candidates:\n${lines.join('\n')}`),
459
+ new ai_proxy_1.HumanMessage(`**Request**: ${prompt ?? 'Select the most relevant record.'}`),
460
+ ];
461
+ const { recordIndex } = await this.invokeWithTool(messages, tool);
462
+ // NOTE: The Zod schema's .min(0).max(maxIndex) shapes the tool prompt only — it is NOT
463
+ // validated against the AI response. This guard is the sole runtime enforcement.
464
+ if (!Number.isInteger(recordIndex) || recordIndex < 0 || recordIndex > maxIndex) {
465
+ throw new errors_1.InvalidAIResponseError(`AI selected record index ${recordIndex} which is out of range (0-${maxIndex}) or not an integer`);
466
+ }
467
+ return recordIndex;
468
+ }
469
+ toRecordRef(data) {
470
+ return {
471
+ collectionName: data.collectionName,
472
+ recordId: data.recordId,
473
+ stepIndex: this.context.stepIndex,
474
+ };
475
+ }
476
+ }
477
+ exports.default = LoadRelatedRecordStepExecutor;
478
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibG9hZC1yZWxhdGVkLXJlY29yZC1zdGVwLWV4ZWN1dG9yLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2V4ZWN1dG9ycy9sb2FkLXJlbGF0ZWQtcmVjb3JkLXN0ZXAtZXhlY3V0b3IudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7QUFjQSxvREFBMkY7QUFDM0YsNkJBQXdCO0FBRXhCLHNDQU9tQjtBQUNuQixrRkFBd0Q7QUFDeEQsd0VBQXVFO0FBRXZFLE1BQU0sNkJBQTZCLEdBQUc7Ozs7Ozs7O3NGQVFnRCxDQUFDO0FBRXZGLE1BQU0sMkJBQTJCLEdBQUc7bUdBQytELENBQUM7QUFFcEcsTUFBTSwyQkFBMkIsR0FBRzt5RkFDcUQsQ0FBQztBQUUxRiwwRkFBMEY7QUFDMUYsZ0dBQWdHO0FBQ2hHLE1BQU0sbUJBQW1CLEdBQUcsQ0FBQyxDQUFDO0FBQzlCLE1BQU0sc0JBQXNCLEdBQUcsRUFBRSxDQUFDLENBQUMsZ0RBQWdEO0FBQ25GLE1BQU0sdUJBQXVCLEdBQUcsS0FBTSxDQUFDLENBQUMseURBQXlEO0FBRWpHLFNBQVMsZUFBZSxDQUFDLEtBQWM7SUFDckMsTUFBTSxVQUFVLEdBQUcsT0FBTyxLQUFLLEtBQUssUUFBUSxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxTQUFTLENBQUMsS0FBSyxDQUFDLElBQUksRUFBRSxDQUFDO0lBQ25GLElBQUksVUFBVSxDQUFDLE1BQU0sSUFBSSxzQkFBc0I7UUFBRSxPQUFPLEtBQUssQ0FBQztJQUU5RCxPQUFPLEdBQUcsVUFBVSxDQUFDLEtBQUssQ0FBQyxDQUFDLEVBQUUsc0JBQXNCLENBQUMsZUFBZSxDQUFDO0FBQ3ZFLENBQUM7QUFnQkQsTUFBcUIsNkJBQThCLFNBQVEsOEJBQW1EO0lBQ2xHLEtBQUssQ0FBQyxTQUFTO1FBQ3ZCLGlFQUFpRTtRQUNqRSxNQUFNLE9BQU8sR0FBRyxNQUFNLElBQUksQ0FBQyx5QkFBeUIsQ0FDbEQsSUFB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
@@ -0,0 +1,22 @@
1
+ import type { ExecutionContext, StepExecutionResult } from '../types/execution-context';
2
+ import type { McpStepDefinition } from '../types/validated/step-definition';
3
+ import type { RecordStepStatus } from '../types/validated/step-outcome';
4
+ import type { RemoteTool } from '@forestadmin/ai-proxy';
5
+ import BaseStepExecutor from './base-step-executor';
6
+ export default class McpStepExecutor extends BaseStepExecutor<McpStepDefinition> {
7
+ private readonly remoteTools;
8
+ private readonly mcpServerName?;
9
+ constructor(context: ExecutionContext<McpStepDefinition>, remoteTools: readonly RemoteTool[], mcpServerName?: string);
10
+ protected getExtraLogContext(): Record<string, unknown>;
11
+ protected buildOutcomeResult(outcome: {
12
+ status: RecordStepStatus;
13
+ error?: string;
14
+ }): StepExecutionResult;
15
+ protected checkIdempotency(): Promise<StepExecutionResult | null>;
16
+ protected doExecute(): Promise<StepExecutionResult>;
17
+ private executeToolAndPersist;
18
+ private formatToolResult;
19
+ private selectTool;
20
+ private requireTools;
21
+ }
22
+ //# sourceMappingURL=mcp-step-executor.d.ts.map