@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
package/CHANGELOG.md
CHANGED
package/README.md
CHANGED
|
@@ -197,6 +197,28 @@ handler: async ({ ctx }) => {
|
|
|
197
197
|
|
|
198
198
|
The CLI detects `ctx.dispatchWorker('id', ...)` and adds `WORKER_QUEUE_URL_<ID>` to that Lambda’s env. Local dev uses the HTTP trigger when queue URL is not set.
|
|
199
199
|
|
|
200
|
+
### Worker queues and HITL (human-in-the-loop)
|
|
201
|
+
|
|
202
|
+
- **Pause:** When a step has `requiresApproval: true`, `wrapHandlerForQueue` stops **before** dispatching that step, marks the next step `awaiting_approval`, and stores the computed pending input. The target worker does not run until approval.
|
|
203
|
+
- **Resume:** `POST .../approve` should call `dispatchWorker` for **that** step only (same `workerJobId`), passing **`{ ...pendingInput, __hitlInput, __hitlDecision }`**. **`wrapHandlerForQueue`** runs **`mapInputFromPrev`** again with **`hitlInput`** / **`pendingStepInput`** on the context so the worker receives merged domain input (no separate app-side merge module).
|
|
204
|
+
- **Types:** Import **`MapStepInputContext`** from `@microfox/ai-worker` for mapper functions; use `satisfies WorkerQueueConfig<YourInitial, YourStepOutput>` on `defineWorkerQueue({...})` to document contracts.
|
|
205
|
+
|
|
206
|
+
#### Optional `chainStrategy` on a step
|
|
207
|
+
|
|
208
|
+
- **`custom`** or **omitted** — one **`mapInputFromPrev`** export handles **both** advancing the chain and HITL resume (legacy behavior).
|
|
209
|
+
- **`passthrough`** — chain path uses **`defaultMapChainPassthrough`**; resume still uses **`mapInputFromPrev`** when `hitlInput` + `pendingStepInput` are present.
|
|
210
|
+
- **`continueFromPrevious`** — chain path uses **`defaultMapChainContinueFromPrevious`**; resume still uses **`mapInputFromPrev`**.
|
|
211
|
+
|
|
212
|
+
#### Worker input: orchestration envelope
|
|
213
|
+
|
|
214
|
+
Merge your domain Zod object with **`withQueueOrchestrationEnvelope(domainSchema)`**, or intersect a union with **`queueOrchestrationFieldsSchema`**, so `__workerQueue` / HITL keys are accepted without loosening the whole schema.
|
|
215
|
+
|
|
216
|
+
#### HITL step metadata (`defineHitlConfig`)
|
|
217
|
+
|
|
218
|
+
Import **`defineHitlConfig`**, **`HitlStepConfig`**, and **`HitlUiSpec`** from **`@microfox/ai-worker`** and pass the result as **`hitl`** on a queue step (with **`requiresApproval: true`**). This is a typed authoring helper; runtime pause behavior is still driven by **`requiresApproval`**.
|
|
219
|
+
|
|
220
|
+
Full field semantics and lifecycle are documented in the `queue` module JSDoc (`WorkerQueueStep`, `WorkerQueueConfig`).
|
|
221
|
+
|
|
200
222
|
## License
|
|
201
223
|
|
|
202
224
|
MIT
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { b as ChainContext } from './queue-DaR2UuZi.mjs';
|
|
2
|
+
import './hitlConfig.mjs';
|
|
3
|
+
import 'zod';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Built-in chain mapping: pass the previous step's output directly as the next step's input.
|
|
7
|
+
* Falls back to `initialInput` if there are no previous outputs.
|
|
8
|
+
*
|
|
9
|
+
* Use via `chain: 'passthrough'` on a queue step — no need to reference this directly.
|
|
10
|
+
*/
|
|
11
|
+
declare function defaultMapChainPassthrough(ctx: ChainContext): unknown;
|
|
12
|
+
/**
|
|
13
|
+
* Built-in chain mapping for same-worker "continue" rounds.
|
|
14
|
+
* Maps `{ current, history, ... }` from the last step output into
|
|
15
|
+
* `{ mode: 'continue', current, history, nextNumber: 0, operator: 'add' }`.
|
|
16
|
+
*
|
|
17
|
+
* Use via `chain: 'continueFromPrevious'` on a queue step.
|
|
18
|
+
*/
|
|
19
|
+
declare function defaultMapChainContinueFromPrevious(ctx: ChainContext): unknown;
|
|
20
|
+
|
|
21
|
+
export { defaultMapChainContinueFromPrevious, defaultMapChainPassthrough };
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { b as ChainContext } from './queue-B5n6YVQV.js';
|
|
2
|
+
import './hitlConfig.js';
|
|
3
|
+
import 'zod';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Built-in chain mapping: pass the previous step's output directly as the next step's input.
|
|
7
|
+
* Falls back to `initialInput` if there are no previous outputs.
|
|
8
|
+
*
|
|
9
|
+
* Use via `chain: 'passthrough'` on a queue step — no need to reference this directly.
|
|
10
|
+
*/
|
|
11
|
+
declare function defaultMapChainPassthrough(ctx: ChainContext): unknown;
|
|
12
|
+
/**
|
|
13
|
+
* Built-in chain mapping for same-worker "continue" rounds.
|
|
14
|
+
* Maps `{ current, history, ... }` from the last step output into
|
|
15
|
+
* `{ mode: 'continue', current, history, nextNumber: 0, operator: 'add' }`.
|
|
16
|
+
*
|
|
17
|
+
* Use via `chain: 'continueFromPrevious'` on a queue step.
|
|
18
|
+
*/
|
|
19
|
+
declare function defaultMapChainContinueFromPrevious(ctx: ChainContext): unknown;
|
|
20
|
+
|
|
21
|
+
export { defaultMapChainContinueFromPrevious, defaultMapChainPassthrough };
|
|
@@ -0,0 +1,59 @@
|
|
|
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/chainMapDefaults.ts
|
|
21
|
+
var chainMapDefaults_exports = {};
|
|
22
|
+
__export(chainMapDefaults_exports, {
|
|
23
|
+
defaultMapChainContinueFromPrevious: () => defaultMapChainContinueFromPrevious,
|
|
24
|
+
defaultMapChainPassthrough: () => defaultMapChainPassthrough
|
|
25
|
+
});
|
|
26
|
+
module.exports = __toCommonJS(chainMapDefaults_exports);
|
|
27
|
+
function defaultMapChainPassthrough(ctx) {
|
|
28
|
+
const { initialInput, previousOutputs } = ctx;
|
|
29
|
+
if (previousOutputs.length > 0) {
|
|
30
|
+
return previousOutputs[previousOutputs.length - 1]?.output;
|
|
31
|
+
}
|
|
32
|
+
return initialInput;
|
|
33
|
+
}
|
|
34
|
+
function defaultMapChainContinueFromPrevious(ctx) {
|
|
35
|
+
const { previousOutputs } = ctx;
|
|
36
|
+
const prev = previousOutputs[previousOutputs.length - 1]?.output;
|
|
37
|
+
if (!prev || typeof prev.current !== "number") {
|
|
38
|
+
return {
|
|
39
|
+
mode: "continue",
|
|
40
|
+
current: 0,
|
|
41
|
+
history: [],
|
|
42
|
+
nextNumber: 0,
|
|
43
|
+
operator: "add"
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
return {
|
|
47
|
+
mode: "continue",
|
|
48
|
+
current: prev.current,
|
|
49
|
+
history: prev.history ?? [],
|
|
50
|
+
nextNumber: 0,
|
|
51
|
+
operator: "add"
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
55
|
+
0 && (module.exports = {
|
|
56
|
+
defaultMapChainContinueFromPrevious,
|
|
57
|
+
defaultMapChainPassthrough
|
|
58
|
+
});
|
|
59
|
+
//# sourceMappingURL=chainMapDefaults.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/chainMapDefaults.ts"],"sourcesContent":["import type { ChainContext } from './queue.js';\n\n/**\n * Built-in chain mapping: pass the previous step's output directly as the next step's input.\n * Falls back to `initialInput` if there are no previous outputs.\n *\n * Use via `chain: 'passthrough'` on a queue step — no need to reference this directly.\n */\nexport function defaultMapChainPassthrough(ctx: ChainContext): unknown {\n const { initialInput, previousOutputs } = ctx;\n if (previousOutputs.length > 0) {\n return previousOutputs[previousOutputs.length - 1]?.output;\n }\n return initialInput;\n}\n\n/**\n * Built-in chain mapping for same-worker \"continue\" rounds.\n * Maps `{ current, history, ... }` from the last step output into\n * `{ mode: 'continue', current, history, nextNumber: 0, operator: 'add' }`.\n *\n * Use via `chain: 'continueFromPrevious'` on a queue step.\n */\nexport function defaultMapChainContinueFromPrevious(ctx: ChainContext): unknown {\n const { previousOutputs } = ctx;\n const prev = previousOutputs[previousOutputs.length - 1]?.output as {\n current?: number;\n history?: string[];\n } | null;\n if (!prev || typeof prev.current !== 'number') {\n return {\n mode: 'continue' as const,\n current: 0,\n history: [] as string[],\n nextNumber: 0,\n operator: 'add' as const,\n };\n }\n return {\n mode: 'continue' as const,\n current: prev.current,\n history: prev.history ?? [],\n nextNumber: 0,\n operator: 'add' as const,\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAQO,SAAS,2BAA2B,KAA4B;AACrE,QAAM,EAAE,cAAc,gBAAgB,IAAI;AAC1C,MAAI,gBAAgB,SAAS,GAAG;AAC9B,WAAO,gBAAgB,gBAAgB,SAAS,CAAC,GAAG;AAAA,EACtD;AACA,SAAO;AACT;AASO,SAAS,oCAAoC,KAA4B;AAC9E,QAAM,EAAE,gBAAgB,IAAI;AAC5B,QAAM,OAAO,gBAAgB,gBAAgB,SAAS,CAAC,GAAG;AAI1D,MAAI,CAAC,QAAQ,OAAO,KAAK,YAAY,UAAU;AAC7C,WAAO;AAAA,MACL,MAAM;AAAA,MACN,SAAS;AAAA,MACT,SAAS,CAAC;AAAA,MACV,YAAY;AAAA,MACZ,UAAU;AAAA,IACZ;AAAA,EACF;AACA,SAAO;AAAA,IACL,MAAM;AAAA,IACN,SAAS,KAAK;AAAA,IACd,SAAS,KAAK,WAAW,CAAC;AAAA,IAC1B,YAAY;AAAA,IACZ,UAAU;AAAA,EACZ;AACF;","names":[]}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import {
|
|
2
|
+
defaultMapChainContinueFromPrevious,
|
|
3
|
+
defaultMapChainPassthrough
|
|
4
|
+
} from "./chunk-XCKWV2WZ.mjs";
|
|
5
|
+
import "./chunk-BJTO5JO5.mjs";
|
|
6
|
+
export {
|
|
7
|
+
defaultMapChainContinueFromPrevious,
|
|
8
|
+
defaultMapChainPassthrough
|
|
9
|
+
};
|
|
10
|
+
//# sourceMappingURL=chainMapDefaults.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/hitlConfig.ts"],"sourcesContent":["import type { ZodType } from 'zod';\n\n/**\n * UI rendering hint for a HITL step.\n *\n * - `'custom'` — render using a registered view component (by `viewId`).\n * - `'schema-form'` — auto-render a form for the step (no custom view needed).\n * The panel falls back to a raw-JSON textarea if no schema-form renderer is available.\n */\nexport type HitlUiSpec =\n | {\n type: 'custom';\n /** View ID used to look up the renderer in the app's HITL view registry. */\n viewId: string;\n title?: string;\n sections?: Array<Record<string, unknown>>;\n [key: string]: unknown;\n }\n | {\n type: 'schema-form';\n title?: string;\n description?: string;\n };\n\n/** Metadata for a human-in-the-loop step on `WorkerQueueStep.hitl`. */\nexport type HitlStepConfig = {\n taskKey: string;\n timeoutSeconds?: number;\n onTimeout?: 'reject' | 'auto-approve';\n assignees?: string[];\n ui: HitlUiSpec;\n /** Reviewer form schema — single source of truth; use `z.infer<typeof schema>` for types. */\n inputSchema?: ZodType;\n};\n\n/**\n * DX helper for queue authors: keeps HITL config typed/readable.\n *\n * @example\n * ```ts\n * const hitl = defineHitlConfig({\n * taskKey: 'review-step',\n * ui: { type: 'schema-form', title: 'Review the output' },\n * inputSchema: z.object({ approved: z.boolean(), comment: z.string().optional() }),\n * });\n * ```\n */\nexport function defineHitlConfig<T extends HitlStepConfig>(config: T): T {\n return config;\n}\n"],"mappings":";AA+CO,SAAS,iBAA2C,QAAc;AACvE,SAAO;AACT;","names":[]}
|
|
@@ -37,6 +37,9 @@ function serializeContext(ctx) {
|
|
|
37
37
|
if (ctx.requestId) {
|
|
38
38
|
serialized.requestId = ctx.requestId;
|
|
39
39
|
}
|
|
40
|
+
if (ctx.userId) {
|
|
41
|
+
serialized.userId = ctx.userId;
|
|
42
|
+
}
|
|
40
43
|
if (ctx.metadata && typeof ctx.metadata === "object") {
|
|
41
44
|
Object.assign(serialized, ctx.metadata);
|
|
42
45
|
}
|
|
@@ -51,6 +54,8 @@ async function dispatch(workerId, input, inputSchema, options, ctx) {
|
|
|
51
54
|
const jobId = options.jobId || `job-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
52
55
|
const triggerUrl = getWorkersTriggerUrl();
|
|
53
56
|
const serializedContext = ctx ? serializeContext(ctx) : {};
|
|
57
|
+
const userId = options.userId ?? ctx?.userId;
|
|
58
|
+
if (userId) serializedContext.userId = userId;
|
|
54
59
|
const messageBody = {
|
|
55
60
|
workerId,
|
|
56
61
|
jobId,
|
|
@@ -58,7 +63,8 @@ async function dispatch(workerId, input, inputSchema, options, ctx) {
|
|
|
58
63
|
context: serializedContext,
|
|
59
64
|
webhookUrl: options.webhookUrl,
|
|
60
65
|
metadata: options.metadata || {},
|
|
61
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
66
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
67
|
+
...options.maxTokens !== void 0 ? { maxTokens: options.maxTokens } : {}
|
|
62
68
|
};
|
|
63
69
|
const headers = {
|
|
64
70
|
"Content-Type": "application/json"
|
|
@@ -93,6 +99,8 @@ async function dispatchWorker(workerId, input, options = {}, ctx) {
|
|
|
93
99
|
const jobId = options.jobId || `job-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
94
100
|
const triggerUrl = getWorkersTriggerUrl();
|
|
95
101
|
const serializedContext = ctx ? serializeContext(ctx) : {};
|
|
102
|
+
const userId = options.userId ?? ctx?.userId;
|
|
103
|
+
if (userId) serializedContext.userId = userId;
|
|
96
104
|
const messageBody = {
|
|
97
105
|
workerId,
|
|
98
106
|
jobId,
|
|
@@ -100,7 +108,8 @@ async function dispatchWorker(workerId, input, options = {}, ctx) {
|
|
|
100
108
|
context: serializedContext,
|
|
101
109
|
webhookUrl: options.webhookUrl,
|
|
102
110
|
metadata: options.metadata || {},
|
|
103
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
111
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
112
|
+
...options.maxTokens !== void 0 ? { maxTokens: options.maxTokens } : {}
|
|
104
113
|
};
|
|
105
114
|
const headers = { "Content-Type": "application/json" };
|
|
106
115
|
const triggerKey = process.env.WORKERS_TRIGGER_API_KEY;
|
|
@@ -130,6 +139,7 @@ async function dispatchQueue(queueId, initialInput, options = {}, _ctx) {
|
|
|
130
139
|
const headers = { "Content-Type": "application/json" };
|
|
131
140
|
const triggerKey = process.env.WORKERS_TRIGGER_API_KEY;
|
|
132
141
|
if (triggerKey) headers["x-workers-trigger-key"] = triggerKey;
|
|
142
|
+
const userId = options.userId ?? _ctx?.userId;
|
|
133
143
|
const response = await fetch(queueStartUrl, {
|
|
134
144
|
method: "POST",
|
|
135
145
|
headers,
|
|
@@ -138,6 +148,7 @@ async function dispatchQueue(queueId, initialInput, options = {}, _ctx) {
|
|
|
138
148
|
initialInput: normalizedInput,
|
|
139
149
|
metadata: options.metadata ?? {},
|
|
140
150
|
jobId,
|
|
151
|
+
...userId ? { userId } : {},
|
|
141
152
|
...options.webhookUrl ? { webhookUrl: options.webhookUrl } : {}
|
|
142
153
|
})
|
|
143
154
|
});
|
|
@@ -160,4 +171,4 @@ export {
|
|
|
160
171
|
dispatchLocal,
|
|
161
172
|
dispatchQueue
|
|
162
173
|
};
|
|
163
|
-
//# sourceMappingURL=chunk-
|
|
174
|
+
//# sourceMappingURL=chunk-CILTGUUQ.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/client.ts"],"sourcesContent":["/**\n * Client for dispatching background worker jobs.\n *\n * In production, dispatching happens via the workers HTTP API:\n * POST /workers/trigger -> enqueues message to SQS on the workers service side\n *\n * This avoids requiring AWS credentials in your Next.js app.\n */\n\nimport type { ZodType, z } from 'zod';\nimport type { WorkerQueueConfig } from './queue.js';\n\nimport type { ChainContext, HitlResumeContext, LoopContext } from './queue.js';\n\nexport interface WorkerQueueRegistry {\n getQueueById(queueId: string): WorkerQueueConfig | undefined;\n getStepAt?(queueId: string, stepIndex: number): {\n workerId?: string;\n requiresApproval?: boolean;\n hasChain?: boolean;\n hasResume?: boolean;\n hasLoop?: boolean;\n hitl?: unknown;\n } | undefined;\n /** Build next-step input during normal chain advancement (no HITL). */\n invokeChain?: (\n queueId: string,\n stepIndex: number,\n ctx: ChainContext\n ) => Promise<unknown> | unknown;\n /** Build domain input when a HITL step resumes after human approval. */\n invokeResume?: (\n queueId: string,\n stepIndex: number,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n ctx: HitlResumeContext<any>\n ) => Promise<unknown> | unknown;\n /** Evaluate whether a looping step should run again after its output. */\n invokeLoop?: (\n queueId: string,\n stepIndex: number,\n ctx: LoopContext\n ) => Promise<boolean> | boolean;\n}\n\nexport interface DispatchOptions {\n /**\n * Optional webhook callback URL to notify when the job finishes.\n * Only called when provided. Default: no webhook (use job store / MongoDB only).\n */\n webhookUrl?: string;\n /**\n * Controls how dispatch executes.\n * - \"auto\" (default): local inline execution in development unless WORKERS_LOCAL_MODE=false.\n * - \"local\": force inline execution (no SQS).\n * - \"remote\": force SQS/Lambda dispatch even in development.\n */\n mode?: 'auto' | 'local' | 'remote';\n jobId?: string;\n /**\n * The ID of the user who triggered this job.\n * Call getClientId() in your API route and pass the result here.\n * If omitted, userId is not stored and not logged.\n */\n userId?: string;\n metadata?: Record<string, any>;\n /**\n * In-memory queue registry for dispatchQueue. Required when using dispatchQueue.\n * Pass a registry that imports from your .queue.ts definitions (works on Vercel/serverless).\n */\n registry?: WorkerQueueRegistry;\n /**\n * Optional callback to create a queue job record before dispatching.\n * Called with queueJobId (= first worker's jobId), queueId, and firstStep.\n */\n onCreateQueueJob?: (params: {\n queueJobId: string;\n queueId: string;\n firstStep: { workerId: string; workerJobId: string };\n metadata?: Record<string, unknown>;\n }) => Promise<void>;\n /**\n * Maximum total tokens (input + output) allowed for this job.\n * The worker must call ctx.reportTokenUsage() after each LLM call.\n * Throws TokenBudgetExceededError when the limit is reached.\n * For queues, applies per-step unless overridden on the queue step config.\n */\n maxTokens?: number;\n}\n\nexport interface DispatchResult {\n messageId: string;\n status: 'queued';\n jobId: string;\n}\n\nexport interface DispatchQueueResult extends DispatchResult {\n queueId: string;\n}\n\nexport interface SerializedContext {\n requestId?: string;\n userId?: string;\n traceId?: string;\n [key: string]: any;\n}\n\n\n/**\n * Derives the full /workers/trigger URL from env.\n * Exported for use by local dispatchWorker (worker-to-worker in dev).\n * Server-side only; clients should use useWorkflowJob with your app's /api/workflows routes.\n *\n * Env vars:\n * - WORKER_BASE_URL: base URL of the workers service (e.g. https://.../prod)\n * - WORKERS_TRIGGER_API_URL / WORKERS_CONFIG_API_URL: legacy, still supported\n */\nexport function getWorkersTriggerUrl(): string {\n const raw =\n process.env.WORKER_BASE_URL ||\n process.env.WORKERS_TRIGGER_API_URL ||\n process.env.WORKERS_CONFIG_API_URL;\n\n if (!raw) {\n throw new Error(\n 'WORKER_BASE_URL is required for background workers. Set it server-side only.'\n );\n }\n\n const url = new URL(raw);\n url.search = '';\n url.hash = '';\n\n const path = url.pathname || '';\n\n // If the user pointed at a specific endpoint, normalize back to the service root.\n url.pathname = path.replace(/\\/?workers\\/(trigger|config)\\/?$/, '');\n\n const basePath = url.pathname.replace(/\\/+$/, '');\n url.pathname = `${basePath}/workers/trigger`.replace(/\\/+$/, '');\n\n return url.toString();\n}\n\n/**\n * URL for the queue start endpoint (dispatch proxy). Use this so queue starts\n * go through the queue handler Lambda for easier debugging (one log stream per queue).\n */\nexport function getQueueStartUrl(queueId: string): string {\n const raw =\n process.env.WORKER_BASE_URL ||\n process.env.WORKERS_TRIGGER_API_URL ||\n process.env.WORKERS_CONFIG_API_URL;\n\n if (!raw) {\n throw new Error(\n 'WORKER_BASE_URL is required for background workers. Set it server-side only.'\n );\n }\n\n const url = new URL(raw);\n url.search = '';\n url.hash = '';\n\n const path = url.pathname || '';\n url.pathname = path.replace(/\\/?workers\\/(trigger|config)\\/?$/, '');\n const basePath = url.pathname.replace(/\\/+$/, '');\n const safeSegment = encodeURIComponent(queueId);\n url.pathname = `${basePath}/queues/${safeSegment}/start`.replace(/\\/+$/, '');\n\n return url.toString();\n}\n\n/**\n * Serializes context data for transmission to Lambda.\n * Only serializes safe, JSON-compatible properties.\n */\nfunction serializeContext(ctx: any): SerializedContext {\n const serialized: SerializedContext = {};\n\n if (ctx.requestId) {\n serialized.requestId = ctx.requestId;\n }\n\n if (ctx.userId) {\n serialized.userId = ctx.userId;\n }\n\n // Extract any additional serializable metadata\n if (ctx.metadata && typeof ctx.metadata === 'object') {\n Object.assign(serialized, ctx.metadata);\n }\n\n // Allow custom context serialization via a helper property\n if (ctx._serializeContext && typeof ctx._serializeContext === 'function') {\n const custom = ctx._serializeContext();\n Object.assign(serialized, custom);\n }\n\n return serialized;\n}\n\n\n/**\n * Dispatches a background worker job to SQS.\n *\n * @param workerId - The ID of the worker to dispatch\n * @param input - The input data for the worker (will be validated against inputSchema)\n * @param inputSchema - Zod schema for input validation\n * @param options - Dispatch options including webhook URL\n * @param ctx - Optional context object (only serializable parts will be sent)\n * @returns Promise resolving to dispatch result with messageId and jobId\n */\nexport async function dispatch<INPUT_SCHEMA extends ZodType<any>>(\n workerId: string,\n input: z.input<INPUT_SCHEMA>,\n inputSchema: INPUT_SCHEMA,\n options: DispatchOptions,\n ctx?: any\n): Promise<DispatchResult> {\n // Validate input against schema\n const validatedInput = inputSchema.parse(input);\n\n // Generate job ID if not provided\n const jobId =\n options.jobId || `job-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;\n\n // Resolve /workers/trigger endpoint URL\n const triggerUrl = getWorkersTriggerUrl();\n\n // Serialize context (only safe, JSON-compatible parts)\n const serializedContext = ctx ? serializeContext(ctx) : {};\n\n // Attach userId if explicitly provided in options or context.\n const userId = options.userId ?? (ctx?.userId as string | undefined);\n if (userId) serializedContext.userId = userId;\n\n // Job updates use MongoDB only; never pass jobStoreUrl/origin URL.\n const messageBody = {\n workerId,\n jobId,\n input: validatedInput,\n context: serializedContext,\n webhookUrl: options.webhookUrl,\n metadata: options.metadata || {},\n timestamp: new Date().toISOString(),\n ...(options.maxTokens !== undefined ? { maxTokens: options.maxTokens } : {}),\n };\n\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n };\n const triggerKey = process.env.WORKERS_TRIGGER_API_KEY;\n if (triggerKey) {\n headers['x-workers-trigger-key'] = triggerKey;\n }\n\n const response = await fetch(triggerUrl, {\n method: 'POST',\n headers,\n body: JSON.stringify({\n workerId,\n body: messageBody,\n }),\n });\n\n if (!response.ok) {\n const text = await response.text().catch(() => '');\n throw new Error(\n `Failed to trigger worker \"${workerId}\": ${response.status} ${response.statusText}${text ? ` - ${text}` : ''}`\n );\n }\n\n const data = (await response.json().catch(() => ({}))) as any;\n const messageId = data?.messageId ? String(data.messageId) : `trigger-${jobId}`;\n\n return {\n messageId,\n status: 'queued',\n jobId,\n };\n}\n\n/**\n * Dispatch a worker by ID without importing the worker module.\n * Sends to the workers trigger API (WORKER_BASE_URL). No input schema validation at call site.\n *\n * @param workerId - The worker ID (e.g. 'echo', 'data-processor')\n * @param input - Input payload (object or undefined)\n * @param options - Optional jobId, webhookUrl, metadata\n * @param ctx - Optional context (serializable parts sent in the request)\n * @returns Promise resolving to { messageId, status: 'queued', jobId }\n */\nexport async function dispatchWorker(\n workerId: string,\n input?: Record<string, unknown>,\n options: DispatchOptions = {},\n ctx?: any\n): Promise<DispatchResult> {\n const jobId =\n options.jobId || `job-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;\n const triggerUrl = getWorkersTriggerUrl();\n const serializedContext = ctx ? serializeContext(ctx) : {};\n const userId = options.userId ?? (ctx?.userId as string | undefined);\n if (userId) serializedContext.userId = userId;\n const messageBody = {\n workerId,\n jobId,\n input: input ?? {},\n context: serializedContext,\n webhookUrl: options.webhookUrl,\n metadata: options.metadata || {},\n timestamp: new Date().toISOString(),\n ...(options.maxTokens !== undefined ? { maxTokens: options.maxTokens } : {}),\n };\n const headers: Record<string, string> = { 'Content-Type': 'application/json' };\n const triggerKey = process.env.WORKERS_TRIGGER_API_KEY;\n if (triggerKey) headers['x-workers-trigger-key'] = triggerKey;\n const response = await fetch(triggerUrl, {\n method: 'POST',\n headers,\n body: JSON.stringify({ workerId, body: messageBody }),\n });\n if (!response.ok) {\n const text = await response.text().catch(() => '');\n throw new Error(\n `Failed to trigger worker \"${workerId}\": ${response.status} ${response.statusText}${text ? ` - ${text}` : ''}`\n );\n }\n const data = (await response.json().catch(() => ({}))) as any;\n const messageId = data?.messageId ? String(data.messageId) : `trigger-${jobId}`;\n return { messageId, status: 'queued', jobId };\n}\n\n/**\n * Local development mode: runs the handler immediately in the same process.\n * This bypasses SQS and Lambda for faster iteration during development.\n *\n * @param handler - The worker handler function\n * @param input - The input data\n * @param ctx - The context object\n * @returns The handler result\n */\nexport async function dispatchLocal<INPUT, OUTPUT>(\n handler: (params: { input: INPUT; ctx: any }) => Promise<OUTPUT>,\n input: INPUT,\n ctx?: any\n): Promise<OUTPUT> {\n return handler({ input, ctx: ctx || {} });\n}\n\n/**\n * Dispatches a queue by ID. POSTs to the queue-start API; the queue-start handler creates the queue job.\n * Pass the first worker's input directly (no registry required).\n */\nexport async function dispatchQueue<InitialInput = any>(\n queueId: string,\n initialInput?: InitialInput,\n options: DispatchOptions = {},\n _ctx?: any\n): Promise<DispatchQueueResult> {\n const jobId =\n options.jobId || `job-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;\n const queueStartUrl = getQueueStartUrl(queueId);\n const normalizedInput =\n initialInput !== null && typeof initialInput === 'object'\n ? (initialInput as Record<string, unknown>)\n : { value: initialInput };\n const headers: Record<string, string> = { 'Content-Type': 'application/json' };\n const triggerKey = process.env.WORKERS_TRIGGER_API_KEY;\n if (triggerKey) headers['x-workers-trigger-key'] = triggerKey;\n const userId = options.userId ?? (_ctx?.userId as string | undefined);\n const response = await fetch(queueStartUrl, {\n method: 'POST',\n headers,\n body: JSON.stringify({\n input: normalizedInput,\n initialInput: normalizedInput,\n metadata: options.metadata ?? {},\n jobId,\n ...(userId ? { userId } : {}),\n ...(options.webhookUrl ? { webhookUrl: options.webhookUrl } : {}),\n }),\n });\n if (!response.ok) {\n const text = await response.text().catch(() => '');\n throw new Error(\n `Failed to start queue \"${queueId}\": ${response.status} ${response.statusText}${text ? ` - ${text}` : ''}`\n );\n }\n const data = (await response.json().catch(() => ({}))) as any;\n const messageId = data?.messageId ?? data?.jobId ?? `queue-${jobId}`;\n return { queueId, messageId, status: 'queued', jobId };\n}\n\n"],"mappings":"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","names":[]}
|