@microfox/ai-worker 1.0.4 → 1.0.5
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/CHANGELOG.md +8 -0
- package/README.md +22 -0
- package/dist/chainMapDefaults.d.mts +21 -0
- package/dist/chainMapDefaults.d.ts +21 -0
- package/dist/chainMapDefaults.js +59 -0
- package/dist/chainMapDefaults.js.map +1 -0
- package/dist/chainMapDefaults.mjs +10 -0
- package/dist/chainMapDefaults.mjs.map +1 -0
- package/dist/chunk-BCRJIFKB.mjs +9 -0
- package/dist/chunk-BCRJIFKB.mjs.map +1 -0
- package/dist/{chunk-72XGFZCE.mjs → chunk-CILTGUUQ.mjs} +14 -3
- package/dist/chunk-CILTGUUQ.mjs.map +1 -0
- package/dist/{chunk-7LQNS2SG.mjs → chunk-QHX55IML.mjs} +442 -56
- package/dist/chunk-QHX55IML.mjs.map +1 -0
- package/dist/chunk-SQB5FQCZ.mjs +21 -0
- package/dist/chunk-SQB5FQCZ.mjs.map +1 -0
- package/dist/{chunk-AOXGONGI.mjs → chunk-T7DRPKR6.mjs} +7 -5
- package/dist/chunk-T7DRPKR6.mjs.map +1 -0
- package/dist/chunk-XCKWV2WZ.mjs +34 -0
- package/dist/chunk-XCKWV2WZ.mjs.map +1 -0
- package/dist/chunk-ZW4PNCDH.mjs +17 -0
- package/dist/chunk-ZW4PNCDH.mjs.map +1 -0
- package/dist/client.d.mts +148 -2
- package/dist/client.d.ts +148 -2
- package/dist/client.js +13 -2
- package/dist/client.js.map +1 -1
- package/dist/client.mjs +1 -1
- package/dist/handler.d.mts +121 -23
- package/dist/handler.d.ts +121 -23
- package/dist/handler.js +450 -58
- package/dist/handler.js.map +1 -1
- package/dist/handler.mjs +5 -2
- package/dist/hitlConfig.d.mts +46 -0
- package/dist/hitlConfig.d.ts +46 -0
- package/dist/hitlConfig.js +33 -0
- package/dist/hitlConfig.js.map +1 -0
- package/dist/hitlConfig.mjs +8 -0
- package/dist/hitlConfig.mjs.map +1 -0
- package/dist/index.d.mts +23 -4
- package/dist/index.d.ts +23 -4
- package/dist/index.js +575 -74
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +78 -20
- package/dist/index.mjs.map +1 -1
- package/dist/queue-B5n6YVQV.d.ts +306 -0
- package/dist/queue-DaR2UuZi.d.mts +306 -0
- package/dist/queue.d.mts +3 -0
- package/dist/queue.d.ts +3 -0
- package/dist/queue.js +47 -0
- package/dist/queue.js.map +1 -0
- package/dist/queue.mjs +12 -0
- package/dist/queue.mjs.map +1 -0
- package/dist/queueInputEnvelope.d.mts +31 -0
- package/dist/queueInputEnvelope.d.ts +31 -0
- package/dist/queueInputEnvelope.js +42 -0
- package/dist/queueInputEnvelope.js.map +1 -0
- package/dist/queueInputEnvelope.mjs +10 -0
- package/dist/queueInputEnvelope.mjs.map +1 -0
- package/dist/queueJobStore.d.mts +3 -2
- package/dist/queueJobStore.d.ts +3 -2
- package/dist/queueJobStore.js +6 -4
- package/dist/queueJobStore.js.map +1 -1
- package/dist/queueJobStore.mjs +1 -1
- package/package.json +7 -2
- package/dist/chunk-72XGFZCE.mjs.map +0 -1
- package/dist/chunk-7LQNS2SG.mjs.map +0 -1
- package/dist/chunk-AOXGONGI.mjs.map +0 -1
- package/dist/client-BqSJQ9mZ.d.mts +0 -183
- package/dist/client-BqSJQ9mZ.d.ts +0 -183
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
import { HitlStepConfig } from './hitlConfig.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Smart retry configuration for workers.
|
|
5
|
+
* Retries execute in-process (same Lambda invocation) so error context is preserved
|
|
6
|
+
* between attempts and the job remains in `running` state throughout.
|
|
7
|
+
*/
|
|
8
|
+
interface RetryContext {
|
|
9
|
+
/** Current attempt number (1-indexed). 2 = first retry, 3 = second retry, etc. */
|
|
10
|
+
attempt: number;
|
|
11
|
+
/** Total max attempts configured. */
|
|
12
|
+
maxAttempts: number;
|
|
13
|
+
/** Error from the previous attempt — use to self-correct (e.g. inject into prompt). */
|
|
14
|
+
lastError: {
|
|
15
|
+
message: string;
|
|
16
|
+
name: string;
|
|
17
|
+
stack?: string;
|
|
18
|
+
/** HTTP status code or error code if present on the error object. */
|
|
19
|
+
code?: string | number;
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
type BuiltInRetryPattern = 'rate-limit' | 'json-parse' | 'overloaded' | 'server-error';
|
|
23
|
+
interface CustomRetryPattern {
|
|
24
|
+
/** Regex test against error.message, or a predicate receiving the full error object. */
|
|
25
|
+
match: RegExp | ((err: Error & Record<string, any>) => boolean);
|
|
26
|
+
/** Delay in ms before the retry. A function receives the retry attempt number (1 = first retry). */
|
|
27
|
+
delayMs?: number | ((attempt: number) => number);
|
|
28
|
+
/** When true, populates ctx.retryContext.lastError so the handler can self-correct. Built-ins set this per pattern. */
|
|
29
|
+
injectContext?: boolean;
|
|
30
|
+
}
|
|
31
|
+
type RetryPattern = BuiltInRetryPattern | CustomRetryPattern;
|
|
32
|
+
interface SmartRetryConfig {
|
|
33
|
+
/** Maximum total attempts, including the first (default: 3). */
|
|
34
|
+
maxAttempts?: number;
|
|
35
|
+
/** Error patterns that trigger a retry. Non-matching errors fail immediately. */
|
|
36
|
+
on: RetryPattern[];
|
|
37
|
+
}
|
|
38
|
+
interface MatchResult {
|
|
39
|
+
matched: boolean;
|
|
40
|
+
delayMs: number;
|
|
41
|
+
injectContext: boolean;
|
|
42
|
+
}
|
|
43
|
+
declare function matchesRetryPattern(err: Error, patterns: RetryPattern[],
|
|
44
|
+
/** 1-indexed retry number (1 = first retry, i.e. second execution). */
|
|
45
|
+
attempt: number): MatchResult;
|
|
46
|
+
/**
|
|
47
|
+
* Executes `fn` with in-process smart retry.
|
|
48
|
+
* `fn` receives `RetryContext | undefined` (undefined on first attempt).
|
|
49
|
+
* `TokenBudgetExceededError` is never retried regardless of config.
|
|
50
|
+
*/
|
|
51
|
+
declare function executeWithRetry<T>(fn: (retryCtx: RetryContext | undefined) => Promise<T>, config: SmartRetryConfig, onRetry?: (retryCtx: RetryContext, delayMs: number) => void): Promise<T>;
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Queue definition and context types for worker queues.
|
|
55
|
+
*
|
|
56
|
+
* ## Human-in-the-loop (HITL) pause / resume
|
|
57
|
+
*
|
|
58
|
+
* **Pause (`awaiting_approval`)** — After a step completes, if the **next** step has
|
|
59
|
+
* `requiresApproval: true`, the runtime calls the step's `chain` function (if any),
|
|
60
|
+
* stores the result as the pending input for that step, marks it `awaiting_approval`,
|
|
61
|
+
* and does **not** dispatch the worker until a human approves.
|
|
62
|
+
*
|
|
63
|
+
* **Resume (`POST .../approve`)** — The app route calls `dispatchWorker` for that step
|
|
64
|
+
* with `__hitlInput` (reviewer form payload) attached. The `wrapHandlerForQueue` runtime
|
|
65
|
+
* calls the step's `resume` function (or merges inputs by default) to produce the final
|
|
66
|
+
* domain input, then strips all envelope keys before the user handler receives it.
|
|
67
|
+
*
|
|
68
|
+
* Workers **do not** need to accept any `__workerQueue` / `__hitlPending` / `__hitlInput`
|
|
69
|
+
* keys in their Zod schemas — the runtime strips them automatically.
|
|
70
|
+
*/
|
|
71
|
+
/** Output from one completed step, available to subsequent steps via ChainContext / HitlResumeContext. */
|
|
72
|
+
interface QueueStepOutput {
|
|
73
|
+
stepIndex: number;
|
|
74
|
+
workerId: string;
|
|
75
|
+
output: unknown;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Context passed to a step's {@link WorkerQueueStep.chain} function when the queue
|
|
79
|
+
* advances normally (previous step completed without HITL).
|
|
80
|
+
*/
|
|
81
|
+
interface ChainContext {
|
|
82
|
+
/** Original input passed to `dispatchQueue` (the first step's input). */
|
|
83
|
+
initialInput: unknown;
|
|
84
|
+
/**
|
|
85
|
+
* Outputs from all previous steps in order.
|
|
86
|
+
* `previousOutputs[0]` = step 0 output; last entry = most recent.
|
|
87
|
+
*/
|
|
88
|
+
previousOutputs: QueueStepOutput[];
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Context passed to a step's {@link WorkerQueueStep.resume} function when a human has
|
|
92
|
+
* approved the HITL pause and the step is being re-dispatched.
|
|
93
|
+
*
|
|
94
|
+
* @template T - Shape of the reviewer / UI form payload.
|
|
95
|
+
* Derive it from your `hitl.inputSchema` if defined:
|
|
96
|
+
* `HitlResumeContext<z.infer<typeof reviewerSchema>>`.
|
|
97
|
+
*/
|
|
98
|
+
interface HitlResumeContext<T = unknown> {
|
|
99
|
+
/** Original input passed to `dispatchQueue` (the first step's input). */
|
|
100
|
+
initialInput: unknown;
|
|
101
|
+
/**
|
|
102
|
+
* Outputs from all previous steps in order.
|
|
103
|
+
* `previousOutputs[0]` = step 0 output; last entry = most recent.
|
|
104
|
+
*/
|
|
105
|
+
previousOutputs: QueueStepOutput[];
|
|
106
|
+
/** Reviewer / UI form payload submitted via `POST .../approve` (`input` body field). */
|
|
107
|
+
reviewerInput: T;
|
|
108
|
+
/**
|
|
109
|
+
* The computed next-step input that was stored pending approval (domain fields only;
|
|
110
|
+
* all `__workerQueue` / `__hitlPending` / `hitl` envelope keys are stripped).
|
|
111
|
+
* This is what the `chain` function produced before the step was paused.
|
|
112
|
+
*/
|
|
113
|
+
pendingInput: Record<string, unknown>;
|
|
114
|
+
}
|
|
115
|
+
/** Internal envelope keys injected by the queue runtime. Never passed to user handlers. */
|
|
116
|
+
declare const QUEUE_ORCHESTRATION_KEYS: readonly ["__workerQueue", "__hitlInput", "__hitlDecision", "__hitlPending", "hitl"];
|
|
117
|
+
interface WorkerQueueStep {
|
|
118
|
+
/** Worker ID for this step. Must match an existing registered worker. */
|
|
119
|
+
workerId: string;
|
|
120
|
+
/**
|
|
121
|
+
* Optional delay (in seconds) before this step is executed.
|
|
122
|
+
* Implemented via SQS DelaySeconds (max 900).
|
|
123
|
+
*/
|
|
124
|
+
delaySeconds?: number;
|
|
125
|
+
/**
|
|
126
|
+
* Called when the queue advances to this step after the previous step completes
|
|
127
|
+
* normally (no HITL). Receives a {@link ChainContext} and must return the input
|
|
128
|
+
* object for this worker.
|
|
129
|
+
*
|
|
130
|
+
* **Built-in shortcuts** (use a string instead of a function):
|
|
131
|
+
* - `'passthrough'` — pass the previous step's output directly as this step's input.
|
|
132
|
+
* - `'continueFromPrevious'` — extract `{ current, history }` from the previous
|
|
133
|
+
* output and build a `{ mode: 'continue', ... }` payload (useful for multi-round sessions).
|
|
134
|
+
*
|
|
135
|
+
* When omitted, the previous step's output is used as-is (equivalent to `'passthrough'`).
|
|
136
|
+
*
|
|
137
|
+
* @example
|
|
138
|
+
* ```ts
|
|
139
|
+
* chain: (ctx) => ({
|
|
140
|
+
* mode: 'review' as const,
|
|
141
|
+
* data: ctx.previousOutputs[ctx.previousOutputs.length - 1]?.output,
|
|
142
|
+
* }),
|
|
143
|
+
* ```
|
|
144
|
+
*/
|
|
145
|
+
chain?: ((ctx: ChainContext) => unknown) | 'passthrough' | 'continueFromPrevious';
|
|
146
|
+
/**
|
|
147
|
+
* Called when this step resumes after a human approves a HITL pause.
|
|
148
|
+
* Receives a {@link HitlResumeContext} with the reviewer's form payload and the
|
|
149
|
+
* stored pending input, and must return the final domain input for this worker.
|
|
150
|
+
*
|
|
151
|
+
* When omitted, a shallow merge of `pendingInput` + `reviewerInput` is used as the
|
|
152
|
+
* default (suitable for simple cases where reviewer fields override pending fields).
|
|
153
|
+
*
|
|
154
|
+
* @example
|
|
155
|
+
* ```ts
|
|
156
|
+
* resume: (ctx: HitlResumeContext<ReviewerSchema>) => ({
|
|
157
|
+
* ...ctx.pendingInput,
|
|
158
|
+
* overriddenField: ctx.reviewerInput.overriddenField,
|
|
159
|
+
* }),
|
|
160
|
+
* ```
|
|
161
|
+
*/
|
|
162
|
+
resume?: (ctx: HitlResumeContext<any>) => unknown;
|
|
163
|
+
/**
|
|
164
|
+
* When `true`, queue execution pauses before dispatching this step.
|
|
165
|
+
* The step waits until a human calls `POST .../approve` (or `POST .../reject`).
|
|
166
|
+
* Define a `resume` function to control how the reviewer's payload is merged with
|
|
167
|
+
* the pending input.
|
|
168
|
+
*/
|
|
169
|
+
requiresApproval?: boolean;
|
|
170
|
+
/**
|
|
171
|
+
* Optional HITL UI/metadata for this step (consumed by app UI and tooling).
|
|
172
|
+
* Use {@link defineHitlConfig} for type-safe authoring.
|
|
173
|
+
* The worker runtime uses only `requiresApproval` — this field is UI-only.
|
|
174
|
+
*/
|
|
175
|
+
hitl?: HitlStepConfig;
|
|
176
|
+
/**
|
|
177
|
+
* Smart retry configuration for this queue step.
|
|
178
|
+
* Overrides any retry config on the worker definition for this step only.
|
|
179
|
+
* Retries run in-process (same Lambda invocation); ctx.retryContext is populated
|
|
180
|
+
* on each retry so the handler can self-correct (e.g. inject error into prompt).
|
|
181
|
+
*
|
|
182
|
+
* @example
|
|
183
|
+
* ```ts
|
|
184
|
+
* retry: { maxAttempts: 3, on: ['rate-limit', 'json-parse'] }
|
|
185
|
+
* ```
|
|
186
|
+
*/
|
|
187
|
+
retry?: SmartRetryConfig;
|
|
188
|
+
/**
|
|
189
|
+
* When defined, evaluated after each run of this step to decide whether to
|
|
190
|
+
* re-run it (another iteration) instead of advancing to the next step.
|
|
191
|
+
*
|
|
192
|
+
* Combine with `requiresApproval: true` for HITL-gated loops where the
|
|
193
|
+
* reviewer's decision (e.g. a `continueLoop` field in their payload)
|
|
194
|
+
* controls whether another round starts.
|
|
195
|
+
*
|
|
196
|
+
* @example
|
|
197
|
+
* ```ts
|
|
198
|
+
* // Loop continues until the worker returns { finalized: true }
|
|
199
|
+
* loop: {
|
|
200
|
+
* shouldContinue: ({ output }) => !(output as { finalized?: boolean }).finalized,
|
|
201
|
+
* maxIterations: 20,
|
|
202
|
+
* }
|
|
203
|
+
* ```
|
|
204
|
+
*/
|
|
205
|
+
loop?: {
|
|
206
|
+
/** Return true to run this step again; false to advance to the next step. */
|
|
207
|
+
shouldContinue: (ctx: LoopContext) => boolean | Promise<boolean>;
|
|
208
|
+
/**
|
|
209
|
+
* Hard cap on total iterations (including the first run).
|
|
210
|
+
* Prevents runaway loops. Default: 50.
|
|
211
|
+
*/
|
|
212
|
+
maxIterations?: number;
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
interface WorkerQueueConfig<InitialInput = any, StepOutput = any> {
|
|
216
|
+
/** Stable queue identifier, e.g. `"cost-review"`. */
|
|
217
|
+
id: string;
|
|
218
|
+
/** Ordered list of steps forming the queue pipeline. */
|
|
219
|
+
steps: WorkerQueueStep[];
|
|
220
|
+
/**
|
|
221
|
+
* Optional schedule for the queue (cron or rate expression).
|
|
222
|
+
* When set, the CLI generates a queue-starter Lambda triggered by this schedule.
|
|
223
|
+
* @example `'cron(0 3 * * ? *)'` — daily at 03:00 UTC.
|
|
224
|
+
*/
|
|
225
|
+
schedule?: string | {
|
|
226
|
+
rate: string;
|
|
227
|
+
enabled?: boolean;
|
|
228
|
+
input?: Record<string, any>;
|
|
229
|
+
};
|
|
230
|
+
_initialInputType?: InitialInput;
|
|
231
|
+
_stepOutputType?: StepOutput;
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Context passed to a step's {@link WorkerQueueStep.loop | loop.shouldContinue} function
|
|
235
|
+
* after each iteration of a looping step.
|
|
236
|
+
*/
|
|
237
|
+
interface LoopContext {
|
|
238
|
+
/** Output returned by the worker for this iteration. */
|
|
239
|
+
output: unknown;
|
|
240
|
+
/** Step definition index (stable; does not change across iterations). */
|
|
241
|
+
stepIndex: number;
|
|
242
|
+
/** 0-based count of how many times this step has already run (0 = first run). */
|
|
243
|
+
iterationCount: number;
|
|
244
|
+
/** Original input passed to `dispatchQueue`. */
|
|
245
|
+
initialInput: unknown;
|
|
246
|
+
/** Outputs from all previous steps (and previous iterations of this step). */
|
|
247
|
+
previousOutputs: QueueStepOutput[];
|
|
248
|
+
}
|
|
249
|
+
/**
|
|
250
|
+
* Queue execution context embedded into job input so queue-aware wrappers
|
|
251
|
+
* know their position in the pipeline. Injected automatically by the runtime.
|
|
252
|
+
*/
|
|
253
|
+
interface WorkerQueueContext<InitialInput = any> {
|
|
254
|
+
id: string;
|
|
255
|
+
/** Queue definition step index — used to look up chain/resume/loop config. Stays fixed across loop iterations. */
|
|
256
|
+
stepIndex: number;
|
|
257
|
+
/**
|
|
258
|
+
* Actual array position of this step in the job's steps[] store.
|
|
259
|
+
* Differs from stepIndex when the same step has looped multiple times
|
|
260
|
+
* (each iteration is appended as a new entry). Defaults to stepIndex when absent.
|
|
261
|
+
*/
|
|
262
|
+
arrayStepIndex?: number;
|
|
263
|
+
initialInput: InitialInput;
|
|
264
|
+
/** Queue job ID for progress tracking (same as first worker's jobId). */
|
|
265
|
+
queueJobId?: string;
|
|
266
|
+
/** How many times this step has already run (used by looping steps). */
|
|
267
|
+
iterationCount?: number;
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* Repeat a step definition `count` times, calling `factory(index)` for each repetition.
|
|
271
|
+
* Eliminates copy-paste for multi-round HITL workflows.
|
|
272
|
+
*
|
|
273
|
+
* @example
|
|
274
|
+
* ```ts
|
|
275
|
+
* const queue = defineWorkerQueue({
|
|
276
|
+
* id: 'multi-review',
|
|
277
|
+
* steps: [
|
|
278
|
+
* { workerId: 'ingest' },
|
|
279
|
+
* ...repeatStep(3, (i) => ({
|
|
280
|
+
* workerId: 'review',
|
|
281
|
+
* chain: chainFromPrev,
|
|
282
|
+
* resume: resumeFromHitl,
|
|
283
|
+
* requiresApproval: true,
|
|
284
|
+
* hitl: defineHitlConfig({ taskKey: `review-r${i + 1}`, ui: { type: 'schema-form', title: `Round ${i + 1}` } }),
|
|
285
|
+
* })),
|
|
286
|
+
* ],
|
|
287
|
+
* });
|
|
288
|
+
* ```
|
|
289
|
+
*/
|
|
290
|
+
declare function repeatStep(count: number, factory: (index: number) => WorkerQueueStep): WorkerQueueStep[];
|
|
291
|
+
/**
|
|
292
|
+
* Identity helper for defining worker queues in `.queue.ts` files.
|
|
293
|
+
* Use `satisfies WorkerQueueConfig<InitialInput, StepOutput>` for phantom-type docs.
|
|
294
|
+
*
|
|
295
|
+
* @example
|
|
296
|
+
* ```ts
|
|
297
|
+
* const q = defineWorkerQueue({
|
|
298
|
+
* id: 'my-queue',
|
|
299
|
+
* steps: [ ... ],
|
|
300
|
+
* }) satisfies WorkerQueueConfig<MyInit, MyOutput>;
|
|
301
|
+
* export default q;
|
|
302
|
+
* ```
|
|
303
|
+
*/
|
|
304
|
+
declare function defineWorkerQueue<T extends WorkerQueueConfig>(config: T): T;
|
|
305
|
+
|
|
306
|
+
export { type BuiltInRetryPattern as B, type CustomRetryPattern as C, type HitlResumeContext as H, type LoopContext as L, type QueueStepOutput as Q, type RetryContext as R, type SmartRetryConfig as S, type WorkerQueueStep as W, type RetryPattern as a, type ChainContext as b, QUEUE_ORCHESTRATION_KEYS as c, type WorkerQueueConfig as d, type WorkerQueueContext as e, defineWorkerQueue as f, executeWithRetry as g, matchesRetryPattern as m, repeatStep as r };
|
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
import { HitlStepConfig } from './hitlConfig.mjs';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Smart retry configuration for workers.
|
|
5
|
+
* Retries execute in-process (same Lambda invocation) so error context is preserved
|
|
6
|
+
* between attempts and the job remains in `running` state throughout.
|
|
7
|
+
*/
|
|
8
|
+
interface RetryContext {
|
|
9
|
+
/** Current attempt number (1-indexed). 2 = first retry, 3 = second retry, etc. */
|
|
10
|
+
attempt: number;
|
|
11
|
+
/** Total max attempts configured. */
|
|
12
|
+
maxAttempts: number;
|
|
13
|
+
/** Error from the previous attempt — use to self-correct (e.g. inject into prompt). */
|
|
14
|
+
lastError: {
|
|
15
|
+
message: string;
|
|
16
|
+
name: string;
|
|
17
|
+
stack?: string;
|
|
18
|
+
/** HTTP status code or error code if present on the error object. */
|
|
19
|
+
code?: string | number;
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
type BuiltInRetryPattern = 'rate-limit' | 'json-parse' | 'overloaded' | 'server-error';
|
|
23
|
+
interface CustomRetryPattern {
|
|
24
|
+
/** Regex test against error.message, or a predicate receiving the full error object. */
|
|
25
|
+
match: RegExp | ((err: Error & Record<string, any>) => boolean);
|
|
26
|
+
/** Delay in ms before the retry. A function receives the retry attempt number (1 = first retry). */
|
|
27
|
+
delayMs?: number | ((attempt: number) => number);
|
|
28
|
+
/** When true, populates ctx.retryContext.lastError so the handler can self-correct. Built-ins set this per pattern. */
|
|
29
|
+
injectContext?: boolean;
|
|
30
|
+
}
|
|
31
|
+
type RetryPattern = BuiltInRetryPattern | CustomRetryPattern;
|
|
32
|
+
interface SmartRetryConfig {
|
|
33
|
+
/** Maximum total attempts, including the first (default: 3). */
|
|
34
|
+
maxAttempts?: number;
|
|
35
|
+
/** Error patterns that trigger a retry. Non-matching errors fail immediately. */
|
|
36
|
+
on: RetryPattern[];
|
|
37
|
+
}
|
|
38
|
+
interface MatchResult {
|
|
39
|
+
matched: boolean;
|
|
40
|
+
delayMs: number;
|
|
41
|
+
injectContext: boolean;
|
|
42
|
+
}
|
|
43
|
+
declare function matchesRetryPattern(err: Error, patterns: RetryPattern[],
|
|
44
|
+
/** 1-indexed retry number (1 = first retry, i.e. second execution). */
|
|
45
|
+
attempt: number): MatchResult;
|
|
46
|
+
/**
|
|
47
|
+
* Executes `fn` with in-process smart retry.
|
|
48
|
+
* `fn` receives `RetryContext | undefined` (undefined on first attempt).
|
|
49
|
+
* `TokenBudgetExceededError` is never retried regardless of config.
|
|
50
|
+
*/
|
|
51
|
+
declare function executeWithRetry<T>(fn: (retryCtx: RetryContext | undefined) => Promise<T>, config: SmartRetryConfig, onRetry?: (retryCtx: RetryContext, delayMs: number) => void): Promise<T>;
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Queue definition and context types for worker queues.
|
|
55
|
+
*
|
|
56
|
+
* ## Human-in-the-loop (HITL) pause / resume
|
|
57
|
+
*
|
|
58
|
+
* **Pause (`awaiting_approval`)** — After a step completes, if the **next** step has
|
|
59
|
+
* `requiresApproval: true`, the runtime calls the step's `chain` function (if any),
|
|
60
|
+
* stores the result as the pending input for that step, marks it `awaiting_approval`,
|
|
61
|
+
* and does **not** dispatch the worker until a human approves.
|
|
62
|
+
*
|
|
63
|
+
* **Resume (`POST .../approve`)** — The app route calls `dispatchWorker` for that step
|
|
64
|
+
* with `__hitlInput` (reviewer form payload) attached. The `wrapHandlerForQueue` runtime
|
|
65
|
+
* calls the step's `resume` function (or merges inputs by default) to produce the final
|
|
66
|
+
* domain input, then strips all envelope keys before the user handler receives it.
|
|
67
|
+
*
|
|
68
|
+
* Workers **do not** need to accept any `__workerQueue` / `__hitlPending` / `__hitlInput`
|
|
69
|
+
* keys in their Zod schemas — the runtime strips them automatically.
|
|
70
|
+
*/
|
|
71
|
+
/** Output from one completed step, available to subsequent steps via ChainContext / HitlResumeContext. */
|
|
72
|
+
interface QueueStepOutput {
|
|
73
|
+
stepIndex: number;
|
|
74
|
+
workerId: string;
|
|
75
|
+
output: unknown;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Context passed to a step's {@link WorkerQueueStep.chain} function when the queue
|
|
79
|
+
* advances normally (previous step completed without HITL).
|
|
80
|
+
*/
|
|
81
|
+
interface ChainContext {
|
|
82
|
+
/** Original input passed to `dispatchQueue` (the first step's input). */
|
|
83
|
+
initialInput: unknown;
|
|
84
|
+
/**
|
|
85
|
+
* Outputs from all previous steps in order.
|
|
86
|
+
* `previousOutputs[0]` = step 0 output; last entry = most recent.
|
|
87
|
+
*/
|
|
88
|
+
previousOutputs: QueueStepOutput[];
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Context passed to a step's {@link WorkerQueueStep.resume} function when a human has
|
|
92
|
+
* approved the HITL pause and the step is being re-dispatched.
|
|
93
|
+
*
|
|
94
|
+
* @template T - Shape of the reviewer / UI form payload.
|
|
95
|
+
* Derive it from your `hitl.inputSchema` if defined:
|
|
96
|
+
* `HitlResumeContext<z.infer<typeof reviewerSchema>>`.
|
|
97
|
+
*/
|
|
98
|
+
interface HitlResumeContext<T = unknown> {
|
|
99
|
+
/** Original input passed to `dispatchQueue` (the first step's input). */
|
|
100
|
+
initialInput: unknown;
|
|
101
|
+
/**
|
|
102
|
+
* Outputs from all previous steps in order.
|
|
103
|
+
* `previousOutputs[0]` = step 0 output; last entry = most recent.
|
|
104
|
+
*/
|
|
105
|
+
previousOutputs: QueueStepOutput[];
|
|
106
|
+
/** Reviewer / UI form payload submitted via `POST .../approve` (`input` body field). */
|
|
107
|
+
reviewerInput: T;
|
|
108
|
+
/**
|
|
109
|
+
* The computed next-step input that was stored pending approval (domain fields only;
|
|
110
|
+
* all `__workerQueue` / `__hitlPending` / `hitl` envelope keys are stripped).
|
|
111
|
+
* This is what the `chain` function produced before the step was paused.
|
|
112
|
+
*/
|
|
113
|
+
pendingInput: Record<string, unknown>;
|
|
114
|
+
}
|
|
115
|
+
/** Internal envelope keys injected by the queue runtime. Never passed to user handlers. */
|
|
116
|
+
declare const QUEUE_ORCHESTRATION_KEYS: readonly ["__workerQueue", "__hitlInput", "__hitlDecision", "__hitlPending", "hitl"];
|
|
117
|
+
interface WorkerQueueStep {
|
|
118
|
+
/** Worker ID for this step. Must match an existing registered worker. */
|
|
119
|
+
workerId: string;
|
|
120
|
+
/**
|
|
121
|
+
* Optional delay (in seconds) before this step is executed.
|
|
122
|
+
* Implemented via SQS DelaySeconds (max 900).
|
|
123
|
+
*/
|
|
124
|
+
delaySeconds?: number;
|
|
125
|
+
/**
|
|
126
|
+
* Called when the queue advances to this step after the previous step completes
|
|
127
|
+
* normally (no HITL). Receives a {@link ChainContext} and must return the input
|
|
128
|
+
* object for this worker.
|
|
129
|
+
*
|
|
130
|
+
* **Built-in shortcuts** (use a string instead of a function):
|
|
131
|
+
* - `'passthrough'` — pass the previous step's output directly as this step's input.
|
|
132
|
+
* - `'continueFromPrevious'` — extract `{ current, history }` from the previous
|
|
133
|
+
* output and build a `{ mode: 'continue', ... }` payload (useful for multi-round sessions).
|
|
134
|
+
*
|
|
135
|
+
* When omitted, the previous step's output is used as-is (equivalent to `'passthrough'`).
|
|
136
|
+
*
|
|
137
|
+
* @example
|
|
138
|
+
* ```ts
|
|
139
|
+
* chain: (ctx) => ({
|
|
140
|
+
* mode: 'review' as const,
|
|
141
|
+
* data: ctx.previousOutputs[ctx.previousOutputs.length - 1]?.output,
|
|
142
|
+
* }),
|
|
143
|
+
* ```
|
|
144
|
+
*/
|
|
145
|
+
chain?: ((ctx: ChainContext) => unknown) | 'passthrough' | 'continueFromPrevious';
|
|
146
|
+
/**
|
|
147
|
+
* Called when this step resumes after a human approves a HITL pause.
|
|
148
|
+
* Receives a {@link HitlResumeContext} with the reviewer's form payload and the
|
|
149
|
+
* stored pending input, and must return the final domain input for this worker.
|
|
150
|
+
*
|
|
151
|
+
* When omitted, a shallow merge of `pendingInput` + `reviewerInput` is used as the
|
|
152
|
+
* default (suitable for simple cases where reviewer fields override pending fields).
|
|
153
|
+
*
|
|
154
|
+
* @example
|
|
155
|
+
* ```ts
|
|
156
|
+
* resume: (ctx: HitlResumeContext<ReviewerSchema>) => ({
|
|
157
|
+
* ...ctx.pendingInput,
|
|
158
|
+
* overriddenField: ctx.reviewerInput.overriddenField,
|
|
159
|
+
* }),
|
|
160
|
+
* ```
|
|
161
|
+
*/
|
|
162
|
+
resume?: (ctx: HitlResumeContext<any>) => unknown;
|
|
163
|
+
/**
|
|
164
|
+
* When `true`, queue execution pauses before dispatching this step.
|
|
165
|
+
* The step waits until a human calls `POST .../approve` (or `POST .../reject`).
|
|
166
|
+
* Define a `resume` function to control how the reviewer's payload is merged with
|
|
167
|
+
* the pending input.
|
|
168
|
+
*/
|
|
169
|
+
requiresApproval?: boolean;
|
|
170
|
+
/**
|
|
171
|
+
* Optional HITL UI/metadata for this step (consumed by app UI and tooling).
|
|
172
|
+
* Use {@link defineHitlConfig} for type-safe authoring.
|
|
173
|
+
* The worker runtime uses only `requiresApproval` — this field is UI-only.
|
|
174
|
+
*/
|
|
175
|
+
hitl?: HitlStepConfig;
|
|
176
|
+
/**
|
|
177
|
+
* Smart retry configuration for this queue step.
|
|
178
|
+
* Overrides any retry config on the worker definition for this step only.
|
|
179
|
+
* Retries run in-process (same Lambda invocation); ctx.retryContext is populated
|
|
180
|
+
* on each retry so the handler can self-correct (e.g. inject error into prompt).
|
|
181
|
+
*
|
|
182
|
+
* @example
|
|
183
|
+
* ```ts
|
|
184
|
+
* retry: { maxAttempts: 3, on: ['rate-limit', 'json-parse'] }
|
|
185
|
+
* ```
|
|
186
|
+
*/
|
|
187
|
+
retry?: SmartRetryConfig;
|
|
188
|
+
/**
|
|
189
|
+
* When defined, evaluated after each run of this step to decide whether to
|
|
190
|
+
* re-run it (another iteration) instead of advancing to the next step.
|
|
191
|
+
*
|
|
192
|
+
* Combine with `requiresApproval: true` for HITL-gated loops where the
|
|
193
|
+
* reviewer's decision (e.g. a `continueLoop` field in their payload)
|
|
194
|
+
* controls whether another round starts.
|
|
195
|
+
*
|
|
196
|
+
* @example
|
|
197
|
+
* ```ts
|
|
198
|
+
* // Loop continues until the worker returns { finalized: true }
|
|
199
|
+
* loop: {
|
|
200
|
+
* shouldContinue: ({ output }) => !(output as { finalized?: boolean }).finalized,
|
|
201
|
+
* maxIterations: 20,
|
|
202
|
+
* }
|
|
203
|
+
* ```
|
|
204
|
+
*/
|
|
205
|
+
loop?: {
|
|
206
|
+
/** Return true to run this step again; false to advance to the next step. */
|
|
207
|
+
shouldContinue: (ctx: LoopContext) => boolean | Promise<boolean>;
|
|
208
|
+
/**
|
|
209
|
+
* Hard cap on total iterations (including the first run).
|
|
210
|
+
* Prevents runaway loops. Default: 50.
|
|
211
|
+
*/
|
|
212
|
+
maxIterations?: number;
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
interface WorkerQueueConfig<InitialInput = any, StepOutput = any> {
|
|
216
|
+
/** Stable queue identifier, e.g. `"cost-review"`. */
|
|
217
|
+
id: string;
|
|
218
|
+
/** Ordered list of steps forming the queue pipeline. */
|
|
219
|
+
steps: WorkerQueueStep[];
|
|
220
|
+
/**
|
|
221
|
+
* Optional schedule for the queue (cron or rate expression).
|
|
222
|
+
* When set, the CLI generates a queue-starter Lambda triggered by this schedule.
|
|
223
|
+
* @example `'cron(0 3 * * ? *)'` — daily at 03:00 UTC.
|
|
224
|
+
*/
|
|
225
|
+
schedule?: string | {
|
|
226
|
+
rate: string;
|
|
227
|
+
enabled?: boolean;
|
|
228
|
+
input?: Record<string, any>;
|
|
229
|
+
};
|
|
230
|
+
_initialInputType?: InitialInput;
|
|
231
|
+
_stepOutputType?: StepOutput;
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Context passed to a step's {@link WorkerQueueStep.loop | loop.shouldContinue} function
|
|
235
|
+
* after each iteration of a looping step.
|
|
236
|
+
*/
|
|
237
|
+
interface LoopContext {
|
|
238
|
+
/** Output returned by the worker for this iteration. */
|
|
239
|
+
output: unknown;
|
|
240
|
+
/** Step definition index (stable; does not change across iterations). */
|
|
241
|
+
stepIndex: number;
|
|
242
|
+
/** 0-based count of how many times this step has already run (0 = first run). */
|
|
243
|
+
iterationCount: number;
|
|
244
|
+
/** Original input passed to `dispatchQueue`. */
|
|
245
|
+
initialInput: unknown;
|
|
246
|
+
/** Outputs from all previous steps (and previous iterations of this step). */
|
|
247
|
+
previousOutputs: QueueStepOutput[];
|
|
248
|
+
}
|
|
249
|
+
/**
|
|
250
|
+
* Queue execution context embedded into job input so queue-aware wrappers
|
|
251
|
+
* know their position in the pipeline. Injected automatically by the runtime.
|
|
252
|
+
*/
|
|
253
|
+
interface WorkerQueueContext<InitialInput = any> {
|
|
254
|
+
id: string;
|
|
255
|
+
/** Queue definition step index — used to look up chain/resume/loop config. Stays fixed across loop iterations. */
|
|
256
|
+
stepIndex: number;
|
|
257
|
+
/**
|
|
258
|
+
* Actual array position of this step in the job's steps[] store.
|
|
259
|
+
* Differs from stepIndex when the same step has looped multiple times
|
|
260
|
+
* (each iteration is appended as a new entry). Defaults to stepIndex when absent.
|
|
261
|
+
*/
|
|
262
|
+
arrayStepIndex?: number;
|
|
263
|
+
initialInput: InitialInput;
|
|
264
|
+
/** Queue job ID for progress tracking (same as first worker's jobId). */
|
|
265
|
+
queueJobId?: string;
|
|
266
|
+
/** How many times this step has already run (used by looping steps). */
|
|
267
|
+
iterationCount?: number;
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* Repeat a step definition `count` times, calling `factory(index)` for each repetition.
|
|
271
|
+
* Eliminates copy-paste for multi-round HITL workflows.
|
|
272
|
+
*
|
|
273
|
+
* @example
|
|
274
|
+
* ```ts
|
|
275
|
+
* const queue = defineWorkerQueue({
|
|
276
|
+
* id: 'multi-review',
|
|
277
|
+
* steps: [
|
|
278
|
+
* { workerId: 'ingest' },
|
|
279
|
+
* ...repeatStep(3, (i) => ({
|
|
280
|
+
* workerId: 'review',
|
|
281
|
+
* chain: chainFromPrev,
|
|
282
|
+
* resume: resumeFromHitl,
|
|
283
|
+
* requiresApproval: true,
|
|
284
|
+
* hitl: defineHitlConfig({ taskKey: `review-r${i + 1}`, ui: { type: 'schema-form', title: `Round ${i + 1}` } }),
|
|
285
|
+
* })),
|
|
286
|
+
* ],
|
|
287
|
+
* });
|
|
288
|
+
* ```
|
|
289
|
+
*/
|
|
290
|
+
declare function repeatStep(count: number, factory: (index: number) => WorkerQueueStep): WorkerQueueStep[];
|
|
291
|
+
/**
|
|
292
|
+
* Identity helper for defining worker queues in `.queue.ts` files.
|
|
293
|
+
* Use `satisfies WorkerQueueConfig<InitialInput, StepOutput>` for phantom-type docs.
|
|
294
|
+
*
|
|
295
|
+
* @example
|
|
296
|
+
* ```ts
|
|
297
|
+
* const q = defineWorkerQueue({
|
|
298
|
+
* id: 'my-queue',
|
|
299
|
+
* steps: [ ... ],
|
|
300
|
+
* }) satisfies WorkerQueueConfig<MyInit, MyOutput>;
|
|
301
|
+
* export default q;
|
|
302
|
+
* ```
|
|
303
|
+
*/
|
|
304
|
+
declare function defineWorkerQueue<T extends WorkerQueueConfig>(config: T): T;
|
|
305
|
+
|
|
306
|
+
export { type BuiltInRetryPattern as B, type CustomRetryPattern as C, type HitlResumeContext as H, type LoopContext as L, type QueueStepOutput as Q, type RetryContext as R, type SmartRetryConfig as S, type WorkerQueueStep as W, type RetryPattern as a, type ChainContext as b, QUEUE_ORCHESTRATION_KEYS as c, type WorkerQueueConfig as d, type WorkerQueueContext as e, defineWorkerQueue as f, executeWithRetry as g, matchesRetryPattern as m, repeatStep as r };
|
package/dist/queue.d.mts
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import './hitlConfig.mjs';
|
|
2
|
+
export { b as ChainContext, H as HitlResumeContext, L as LoopContext, c as QUEUE_ORCHESTRATION_KEYS, Q as QueueStepOutput, d as WorkerQueueConfig, e as WorkerQueueContext, W as WorkerQueueStep, f as defineWorkerQueue, r as repeatStep } from './queue-DaR2UuZi.mjs';
|
|
3
|
+
import 'zod';
|
package/dist/queue.d.ts
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import './hitlConfig.js';
|
|
2
|
+
export { b as ChainContext, H as HitlResumeContext, L as LoopContext, c as QUEUE_ORCHESTRATION_KEYS, Q as QueueStepOutput, d as WorkerQueueConfig, e as WorkerQueueContext, W as WorkerQueueStep, f as defineWorkerQueue, r as repeatStep } from './queue-B5n6YVQV.js';
|
|
3
|
+
import 'zod';
|
package/dist/queue.js
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/queue.ts
|
|
21
|
+
var queue_exports = {};
|
|
22
|
+
__export(queue_exports, {
|
|
23
|
+
QUEUE_ORCHESTRATION_KEYS: () => QUEUE_ORCHESTRATION_KEYS,
|
|
24
|
+
defineWorkerQueue: () => defineWorkerQueue,
|
|
25
|
+
repeatStep: () => repeatStep
|
|
26
|
+
});
|
|
27
|
+
module.exports = __toCommonJS(queue_exports);
|
|
28
|
+
var QUEUE_ORCHESTRATION_KEYS = [
|
|
29
|
+
"__workerQueue",
|
|
30
|
+
"__hitlInput",
|
|
31
|
+
"__hitlDecision",
|
|
32
|
+
"__hitlPending",
|
|
33
|
+
"hitl"
|
|
34
|
+
];
|
|
35
|
+
function repeatStep(count, factory) {
|
|
36
|
+
return Array.from({ length: count }, (_, i) => factory(i));
|
|
37
|
+
}
|
|
38
|
+
function defineWorkerQueue(config) {
|
|
39
|
+
return config;
|
|
40
|
+
}
|
|
41
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
42
|
+
0 && (module.exports = {
|
|
43
|
+
QUEUE_ORCHESTRATION_KEYS,
|
|
44
|
+
defineWorkerQueue,
|
|
45
|
+
repeatStep
|
|
46
|
+
});
|
|
47
|
+
//# sourceMappingURL=queue.js.map
|