@forestadmin/workflow-executor 1.33.1 → 1.34.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.
- package/dist/adapters/agent-client-agent-port.js +6 -8
- package/dist/adapters/agent-client-segment-reader.d.ts +24 -0
- package/dist/adapters/agent-client-segment-reader.js +182 -0
- package/dist/adapters/agent-errors.d.ts +11 -0
- package/dist/adapters/agent-errors.js +81 -0
- package/dist/adapters/forest-server-automation-port.d.ts +22 -0
- package/dist/adapters/forest-server-automation-port.js +174 -0
- package/dist/adapters/forest-server-workflow-port.d.ts +3 -1
- package/dist/adapters/forest-server-workflow-port.js +47 -21
- package/dist/adapters/forestadmin-client-activity-log-port.js +3 -3
- package/dist/adapters/project-timezone.d.ts +2 -0
- package/dist/adapters/project-timezone.js +11 -0
- package/dist/adapters/run-to-available-step-mapper.js +7 -16
- package/dist/adapters/server-types.d.ts +119 -1
- package/dist/adapters/server-types.js +104 -2
- package/dist/adapters/step-user.d.ts +23 -0
- package/dist/adapters/step-user.js +33 -0
- package/dist/automation/automation-poller.d.ts +45 -0
- package/dist/automation/automation-poller.js +201 -0
- package/dist/automation/inbox-poll.d.ts +47 -0
- package/dist/automation/inbox-poll.js +357 -0
- package/dist/automation/lease-keeper.d.ts +12 -0
- package/dist/automation/lease-keeper.js +30 -0
- package/dist/automation/read-failure.d.ts +5 -0
- package/dist/automation/read-failure.js +40 -0
- package/dist/automation/reconciliation.d.ts +13 -0
- package/dist/automation/reconciliation.js +88 -0
- package/dist/build-workflow-executor.d.ts +2 -0
- package/dist/build-workflow-executor.js +62 -4
- package/dist/cli-core.js +7 -1
- package/dist/defaults.d.ts +2 -0
- package/dist/defaults.js +10 -2
- package/dist/errors.d.ts +30 -2
- package/dist/errors.js +56 -26
- package/dist/http/pending-data-validators.js +3 -3
- package/dist/http/step-serializer.js +5 -5
- package/dist/ports/automation-port.d.ts +19 -0
- package/dist/ports/automation-port.js +3 -0
- package/dist/ports/segment-reader-port.d.ts +43 -0
- package/dist/ports/segment-reader-port.js +3 -0
- package/dist/ports/workflow-port.d.ts +3 -1
- package/dist/{adapters/record-id-serializer.d.ts → record-id.d.ts} +2 -2
- package/dist/record-id.js +20 -0
- package/dist/runner.d.ts +3 -0
- package/dist/runner.js +59 -12
- package/dist/types/automation.d.ts +64 -0
- package/dist/types/automation.js +3 -0
- package/dist/types/validated/execution.d.ts +2 -1
- package/dist/types/validated/execution.js +2 -1
- package/package.json +6 -4
- package/dist/adapters/record-id-serializer.js +0 -20
|
@@ -0,0 +1,357 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const read_failure_1 = require("./read-failure");
|
|
4
|
+
const reconciliation_1 = require("./reconciliation");
|
|
5
|
+
const errors_1 = require("../errors");
|
|
6
|
+
function describeAgentFailure(error) {
|
|
7
|
+
return {
|
|
8
|
+
error: (0, errors_1.extractErrorMessage)(error),
|
|
9
|
+
httpStatus: error instanceof errors_1.SegmentReadError ? error.httpStatus : undefined,
|
|
10
|
+
agentError: error instanceof errors_1.SegmentReadError ? error.agentDetail : undefined,
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
function agentSort(inboxSort, sortsOnSeveralFields) {
|
|
14
|
+
return sortsOnSeveralFields ? inboxSort : inboxSort?.slice(0, 1);
|
|
15
|
+
}
|
|
16
|
+
// Offset pages only walk a total order, which only a single-column key can close as a tiebreak.
|
|
17
|
+
function paddedOrder(inbox, wholeInboxSort, sortsOnSeveralFields) {
|
|
18
|
+
const [primaryKey] = inbox.primaryKeys;
|
|
19
|
+
const byKey = { field: primaryKey, ascending: true };
|
|
20
|
+
const inboxSort = agentSort(wholeInboxSort, sortsOnSeveralFields);
|
|
21
|
+
if (inbox.primaryKeys.length !== 1)
|
|
22
|
+
return { sort: inboxSort, pageable: false };
|
|
23
|
+
if (!inboxSort)
|
|
24
|
+
return { sort: [byKey], pageable: true };
|
|
25
|
+
if (inboxSort.some(({ field }) => field === primaryKey)) {
|
|
26
|
+
return { sort: inboxSort, pageable: true };
|
|
27
|
+
}
|
|
28
|
+
if (!sortsOnSeveralFields)
|
|
29
|
+
return { sort: inboxSort, pageable: false };
|
|
30
|
+
return { sort: [...inboxSort, byKey], pageable: true };
|
|
31
|
+
}
|
|
32
|
+
function activeInboxSort(inbox, attempt) {
|
|
33
|
+
return attempt.inboxSortDropped ? undefined : inbox.sort;
|
|
34
|
+
}
|
|
35
|
+
function segmentQuery(inbox) {
|
|
36
|
+
return {
|
|
37
|
+
collectionName: inbox.collectionName,
|
|
38
|
+
segment: inbox.segment,
|
|
39
|
+
primaryKeys: inbox.primaryKeys,
|
|
40
|
+
user: inbox.user,
|
|
41
|
+
timezone: inbox.timezone,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* One sweep of one automated inbox: reconciles the records whose run is over, reads new candidates,
|
|
46
|
+
* and reports both to the orchestrator in a single sync.
|
|
47
|
+
*/
|
|
48
|
+
class InboxPoll {
|
|
49
|
+
constructor(params) {
|
|
50
|
+
this.automationPort = params.automationPort;
|
|
51
|
+
this.segmentReaderPort = params.segmentReaderPort;
|
|
52
|
+
this.logger = params.logger;
|
|
53
|
+
}
|
|
54
|
+
async poll(inbox) {
|
|
55
|
+
const logContext = {
|
|
56
|
+
inboxId: inbox.inboxId,
|
|
57
|
+
renderingId: inbox.renderingId,
|
|
58
|
+
workflowId: inbox.workflowId,
|
|
59
|
+
collectionName: inbox.collectionName,
|
|
60
|
+
};
|
|
61
|
+
try {
|
|
62
|
+
const assignments = await this.automationPort.listAssignments(inbox.inboxId);
|
|
63
|
+
// Read independently, and neither is allowed to cost the other. Losing the reconciliation
|
|
64
|
+
// because the candidate page timed out would leave those assignments open, which makes the
|
|
65
|
+
// next page larger, which makes the next timeout likelier.
|
|
66
|
+
const [closed, candidates] = await Promise.all([
|
|
67
|
+
this.tryRead(logContext, 'closed records', attempt => this.reconcileClosed(inbox, assignments, attempt)),
|
|
68
|
+
this.tryRead(logContext, 'new candidates', attempt => this.readCandidates(logContext, inbox, assignments, attempt)),
|
|
69
|
+
]);
|
|
70
|
+
// The candidate read is the one that starts runs, so its failure is the one worth fixing.
|
|
71
|
+
const readFailure = candidates.failure ?? closed.failure;
|
|
72
|
+
// Sent even when both lists are empty or both reads failed: the orchestrator keeps the last
|
|
73
|
+
// read failure until a sync comes without one, so a skipped sync would leave the settings
|
|
74
|
+
// panel with a stale warning, or none at all.
|
|
75
|
+
const results = await this.automationPort.sync(inbox.inboxId, {
|
|
76
|
+
closed: closed.items,
|
|
77
|
+
candidates: candidates.items,
|
|
78
|
+
readFailure,
|
|
79
|
+
});
|
|
80
|
+
this.logger('Info', 'Automated inbox polled', {
|
|
81
|
+
...logContext,
|
|
82
|
+
assignments: assignments.length,
|
|
83
|
+
reconciled: closed.items.length,
|
|
84
|
+
candidates: candidates.items.length,
|
|
85
|
+
candidatePageSize: candidates.requestedPageSize,
|
|
86
|
+
candidatePagesRead: candidates.pagesRead,
|
|
87
|
+
paddedPageReason: candidates.paddedPageReason,
|
|
88
|
+
readFailure: readFailure?.reason,
|
|
89
|
+
outcomes: results.reduce((counts, { outcome }) => ({ ...counts, [outcome]: (counts[outcome] ?? 0) + 1 }), {}),
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
catch (error) {
|
|
93
|
+
// Legitimate only if the inbox was disabled or degraded since this cycle's listing served it.
|
|
94
|
+
// Otherwise its route is missing behind a proxy, a path prefix or a version skew, and the inbox
|
|
95
|
+
// is never swept again.
|
|
96
|
+
if (error instanceof errors_1.AutomatedInboxGoneError) {
|
|
97
|
+
this.logger('Warn', 'Automated inbox listed this cycle but its route answered 404, dropping it for this cycle', { ...logContext, operation: error.operation, detail: error.detail });
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
this.logger('Error', 'Automated inbox poll failed', {
|
|
101
|
+
...logContext,
|
|
102
|
+
error: (0, errors_1.extractErrorMessage)(error),
|
|
103
|
+
stack: error instanceof Error ? error.stack : undefined,
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* A record whose run is over is only treated once it has left the segment. Reading the assignment
|
|
109
|
+
* state alone is not enough: a record reported still in the segment while its run is going would
|
|
110
|
+
* be judged untreated before the run had a chance to take it out.
|
|
111
|
+
*/
|
|
112
|
+
async reconcileClosed(inbox, assignments, attempt) {
|
|
113
|
+
const logContext = { inboxId: inbox.inboxId, renderingId: inbox.renderingId };
|
|
114
|
+
for (const { state, recordId, workflowRunId } of (0, reconciliation_1.withUnknownState)(assignments)) {
|
|
115
|
+
this.logger('Warn', 'Unknown assignment state, leaving the record out of every sweep until this executor knows it', { ...logContext, state, recordId, workflowRunId });
|
|
116
|
+
}
|
|
117
|
+
const toReconcile = (0, reconciliation_1.reconcilable)(assignments);
|
|
118
|
+
for (const { runState, recordId, workflowRunId } of (0, reconciliation_1.withUnexpectedRunState)(toReconcile)) {
|
|
119
|
+
this.logger('Warn', 'Unexpected workflow run state, leaving the record out of every sweep until this executor knows it', { ...logContext, runState, recordId, workflowRunId });
|
|
120
|
+
}
|
|
121
|
+
// An unreadable id is dropped one by one rather than failing the chunk, which would end
|
|
122
|
+
// reconciliation for the whole inbox on every cycle, and left out of the report entirely:
|
|
123
|
+
// telling the orchestrator it is not in the segment would retire its assignment and let the
|
|
124
|
+
// record be launched again.
|
|
125
|
+
const readable = (0, reconciliation_1.recordsToCheck)(assignments, toReconcile).filter(recordId => {
|
|
126
|
+
if ((0, reconciliation_1.isReadableRecordId)(recordId, inbox.primaryKeys))
|
|
127
|
+
return true;
|
|
128
|
+
this.logger('Warn', 'Unreadable record id, leaving the record out of the reconciliation', {
|
|
129
|
+
...logContext,
|
|
130
|
+
recordId,
|
|
131
|
+
primaryKeyCount: inbox.primaryKeys.length,
|
|
132
|
+
});
|
|
133
|
+
return false;
|
|
134
|
+
});
|
|
135
|
+
if (readable.length === 0)
|
|
136
|
+
return { items: [] };
|
|
137
|
+
const stillInSegment = new Set();
|
|
138
|
+
const batches = (0, reconciliation_1.chunk)(readable, reconciliation_1.MEMBERSHIP_CHUNK_SIZE);
|
|
139
|
+
for (const [index, batch] of batches.entries()) {
|
|
140
|
+
Object.assign(attempt, { membershipChunk: index + 1, membershipChunks: batches.length });
|
|
141
|
+
// eslint-disable-next-line no-await-in-loop
|
|
142
|
+
const found = await this.segmentReaderPort.listRecordIds({
|
|
143
|
+
...segmentQuery(inbox),
|
|
144
|
+
recordIds: batch,
|
|
145
|
+
pageSize: batch.length,
|
|
146
|
+
});
|
|
147
|
+
found.forEach(recordId => stillInSegment.add(recordId));
|
|
148
|
+
}
|
|
149
|
+
return {
|
|
150
|
+
items: readable.map(recordId => ({
|
|
151
|
+
recordId,
|
|
152
|
+
stillInSegment: stillInSegment.has(recordId),
|
|
153
|
+
})),
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Runs one of the two segment reads. A failure yields an empty list rather than ending the poll,
|
|
158
|
+
* so the other read and the sync still happen — but it is reported as a failure, because "read
|
|
159
|
+
* nothing" and "could not read" must not look alike to the caller.
|
|
160
|
+
*/
|
|
161
|
+
async tryRead(logContext, what, read) {
|
|
162
|
+
const attempt = {};
|
|
163
|
+
try {
|
|
164
|
+
return await read(attempt);
|
|
165
|
+
}
|
|
166
|
+
catch (error) {
|
|
167
|
+
this.logger('Error', `Could not read ${what} of an automated inbox`, {
|
|
168
|
+
...logContext,
|
|
169
|
+
...attempt,
|
|
170
|
+
...describeAgentFailure(error),
|
|
171
|
+
});
|
|
172
|
+
return { items: [], failure: (0, read_failure_1.toReadFailure)(error) };
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Asks the agent for records the orchestrator has no assignment for. Excluding them in the query
|
|
177
|
+
* is what keeps the page at the run cap: a record already treated but still in the segment stays
|
|
178
|
+
* there for good, and left in the page it would take a slot on every poll from then on.
|
|
179
|
+
*/
|
|
180
|
+
async readCandidates(logContext, inbox, assignments, attempt) {
|
|
181
|
+
const known = (0, reconciliation_1.knownRecordIds)(assignments);
|
|
182
|
+
const knownSet = new Set(known);
|
|
183
|
+
const paddedPageReason = await this.paddedPageReason(logContext, inbox, known);
|
|
184
|
+
const sortsOnSeveralFields = this.segmentReaderPort.sortsOnSeveralFields(inbox.liana, inbox.lianaVersion);
|
|
185
|
+
if (!paddedPageReason) {
|
|
186
|
+
Object.assign(attempt, {
|
|
187
|
+
requestedPageSize: inbox.maxConcurrentRuns,
|
|
188
|
+
notIn: known.length > 0,
|
|
189
|
+
});
|
|
190
|
+
let page;
|
|
191
|
+
try {
|
|
192
|
+
page = await this.readWithInboxSort(logContext, inbox, attempt, inboxSort => agentSort(inboxSort, sortsOnSeveralFields), sort => this.segmentReaderPort.listRecordIds({
|
|
193
|
+
...segmentQuery(inbox),
|
|
194
|
+
...(known.length ? { excludedRecordIds: known } : {}),
|
|
195
|
+
pageSize: inbox.maxConcurrentRuns,
|
|
196
|
+
sort,
|
|
197
|
+
}));
|
|
198
|
+
}
|
|
199
|
+
catch (error) {
|
|
200
|
+
if (!known.length || !(0, read_failure_1.mayBeOperatorRefusal)(error))
|
|
201
|
+
throw error;
|
|
202
|
+
// Declared is not implemented: a datasource can list `not_in` and still refuse it.
|
|
203
|
+
this.logger('Warn', 'The not_in candidate read failed, padding the page instead', {
|
|
204
|
+
...logContext,
|
|
205
|
+
...attempt,
|
|
206
|
+
...describeAgentFailure(error),
|
|
207
|
+
});
|
|
208
|
+
return this.readPaddedCandidates(logContext, inbox, knownSet, 'not-in-refused', attempt);
|
|
209
|
+
}
|
|
210
|
+
const fresh = page.filter(recordId => !knownSet.has(recordId));
|
|
211
|
+
// An agent that ignores an operator it does not know hands known records back. A page of
|
|
212
|
+
// nothing else would starve the inbox on every sweep, so it is padded like a refused read.
|
|
213
|
+
if (page.length > 0 && fresh.length === 0) {
|
|
214
|
+
this.logger('Warn', 'The not_in candidate read returned only known records, padding the page instead', {
|
|
215
|
+
...logContext,
|
|
216
|
+
...attempt,
|
|
217
|
+
});
|
|
218
|
+
return this.readPaddedCandidates(logContext, inbox, knownSet, 'not-in-ignored', attempt);
|
|
219
|
+
}
|
|
220
|
+
return { items: fresh, requestedPageSize: inbox.maxConcurrentRuns };
|
|
221
|
+
}
|
|
222
|
+
return this.readPaddedCandidates(logContext, inbox, knownSet, paddedPageReason, attempt);
|
|
223
|
+
}
|
|
224
|
+
async readPaddedCandidates(logContext, inbox, knownSet, paddedPageReason, attempt) {
|
|
225
|
+
const requestedPageSize = (0, reconciliation_1.paddedPageSize)(inbox.maxConcurrentRuns, knownSet.size);
|
|
226
|
+
const sortsOnSeveralFields = this.segmentReaderPort.sortsOnSeveralFields(inbox.liana, inbox.lianaVersion);
|
|
227
|
+
const orderFor = (inboxSort) => paddedOrder(inbox, inboxSort, sortsOnSeveralFields);
|
|
228
|
+
const byKeyOrder = orderFor(undefined);
|
|
229
|
+
let order = orderFor(activeInboxSort(inbox, attempt));
|
|
230
|
+
let orderPagesRead = 0;
|
|
231
|
+
const candidates = new Set();
|
|
232
|
+
let pagesRead = 0;
|
|
233
|
+
let reachedEnd = false;
|
|
234
|
+
while (pagesRead < reconciliation_1.MAX_PADDED_PAGES &&
|
|
235
|
+
candidates.size < inbox.maxConcurrentRuns &&
|
|
236
|
+
!reachedEnd) {
|
|
237
|
+
// A page in an order that cannot be paged may hold nothing but treated records: the key order
|
|
238
|
+
// then walks past them, as it would without an inbox sort.
|
|
239
|
+
if (orderPagesRead > 0 && !order.pageable) {
|
|
240
|
+
if (!byKeyOrder.pageable)
|
|
241
|
+
break;
|
|
242
|
+
order = byKeyOrder;
|
|
243
|
+
orderPagesRead = 0;
|
|
244
|
+
}
|
|
245
|
+
pagesRead += 1;
|
|
246
|
+
orderPagesRead += 1;
|
|
247
|
+
const pageNumber = orderPagesRead;
|
|
248
|
+
const pageSort = order.sort;
|
|
249
|
+
Object.assign(attempt, {
|
|
250
|
+
requestedPageSize,
|
|
251
|
+
pageNumber,
|
|
252
|
+
paddedPageReason,
|
|
253
|
+
notIn: false,
|
|
254
|
+
});
|
|
255
|
+
const readPage = (sort) => this.segmentReaderPort.listRecordIds({
|
|
256
|
+
...segmentQuery(inbox),
|
|
257
|
+
pageSize: requestedPageSize,
|
|
258
|
+
pageNumber,
|
|
259
|
+
sort,
|
|
260
|
+
});
|
|
261
|
+
let page;
|
|
262
|
+
try {
|
|
263
|
+
// eslint-disable-next-line no-await-in-loop
|
|
264
|
+
page = await (pagesRead === 1
|
|
265
|
+
? this.readWithInboxSort(logContext, inbox, attempt, inboxSort => orderFor(inboxSort).sort, readPage)
|
|
266
|
+
: readPage(pageSort));
|
|
267
|
+
if (pagesRead === 1)
|
|
268
|
+
order = orderFor(activeInboxSort(inbox, attempt));
|
|
269
|
+
}
|
|
270
|
+
catch (error) {
|
|
271
|
+
if (candidates.size === 0)
|
|
272
|
+
throw error;
|
|
273
|
+
// The candidates the earlier pages found are still good: dropping them would send an empty
|
|
274
|
+
// sync on every sweep of a slow agent.
|
|
275
|
+
this.logger('Warn', 'A later padded candidate page failed, keeping what earlier pages found', {
|
|
276
|
+
...logContext,
|
|
277
|
+
...attempt,
|
|
278
|
+
pagesRead,
|
|
279
|
+
...describeAgentFailure(error),
|
|
280
|
+
});
|
|
281
|
+
break;
|
|
282
|
+
}
|
|
283
|
+
(0, reconciliation_1.newCandidates)(page, knownSet, candidates, inbox.maxConcurrentRuns - candidates.size).forEach(id => candidates.add(id));
|
|
284
|
+
reachedEnd = page.length < requestedPageSize;
|
|
285
|
+
}
|
|
286
|
+
if (candidates.size === 0 && !reachedEnd) {
|
|
287
|
+
this.logger('Warn', 'The padded candidate read found no new record within its page cap', {
|
|
288
|
+
...logContext,
|
|
289
|
+
known: knownSet.size,
|
|
290
|
+
pagesRead,
|
|
291
|
+
paddedPageReason,
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
return {
|
|
295
|
+
items: [...candidates],
|
|
296
|
+
paddedPageReason,
|
|
297
|
+
requestedPageSize,
|
|
298
|
+
pagesRead,
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
/**
|
|
302
|
+
* A sort field renamed or out of the service account's reach must not stop the inbox from
|
|
303
|
+
* dispatching. Used on the not_in read and the first padded page only: a refused sort is refused
|
|
304
|
+
* on the first page already. The order is dropped for the rest of the candidate read only once the
|
|
305
|
+
* read without it succeeds, since a read failing both ways says nothing against the sort.
|
|
306
|
+
*/
|
|
307
|
+
async readWithInboxSort(logContext, inbox, attempt, toSort, read) {
|
|
308
|
+
const inboxSort = activeInboxSort(inbox, attempt);
|
|
309
|
+
const sort = toSort(inboxSort);
|
|
310
|
+
try {
|
|
311
|
+
return await read(sort);
|
|
312
|
+
}
|
|
313
|
+
catch (error) {
|
|
314
|
+
if (!inboxSort?.length || !(0, read_failure_1.mayBeSortRefusal)(error))
|
|
315
|
+
throw error;
|
|
316
|
+
let page;
|
|
317
|
+
try {
|
|
318
|
+
page = await read(toSort(undefined));
|
|
319
|
+
}
|
|
320
|
+
catch (unsortedError) {
|
|
321
|
+
Object.assign(attempt, { sortedReadFailure: { sort, ...describeAgentFailure(error) } });
|
|
322
|
+
throw unsortedError;
|
|
323
|
+
}
|
|
324
|
+
this.logger('Warn', 'The agent rejected the inbox sort, reading candidates without it', {
|
|
325
|
+
...logContext,
|
|
326
|
+
...attempt,
|
|
327
|
+
sort,
|
|
328
|
+
...describeAgentFailure(error),
|
|
329
|
+
});
|
|
330
|
+
Object.assign(attempt, { inboxSortDropped: true });
|
|
331
|
+
return page;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
async paddedPageReason(logContext, inbox, known) {
|
|
335
|
+
try {
|
|
336
|
+
return await this.segmentReaderPort.exclusionUnavailableReason({
|
|
337
|
+
collectionName: inbox.collectionName,
|
|
338
|
+
primaryKeys: inbox.primaryKeys,
|
|
339
|
+
user: inbox.user,
|
|
340
|
+
timezone: inbox.timezone,
|
|
341
|
+
liana: inbox.liana,
|
|
342
|
+
knownRecordCount: known.length,
|
|
343
|
+
});
|
|
344
|
+
}
|
|
345
|
+
catch (error) {
|
|
346
|
+
if (!(error instanceof errors_1.SegmentReadError))
|
|
347
|
+
throw error;
|
|
348
|
+
this.logger('Warn', 'Could not read the agent capabilities, padding the page instead', {
|
|
349
|
+
...logContext,
|
|
350
|
+
...describeAgentFailure(error),
|
|
351
|
+
});
|
|
352
|
+
return 'capabilities-unreadable';
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
exports.default = InboxPoll;
|
|
357
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5ib3gtcG9sbC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9hdXRvbWF0aW9uL2luYm94LXBvbGwudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7QUFVQSxpREFBdUY7QUFDdkYscURBWTBCO0FBQzFCLHNDQUEyRjtBQTJCM0YsU0FBUyxvQkFBb0IsQ0FBQyxLQUFjO0lBQzFDLE9BQU87UUFDTCxLQUFLLEVBQUUsSUFBQSw0QkFBbUIsRUFBQyxLQUFLLENBQUM7UUFDakMsVUFBVSxFQUFFLEtBQUssWUFBWSx5QkFBZ0IsQ0FBQyxDQUFDLENBQUMsS0FBSyxDQUFDLFVBQVUsQ0FBQyxDQUFDLENBQUMsU0FBUztRQUM1RSxVQUFVLEVBQUUsS0FBSyxZQUFZLHlCQUFnQixDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUMsV0FBVyxDQUFDLENBQUMsQ0FBQyxTQUFTO0tBQzlFLENBQUM7QUFDSixDQUFDO0FBT0QsU0FBUyxTQUFTLENBQ2hCLFNBQXdDLEVBQ3hDLG9CQUE2QjtJQUU3QixPQUFPLG9CQUFvQixDQUFDLENBQUMsQ0FBQyxTQUFTLENBQUMsQ0FBQyxDQUFDLFNBQVMsRUFBRSxLQUFLLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDO0FBQ25FLENBQUM7QUFFRCxnR0FBZ0c7QUFDaEcsU0FBUyxXQUFXLENBQ2xCLEtBQXFCLEVBQ3JCLGNBQTZDLEVBQzdDLG9CQUE2QjtJQUU3QixNQUFNLENBQUMsVUFBVSxDQUFDLEdBQUcsS0FBSyxDQUFDLFdBQVcsQ0FBQztJQUN2QyxNQUFNLEtBQUssR0FBRyxFQUFFLEtBQUssRUFBRSxVQUFVLEVBQUUsU0FBUyxFQUFFLElBQUksRUFBRSxDQUFDO0lBQ3JELE1BQU0sU0FBUyxHQUFHLFNBQVMsQ0FBQyxjQUFjLEVBQUUsb0JBQW9CLENBQUMsQ0FBQztJQUVsRSxJQUFJLEtBQUssQ0FBQyxXQUFXLENBQUMsTUFBTSxLQUFLLENBQUM7UUFBRSxPQUFPLEVBQUUsSUFBSSxFQUFFLFNB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
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export default class LeaseKeeper {
|
|
2
|
+
private holdsLease;
|
|
3
|
+
private leaseConfirmedAt;
|
|
4
|
+
recordBeat(held: boolean, now: number): {
|
|
5
|
+
roleChanged: boolean;
|
|
6
|
+
};
|
|
7
|
+
recordFailedBeat(now: number): {
|
|
8
|
+
trustLost: boolean;
|
|
9
|
+
};
|
|
10
|
+
isTrusted(now: number): boolean;
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=lease-keeper.d.ts.map
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
// Past this without a confirmed beat, the lease may have expired and gone to another instance.
|
|
4
|
+
// Judged at each dispatch rather than when a beat fails: a timed-out beat only fails 5 s late, and
|
|
5
|
+
// the next one 15 s after that, which would land past the 45 s lease.
|
|
6
|
+
const LEASE_TRUSTED_FOR_MS = 30000;
|
|
7
|
+
class LeaseKeeper {
|
|
8
|
+
constructor() {
|
|
9
|
+
this.leaseConfirmedAt = 0;
|
|
10
|
+
}
|
|
11
|
+
recordBeat(held, now) {
|
|
12
|
+
const roleChanged = held !== this.holdsLease;
|
|
13
|
+
this.holdsLease = held;
|
|
14
|
+
if (held)
|
|
15
|
+
this.leaseConfirmedAt = now;
|
|
16
|
+
return { roleChanged };
|
|
17
|
+
}
|
|
18
|
+
recordFailedBeat(now) {
|
|
19
|
+
if (!this.holdsLease || now - this.leaseConfirmedAt < LEASE_TRUSTED_FOR_MS) {
|
|
20
|
+
return { trustLost: false };
|
|
21
|
+
}
|
|
22
|
+
this.holdsLease = false;
|
|
23
|
+
return { trustLost: true };
|
|
24
|
+
}
|
|
25
|
+
isTrusted(now) {
|
|
26
|
+
return this.holdsLease === true && now - this.leaseConfirmedAt < LEASE_TRUSTED_FOR_MS;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
exports.default = LeaseKeeper;
|
|
30
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibGVhc2Uta2VlcGVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2F1dG9tYXRpb24vbGVhc2Uta2VlcGVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7O0FBQUEsK0ZBQStGO0FBQy9GLG1HQUFtRztBQUNuRyxzRUFBc0U7QUFDdEUsTUFBTSxvQkFBb0IsR0FBRyxLQUFNLENBQUM7QUFFcEMsTUFBcUIsV0FBVztJQUFoQztRQUVVLHFCQUFnQixHQUFHLENBQUMsQ0FBQztJQXdCL0IsQ0FBQztJQXRCQyxVQUFVLENBQUMsSUFBYSxFQUFFLEdBQVc7UUFDbkMsTUFBTSxXQUFXLEdBQUcsSUFBSSxLQUFLLElBQUksQ0FBQyxVQUFVLENBQUM7UUFDN0MsSUFBSSxDQUFDLFVBQVUsR0FBRyxJQUFJLENBQUM7UUFFdkIsSUFBSSxJQUFJO1lBQUUsSUFBSSxDQUFDLGdCQUFnQixHQUFHLEdBQUcsQ0FBQztRQUV0QyxPQUFPLEVBQUUsV0FBVyxFQUFFLENBQUM7SUFDekIsQ0FBQztJQUVELGdCQUFnQixDQUFDLEdBQVc7UUFDMUIsSUFBSSxDQUFDLElBQUksQ0FBQyxVQUFVLElBQUksR0FBRyxHQUFHLElBQUksQ0FBQyxnQkFBZ0IsR0FBRyxvQkFBb0IsRUFBRSxDQUFDO1lBQzNFLE9BQU8sRUFBRSxTQUFTLEVBQUUsS0FBSyxFQUFFLENBQUM7UUFDOUIsQ0FBQztRQUVELElBQUksQ0FBQyxVQUFVLEdBQUcsS0FBSyxDQUFDO1FBRXhCLE9BQU8sRUFBRSxTQUFTLEVBQUUsSUFBSSxFQUFFLENBQUM7SUFDN0IsQ0FBQztJQUVELFNBQVMsQ0FBQyxHQUFXO1FBQ25CLE9BQU8sSUFBSSxDQUFDLFVBQVUsS0FBSyxJQUFJLElBQUksR0FBRyxHQUFHLElBQUksQ0FBQyxnQkFBZ0IsR0FBRyxvQkFBb0IsQ0FBQztJQUN4RixDQUFDO0NBQ0Y7QUExQkQsOEJBMEJDIn0=
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { SegmentReadFailure } from '../types/automation';
|
|
2
|
+
export declare function toReadFailure(error: unknown): SegmentReadFailure;
|
|
3
|
+
export declare function mayBeOperatorRefusal(error: unknown): boolean;
|
|
4
|
+
export declare function mayBeSortRefusal(error: unknown): boolean;
|
|
5
|
+
//# sourceMappingURL=read-failure.d.ts.map
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.toReadFailure = toReadFailure;
|
|
4
|
+
exports.mayBeOperatorRefusal = mayBeOperatorRefusal;
|
|
5
|
+
exports.mayBeSortRefusal = mayBeSortRefusal;
|
|
6
|
+
const errors_1 = require("../errors");
|
|
7
|
+
const READ_FAILURE_REASONS = {
|
|
8
|
+
forbidden: 'agent-forbidden',
|
|
9
|
+
unreachable: 'agent-unreachable',
|
|
10
|
+
overloaded: 'segment-read-failed',
|
|
11
|
+
failed: 'segment-read-failed',
|
|
12
|
+
};
|
|
13
|
+
function toReadFailure(error) {
|
|
14
|
+
if (!(error instanceof errors_1.SegmentReadError))
|
|
15
|
+
return { reason: 'segment-read-failed' };
|
|
16
|
+
const { failure, httpStatus } = error;
|
|
17
|
+
const reason = READ_FAILURE_REASONS[failure];
|
|
18
|
+
return httpStatus === undefined ? { reason } : { reason, httpStatus };
|
|
19
|
+
}
|
|
20
|
+
// Some agents refuse an undeclared operator with a plain failure rather than a refusal, so any
|
|
21
|
+
// `failed` read may be one. An unreachable or overloaded agent rules it out, and padding then would
|
|
22
|
+
// evaluate the segment again on top of the read the agent may still be running; a forbidden read
|
|
23
|
+
// would refuse the padded one just the same.
|
|
24
|
+
function mayBeOperatorRefusal(error) {
|
|
25
|
+
return !(error instanceof errors_1.SegmentReadError) || error.failure === 'failed';
|
|
26
|
+
}
|
|
27
|
+
// An agent rejects a sort on a field it does not know with a 4xx, and one on a relation the service
|
|
28
|
+
// account cannot read with a refusal. Anything else says nothing about the sort, and a segment that
|
|
29
|
+
// timed out its database would only be evaluated twice.
|
|
30
|
+
function mayBeSortRefusal(error) {
|
|
31
|
+
if (!(error instanceof errors_1.SegmentReadError))
|
|
32
|
+
return false;
|
|
33
|
+
if (error.failure === 'forbidden')
|
|
34
|
+
return true;
|
|
35
|
+
return (error.failure === 'failed' &&
|
|
36
|
+
error.httpStatus !== undefined &&
|
|
37
|
+
error.httpStatus >= 400 &&
|
|
38
|
+
error.httpStatus < 500);
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmVhZC1mYWlsdXJlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2F1dG9tYXRpb24vcmVhZC1mYWlsdXJlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7O0FBWUEsc0NBT0M7QUFNRCxvREFFQztBQUtELDRDQVVDO0FBdkNELHNDQUE2QztBQUU3QyxNQUFNLG9CQUFvQixHQUE2RDtJQUNyRixTQUFTLEVBQUUsaUJBQWlCO0lBQzVCLFdBQVcsRUFBRSxtQkFBbUI7SUFDaEMsVUFBVSxFQUFFLHFCQUFxQjtJQUNqQyxNQUFNLEVBQUUscUJBQXFCO0NBQzlCLENBQUM7QUFFRixTQUFnQixhQUFhLENBQUMsS0FBYztJQUMxQyxJQUFJLENBQUMsQ0FBQyxLQUFLLFlBQVkseUJBQWdCLENBQUM7UUFBRSxPQUFPLEVBQUUsTUFBTSxFQUFFLHFCQUFxQixFQUFFLENBQUM7SUFFbkYsTUFBTSxFQUFFLE9BQU8sRUFBRSxVQUFVLEVBQUUsR0FBRyxLQUFLLENBQUM7SUFDdEMsTUFBTSxNQUFNLEdBQUcsb0JBQW9CLENBQUMsT0FBTyxDQUFDLENBQUM7SUFFN0MsT0FBTyxVQUFVLEtBQUssU0FBUyxDQUFDLENBQUMsQ0FBQyxFQUFFLE1BQU0sRUFBRSxDQUFDLENBQUMsQ0FBQyxFQUFFLE1BQU0sRUFBRSxVQUFVLEVBQUUsQ0FBQztBQUN4RSxDQUFDO0FBRUQsK0ZBQStGO0FBQy9GLG9HQUFvRztBQUNwRyxpR0FBaUc7QUFDakcsNkNBQTZDO0FBQzdDLFNBQWdCLG9CQUFvQixDQUFDLEtBQWM7SUFDakQsT0FBTyxDQUFDLENBQUMsS0FBSyxZQUFZLHlCQUFnQixDQUFDLElBQUksS0FBSyxDQUFDLE9BQU8sS0FBSyxRQUFRLENBQUM7QUFDNUUsQ0FBQztBQUVELG9HQUFvRztBQUNwRyxvR0FBb0c7QUFDcEcsd0RBQXdEO0FBQ3hELFNBQWdCLGdCQUFnQixDQUFDLEtBQWM7SUFDN0MsSUFBSSxDQUFDLENBQUMsS0FBSyxZQUFZLHlCQUFnQixDQUFDO1FBQUUsT0FBTyxLQUFLLENBQUM7SUFDdkQsSUFBSSxLQUFLLENBQUMsT0FBTyxLQUFLLFdBQVc7UUFBRSxPQUFPLElBQUksQ0FBQztJQUUvQyxPQUFPLENBQ0wsS0FBSyxDQUFDLE9BQU8sS0FBSyxRQUFRO1FBQzFCLEtBQUssQ0FBQyxVQUFVLEtBQUssU0FBUztRQUM5QixLQUFLLENBQUMsVUFBVSxJQUFJLEdBQUc7UUFDdkIsS0FBSyxDQUFDLFVBQVUsR0FBRyxHQUFHLENBQ3ZCLENBQUM7QUFDSixDQUFDIn0=
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { InboxAssignment } from '../types/automation';
|
|
2
|
+
export declare const MEMBERSHIP_CHUNK_SIZE = 50;
|
|
3
|
+
export declare const MAX_PADDED_PAGES = 5;
|
|
4
|
+
export declare function withUnknownState(assignments: InboxAssignment[]): InboxAssignment[];
|
|
5
|
+
export declare function reconcilable(assignments: InboxAssignment[]): InboxAssignment[];
|
|
6
|
+
export declare function withUnexpectedRunState(assignments: InboxAssignment[]): InboxAssignment[];
|
|
7
|
+
export declare function recordsToCheck(assignments: InboxAssignment[], reconcilableAssignments: InboxAssignment[]): string[];
|
|
8
|
+
export declare function isReadableRecordId(recordId: string, primaryKeys: string[]): boolean;
|
|
9
|
+
export declare function knownRecordIds(assignments: InboxAssignment[]): string[];
|
|
10
|
+
export declare function paddedPageSize(maxConcurrentRuns: number, knownRecordCount: number): number;
|
|
11
|
+
export declare function newCandidates(page: string[], known: ReadonlySet<string>, found: ReadonlySet<string>, room: number): string[];
|
|
12
|
+
export declare function chunk<T>(items: T[], size: number): T[][];
|
|
13
|
+
//# sourceMappingURL=reconciliation.d.ts.map
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MAX_PADDED_PAGES = exports.MEMBERSHIP_CHUNK_SIZE = void 0;
|
|
4
|
+
exports.withUnknownState = withUnknownState;
|
|
5
|
+
exports.reconcilable = reconcilable;
|
|
6
|
+
exports.withUnexpectedRunState = withUnexpectedRunState;
|
|
7
|
+
exports.recordsToCheck = recordsToCheck;
|
|
8
|
+
exports.isReadableRecordId = isReadableRecordId;
|
|
9
|
+
exports.knownRecordIds = knownRecordIds;
|
|
10
|
+
exports.paddedPageSize = paddedPageSize;
|
|
11
|
+
exports.newCandidates = newCandidates;
|
|
12
|
+
exports.chunk = chunk;
|
|
13
|
+
const record_id_1 = require("../record-id");
|
|
14
|
+
// One membership question per chunk, small enough that a `pk In (...)` stays a query an agent will
|
|
15
|
+
// accept whatever its datasource.
|
|
16
|
+
exports.MEMBERSHIP_CHUNK_SIZE = 50;
|
|
17
|
+
// Ceiling on the padded fallback page. The padding grows with the backlog while the agent read is
|
|
18
|
+
// bounded by the client's ten-second timeout, so past some size the page stops being served at all —
|
|
19
|
+
// worse than a partial page that finds fewer candidates.
|
|
20
|
+
const MAX_CANDIDATE_PAGE_SIZE = 500;
|
|
21
|
+
// Records waiting on a person stay known for as long as nobody handles them, so one padded page can
|
|
22
|
+
// hold nothing but them. Bounds the reads a sweep makes on the customer's agent to walk past them.
|
|
23
|
+
exports.MAX_PADDED_PAGES = 5;
|
|
24
|
+
const RECONCILABLE_ASSIGNMENT_STATES = new Set([
|
|
25
|
+
'done',
|
|
26
|
+
'canceled',
|
|
27
|
+
'auto-canceled',
|
|
28
|
+
]);
|
|
29
|
+
const TERMINAL_RUN_STATES = new Set(['finished', 'aborted']);
|
|
30
|
+
const LIVE_RUN_STATES = new Set(['started', 'pending', 'loading']);
|
|
31
|
+
const OPEN_ASSIGNMENT_STATES = new Set(['todo', 'doing']);
|
|
32
|
+
const isTerminalRun = (runState) => runState != null && TERMINAL_RUN_STATES.has(runState);
|
|
33
|
+
const isLiveRun = (runState) => runState != null && LIVE_RUN_STATES.has(runState);
|
|
34
|
+
function withUnknownState(assignments) {
|
|
35
|
+
return assignments.filter(({ state }) => !RECONCILABLE_ASSIGNMENT_STATES.has(state) && !OPEN_ASSIGNMENT_STATES.has(state));
|
|
36
|
+
}
|
|
37
|
+
// The orchestrator keeps the row of an ended run `doing` while its record is still in the
|
|
38
|
+
// segment, so that record is only released once it is seen leaving. An assignment with no
|
|
39
|
+
// workflowRunId was made by a person, not the orchestrator, which binds the run first: there is
|
|
40
|
+
// nothing to reconcile, so it stays out of the report — the record stays known, and no automated
|
|
41
|
+
// run is ever started over the human's work. Keyed on the run's presence, not its state: the
|
|
42
|
+
// orchestrator binds a run before an automated assignment and its `runState` is NOT NULL, so a
|
|
43
|
+
// bound run with a null state is a contract break, not a human assignment — it belongs here so
|
|
44
|
+
// `withUnexpectedRunState` surfaces it instead of dropping it silently.
|
|
45
|
+
function reconcilable(assignments) {
|
|
46
|
+
return assignments.filter(({ state, runState, workflowRunId }) => workflowRunId != null &&
|
|
47
|
+
(RECONCILABLE_ASSIGNMENT_STATES.has(state) || (state === 'doing' && isTerminalRun(runState))));
|
|
48
|
+
}
|
|
49
|
+
// A run state this executor cannot place: a non-null one it predates, or null on a bound run —
|
|
50
|
+
// which the orchestrator's schema (NOT NULL runState behind a cascading FK) should never emit, so
|
|
51
|
+
// it is a contract break worth surfacing until a fix lands. The human assignment never reaches
|
|
52
|
+
// here — it has no workflowRunId, so warning forever would be noise it could never resolve.
|
|
53
|
+
function withUnexpectedRunState(assignments) {
|
|
54
|
+
return assignments.filter(({ runState }) => !isTerminalRun(runState) && !isLiveRun(runState));
|
|
55
|
+
}
|
|
56
|
+
// A record whose run is still going is not judged yet, whatever its assignments say. Judged per
|
|
57
|
+
// record rather than per assignment: nothing in the contract says a record holds only one, and one
|
|
58
|
+
// terminal assignment must not speak for a sibling whose run is still alive.
|
|
59
|
+
function recordsToCheck(assignments, reconcilableAssignments) {
|
|
60
|
+
const liveRecords = new Set(assignments.filter(({ runState }) => !isTerminalRun(runState)).map(a => a.recordId));
|
|
61
|
+
return [
|
|
62
|
+
...new Set(reconcilableAssignments
|
|
63
|
+
.filter(({ recordId }) => !liveRecords.has(recordId))
|
|
64
|
+
.map(({ recordId }) => recordId)),
|
|
65
|
+
];
|
|
66
|
+
}
|
|
67
|
+
// A packed id that does not split into as many parts as the key has columns cannot be asked about —
|
|
68
|
+
// the agent has the same limitation in `IdUtils.packId`.
|
|
69
|
+
function isReadableRecordId(recordId, primaryKeys) {
|
|
70
|
+
return primaryKeys.length === 1 || (0, record_id_1.deserializeRecordId)(recordId).length === primaryKeys.length;
|
|
71
|
+
}
|
|
72
|
+
function knownRecordIds(assignments) {
|
|
73
|
+
return [...new Set(assignments.map(({ recordId }) => recordId))];
|
|
74
|
+
}
|
|
75
|
+
function paddedPageSize(maxConcurrentRuns, knownRecordCount) {
|
|
76
|
+
return Math.min(maxConcurrentRuns + knownRecordCount, MAX_CANDIDATE_PAGE_SIZE);
|
|
77
|
+
}
|
|
78
|
+
function newCandidates(page, known, found, room) {
|
|
79
|
+
return page.filter(recordId => !known.has(recordId) && !found.has(recordId)).slice(0, room);
|
|
80
|
+
}
|
|
81
|
+
function chunk(items, size) {
|
|
82
|
+
const chunks = [];
|
|
83
|
+
for (let index = 0; index < items.length; index += size) {
|
|
84
|
+
chunks.push(items.slice(index, index + size));
|
|
85
|
+
}
|
|
86
|
+
return chunks;
|
|
87
|
+
}
|
|
88
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmVjb25jaWxpYXRpb24uanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvYXV0b21hdGlvbi9yZWNvbmNpbGlhdGlvbi50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFtQ0EsNENBSUM7QUFVRCxvQ0FNQztBQU1ELHdEQUVDO0FBS0Qsd0NBZUM7QUFJRCxnREFFQztBQUVELHdDQUVDO0FBRUQsd0NBRUM7QUFFRCxzQ0FPQztBQUVELHNCQVFDO0FBbEhELDRDQUFtRDtBQUVuRCxtR0FBbUc7QUFDbkcsa0NBQWtDO0FBQ3JCLFFBQUEscUJBQXFCLEdBQUcsRUFBRSxDQUFDO0FBRXhDLGtHQUFrRztBQUNsRyxxR0FBcUc7QUFDckcseURBQXlEO0FBQ3pELE1BQU0sdUJBQXVCLEdBQUcsR0FBRyxDQUFDO0FBRXBDLG9HQUFvRztBQUNwRyxtR0FBbUc7QUFDdEYsUUFBQSxnQkFBZ0IsR0FBRyxDQUFDLENBQUM7QUFFbEMsTUFBTSw4QkFBOEIsR0FBd0IsSUFBSSxHQUFHLENBQUM7SUFDbEUsTUFBTTtJQUNOLFVBQVU7SUFDVixlQUFlO0NBQ2hCLENBQUMsQ0FBQztBQUVILE1BQU0sbUJBQW1CLEdBQXdCLElBQUksR0FBRyxDQUFDLENBQUMsVUFBVSxFQUFFLFNBQVMsQ0FBQyxDQUFDLENBQUM7QUFFbEYsTUFBTSxlQUFlLEdBQXdCLElBQUksR0FBRyxDQUFDLENBQUMsU0FBUyxFQUFFLFNBQVMsRUFBRSxTQUFTLENBQUMsQ0FBQyxDQUFDO0FBRXhGLE1BQU0sc0JBQXNCLEdBQXdCLElBQUksR0FBRyxDQUFDLENBQUMsTUFBTSxFQUFFLE9BQU8sQ0FBQyxDQUFDLENBQUM7QUFFL0UsTUFBTSxhQUFhLEdBQUcsQ0FBQyxRQUFtQyxFQUFXLEVBQUUsQ0FDckUsUUFBUSxJQUFJLElBQUksSUFBSSxtQkFBbUIsQ0FBQyxHQUFHLENBQUMsUUFBUSxDQUFDLENBQUM7QUFFeEQsTUFBTSxTQUFTLEdBQUcsQ0FBQyxRQUFtQyxFQUFXLEVBQUUs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
|
|
@@ -10,6 +10,8 @@ export interface WorkflowExecutor {
|
|
|
10
10
|
export interface WorkflowExecutorTuningOptions {
|
|
11
11
|
/** Interval in seconds at which the executor polls the orchestrator for pending steps. */
|
|
12
12
|
pollingIntervalS?: number;
|
|
13
|
+
/** Interval in seconds at which automated inboxes are swept. Defaults to 300. */
|
|
14
|
+
automationPollingIntervalS?: number;
|
|
13
15
|
/** Per-step execution timeout in seconds. */
|
|
14
16
|
stepTimeoutS?: number;
|
|
15
17
|
/** Max duration in seconds of a single AI provider invocation. */
|