@microfox/ai-worker 1.0.3 → 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.
Files changed (69) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/README.md +22 -0
  3. package/dist/chainMapDefaults.d.mts +21 -0
  4. package/dist/chainMapDefaults.d.ts +21 -0
  5. package/dist/chainMapDefaults.js +59 -0
  6. package/dist/chainMapDefaults.js.map +1 -0
  7. package/dist/chainMapDefaults.mjs +10 -0
  8. package/dist/chainMapDefaults.mjs.map +1 -0
  9. package/dist/chunk-BCRJIFKB.mjs +9 -0
  10. package/dist/chunk-BCRJIFKB.mjs.map +1 -0
  11. package/dist/{chunk-72XGFZCE.mjs → chunk-CILTGUUQ.mjs} +14 -3
  12. package/dist/chunk-CILTGUUQ.mjs.map +1 -0
  13. package/dist/{chunk-7LQNS2SG.mjs → chunk-QHX55IML.mjs} +442 -56
  14. package/dist/chunk-QHX55IML.mjs.map +1 -0
  15. package/dist/chunk-SQB5FQCZ.mjs +21 -0
  16. package/dist/chunk-SQB5FQCZ.mjs.map +1 -0
  17. package/dist/{chunk-AOXGONGI.mjs → chunk-T7DRPKR6.mjs} +7 -5
  18. package/dist/chunk-T7DRPKR6.mjs.map +1 -0
  19. package/dist/chunk-XCKWV2WZ.mjs +34 -0
  20. package/dist/chunk-XCKWV2WZ.mjs.map +1 -0
  21. package/dist/chunk-ZW4PNCDH.mjs +17 -0
  22. package/dist/chunk-ZW4PNCDH.mjs.map +1 -0
  23. package/dist/client.d.mts +148 -2
  24. package/dist/client.d.ts +148 -2
  25. package/dist/client.js +13 -2
  26. package/dist/client.js.map +1 -1
  27. package/dist/client.mjs +1 -1
  28. package/dist/handler.d.mts +121 -23
  29. package/dist/handler.d.ts +121 -23
  30. package/dist/handler.js +450 -58
  31. package/dist/handler.js.map +1 -1
  32. package/dist/handler.mjs +5 -2
  33. package/dist/hitlConfig.d.mts +46 -0
  34. package/dist/hitlConfig.d.ts +46 -0
  35. package/dist/hitlConfig.js +33 -0
  36. package/dist/hitlConfig.js.map +1 -0
  37. package/dist/hitlConfig.mjs +8 -0
  38. package/dist/hitlConfig.mjs.map +1 -0
  39. package/dist/index.d.mts +28 -4
  40. package/dist/index.d.ts +28 -4
  41. package/dist/index.js +575 -74
  42. package/dist/index.js.map +1 -1
  43. package/dist/index.mjs +78 -20
  44. package/dist/index.mjs.map +1 -1
  45. package/dist/queue-B5n6YVQV.d.ts +306 -0
  46. package/dist/queue-DaR2UuZi.d.mts +306 -0
  47. package/dist/queue.d.mts +3 -0
  48. package/dist/queue.d.ts +3 -0
  49. package/dist/queue.js +47 -0
  50. package/dist/queue.js.map +1 -0
  51. package/dist/queue.mjs +12 -0
  52. package/dist/queue.mjs.map +1 -0
  53. package/dist/queueInputEnvelope.d.mts +31 -0
  54. package/dist/queueInputEnvelope.d.ts +31 -0
  55. package/dist/queueInputEnvelope.js +42 -0
  56. package/dist/queueInputEnvelope.js.map +1 -0
  57. package/dist/queueInputEnvelope.mjs +10 -0
  58. package/dist/queueInputEnvelope.mjs.map +1 -0
  59. package/dist/queueJobStore.d.mts +3 -2
  60. package/dist/queueJobStore.d.ts +3 -2
  61. package/dist/queueJobStore.js +6 -4
  62. package/dist/queueJobStore.js.map +1 -1
  63. package/dist/queueJobStore.mjs +1 -1
  64. package/package.json +7 -2
  65. package/dist/chunk-72XGFZCE.mjs.map +0 -1
  66. package/dist/chunk-7LQNS2SG.mjs.map +0 -1
  67. package/dist/chunk-AOXGONGI.mjs.map +0 -1
  68. package/dist/client-BqSJQ9mZ.d.mts +0 -183
  69. package/dist/client-BqSJQ9mZ.d.ts +0 -183
package/dist/client.d.ts CHANGED
@@ -1,2 +1,148 @@
1
- import 'zod';
2
- export { D as DispatchOptions, b as DispatchQueueResult, a as DispatchResult, S as SerializedContext, W as WorkerQueueRegistry, d as dispatch, f as dispatchLocal, h as dispatchQueue, e as dispatchWorker, c as getQueueStartUrl, g as getWorkersTriggerUrl } from './client-BqSJQ9mZ.js';
1
+ import { ZodType, z } from 'zod';
2
+ import { d as WorkerQueueConfig, b as ChainContext, H as HitlResumeContext, L as LoopContext } from './queue-B5n6YVQV.js';
3
+ import './hitlConfig.js';
4
+
5
+ /**
6
+ * Client for dispatching background worker jobs.
7
+ *
8
+ * In production, dispatching happens via the workers HTTP API:
9
+ * POST /workers/trigger -> enqueues message to SQS on the workers service side
10
+ *
11
+ * This avoids requiring AWS credentials in your Next.js app.
12
+ */
13
+
14
+ interface WorkerQueueRegistry {
15
+ getQueueById(queueId: string): WorkerQueueConfig | undefined;
16
+ getStepAt?(queueId: string, stepIndex: number): {
17
+ workerId?: string;
18
+ requiresApproval?: boolean;
19
+ hasChain?: boolean;
20
+ hasResume?: boolean;
21
+ hasLoop?: boolean;
22
+ hitl?: unknown;
23
+ } | undefined;
24
+ /** Build next-step input during normal chain advancement (no HITL). */
25
+ invokeChain?: (queueId: string, stepIndex: number, ctx: ChainContext) => Promise<unknown> | unknown;
26
+ /** Build domain input when a HITL step resumes after human approval. */
27
+ invokeResume?: (queueId: string, stepIndex: number, ctx: HitlResumeContext<any>) => Promise<unknown> | unknown;
28
+ /** Evaluate whether a looping step should run again after its output. */
29
+ invokeLoop?: (queueId: string, stepIndex: number, ctx: LoopContext) => Promise<boolean> | boolean;
30
+ }
31
+ interface DispatchOptions {
32
+ /**
33
+ * Optional webhook callback URL to notify when the job finishes.
34
+ * Only called when provided. Default: no webhook (use job store / MongoDB only).
35
+ */
36
+ webhookUrl?: string;
37
+ /**
38
+ * Controls how dispatch executes.
39
+ * - "auto" (default): local inline execution in development unless WORKERS_LOCAL_MODE=false.
40
+ * - "local": force inline execution (no SQS).
41
+ * - "remote": force SQS/Lambda dispatch even in development.
42
+ */
43
+ mode?: 'auto' | 'local' | 'remote';
44
+ jobId?: string;
45
+ /**
46
+ * The ID of the user who triggered this job.
47
+ * Call getClientId() in your API route and pass the result here.
48
+ * If omitted, userId is not stored and not logged.
49
+ */
50
+ userId?: string;
51
+ metadata?: Record<string, any>;
52
+ /**
53
+ * In-memory queue registry for dispatchQueue. Required when using dispatchQueue.
54
+ * Pass a registry that imports from your .queue.ts definitions (works on Vercel/serverless).
55
+ */
56
+ registry?: WorkerQueueRegistry;
57
+ /**
58
+ * Optional callback to create a queue job record before dispatching.
59
+ * Called with queueJobId (= first worker's jobId), queueId, and firstStep.
60
+ */
61
+ onCreateQueueJob?: (params: {
62
+ queueJobId: string;
63
+ queueId: string;
64
+ firstStep: {
65
+ workerId: string;
66
+ workerJobId: string;
67
+ };
68
+ metadata?: Record<string, unknown>;
69
+ }) => Promise<void>;
70
+ /**
71
+ * Maximum total tokens (input + output) allowed for this job.
72
+ * The worker must call ctx.reportTokenUsage() after each LLM call.
73
+ * Throws TokenBudgetExceededError when the limit is reached.
74
+ * For queues, applies per-step unless overridden on the queue step config.
75
+ */
76
+ maxTokens?: number;
77
+ }
78
+ interface DispatchResult {
79
+ messageId: string;
80
+ status: 'queued';
81
+ jobId: string;
82
+ }
83
+ interface DispatchQueueResult extends DispatchResult {
84
+ queueId: string;
85
+ }
86
+ interface SerializedContext {
87
+ requestId?: string;
88
+ userId?: string;
89
+ traceId?: string;
90
+ [key: string]: any;
91
+ }
92
+ /**
93
+ * Derives the full /workers/trigger URL from env.
94
+ * Exported for use by local dispatchWorker (worker-to-worker in dev).
95
+ * Server-side only; clients should use useWorkflowJob with your app's /api/workflows routes.
96
+ *
97
+ * Env vars:
98
+ * - WORKER_BASE_URL: base URL of the workers service (e.g. https://.../prod)
99
+ * - WORKERS_TRIGGER_API_URL / WORKERS_CONFIG_API_URL: legacy, still supported
100
+ */
101
+ declare function getWorkersTriggerUrl(): string;
102
+ /**
103
+ * URL for the queue start endpoint (dispatch proxy). Use this so queue starts
104
+ * go through the queue handler Lambda for easier debugging (one log stream per queue).
105
+ */
106
+ declare function getQueueStartUrl(queueId: string): string;
107
+ /**
108
+ * Dispatches a background worker job to SQS.
109
+ *
110
+ * @param workerId - The ID of the worker to dispatch
111
+ * @param input - The input data for the worker (will be validated against inputSchema)
112
+ * @param inputSchema - Zod schema for input validation
113
+ * @param options - Dispatch options including webhook URL
114
+ * @param ctx - Optional context object (only serializable parts will be sent)
115
+ * @returns Promise resolving to dispatch result with messageId and jobId
116
+ */
117
+ declare function dispatch<INPUT_SCHEMA extends ZodType<any>>(workerId: string, input: z.input<INPUT_SCHEMA>, inputSchema: INPUT_SCHEMA, options: DispatchOptions, ctx?: any): Promise<DispatchResult>;
118
+ /**
119
+ * Dispatch a worker by ID without importing the worker module.
120
+ * Sends to the workers trigger API (WORKER_BASE_URL). No input schema validation at call site.
121
+ *
122
+ * @param workerId - The worker ID (e.g. 'echo', 'data-processor')
123
+ * @param input - Input payload (object or undefined)
124
+ * @param options - Optional jobId, webhookUrl, metadata
125
+ * @param ctx - Optional context (serializable parts sent in the request)
126
+ * @returns Promise resolving to { messageId, status: 'queued', jobId }
127
+ */
128
+ declare function dispatchWorker(workerId: string, input?: Record<string, unknown>, options?: DispatchOptions, ctx?: any): Promise<DispatchResult>;
129
+ /**
130
+ * Local development mode: runs the handler immediately in the same process.
131
+ * This bypasses SQS and Lambda for faster iteration during development.
132
+ *
133
+ * @param handler - The worker handler function
134
+ * @param input - The input data
135
+ * @param ctx - The context object
136
+ * @returns The handler result
137
+ */
138
+ declare function dispatchLocal<INPUT, OUTPUT>(handler: (params: {
139
+ input: INPUT;
140
+ ctx: any;
141
+ }) => Promise<OUTPUT>, input: INPUT, ctx?: any): Promise<OUTPUT>;
142
+ /**
143
+ * Dispatches a queue by ID. POSTs to the queue-start API; the queue-start handler creates the queue job.
144
+ * Pass the first worker's input directly (no registry required).
145
+ */
146
+ declare function dispatchQueue<InitialInput = any>(queueId: string, initialInput?: InitialInput, options?: DispatchOptions, _ctx?: any): Promise<DispatchQueueResult>;
147
+
148
+ export { type DispatchOptions, type DispatchQueueResult, type DispatchResult, type SerializedContext, type WorkerQueueRegistry, dispatch, dispatchLocal, dispatchQueue, dispatchWorker, getQueueStartUrl, getWorkersTriggerUrl };
package/dist/client.js CHANGED
@@ -66,6 +66,9 @@ function serializeContext(ctx) {
66
66
  if (ctx.requestId) {
67
67
  serialized.requestId = ctx.requestId;
68
68
  }
69
+ if (ctx.userId) {
70
+ serialized.userId = ctx.userId;
71
+ }
69
72
  if (ctx.metadata && typeof ctx.metadata === "object") {
70
73
  Object.assign(serialized, ctx.metadata);
71
74
  }
@@ -80,6 +83,8 @@ async function dispatch(workerId, input, inputSchema, options, ctx) {
80
83
  const jobId = options.jobId || `job-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
81
84
  const triggerUrl = getWorkersTriggerUrl();
82
85
  const serializedContext = ctx ? serializeContext(ctx) : {};
86
+ const userId = options.userId ?? ctx?.userId;
87
+ if (userId) serializedContext.userId = userId;
83
88
  const messageBody = {
84
89
  workerId,
85
90
  jobId,
@@ -87,7 +92,8 @@ async function dispatch(workerId, input, inputSchema, options, ctx) {
87
92
  context: serializedContext,
88
93
  webhookUrl: options.webhookUrl,
89
94
  metadata: options.metadata || {},
90
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
95
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
96
+ ...options.maxTokens !== void 0 ? { maxTokens: options.maxTokens } : {}
91
97
  };
92
98
  const headers = {
93
99
  "Content-Type": "application/json"
@@ -122,6 +128,8 @@ async function dispatchWorker(workerId, input, options = {}, ctx) {
122
128
  const jobId = options.jobId || `job-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
123
129
  const triggerUrl = getWorkersTriggerUrl();
124
130
  const serializedContext = ctx ? serializeContext(ctx) : {};
131
+ const userId = options.userId ?? ctx?.userId;
132
+ if (userId) serializedContext.userId = userId;
125
133
  const messageBody = {
126
134
  workerId,
127
135
  jobId,
@@ -129,7 +137,8 @@ async function dispatchWorker(workerId, input, options = {}, ctx) {
129
137
  context: serializedContext,
130
138
  webhookUrl: options.webhookUrl,
131
139
  metadata: options.metadata || {},
132
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
140
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
141
+ ...options.maxTokens !== void 0 ? { maxTokens: options.maxTokens } : {}
133
142
  };
134
143
  const headers = { "Content-Type": "application/json" };
135
144
  const triggerKey = process.env.WORKERS_TRIGGER_API_KEY;
@@ -159,6 +168,7 @@ async function dispatchQueue(queueId, initialInput, options = {}, _ctx) {
159
168
  const headers = { "Content-Type": "application/json" };
160
169
  const triggerKey = process.env.WORKERS_TRIGGER_API_KEY;
161
170
  if (triggerKey) headers["x-workers-trigger-key"] = triggerKey;
171
+ const userId = options.userId ?? _ctx?.userId;
162
172
  const response = await fetch(queueStartUrl, {
163
173
  method: "POST",
164
174
  headers,
@@ -167,6 +177,7 @@ async function dispatchQueue(queueId, initialInput, options = {}, _ctx) {
167
177
  initialInput: normalizedInput,
168
178
  metadata: options.metadata ?? {},
169
179
  jobId,
180
+ ...userId ? { userId } : {},
170
181
  ...options.webhookUrl ? { webhookUrl: options.webhookUrl } : {}
171
182
  })
172
183
  });
@@ -1 +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\nexport interface WorkerQueueRegistry {\n getQueueById(queueId: string): WorkerQueueConfig | undefined;\n /** (initialInput, previousOutputs) for best DX: derive next input from original request and all prior step outputs. */\n invokeMapInput?: (\n queueId: string,\n stepIndex: number,\n initialInput: unknown,\n previousOutputs: Array<{ stepIndex: number; workerId: string; output: unknown }>\n ) => Promise<unknown> | unknown;\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 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\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 * 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 // 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 // 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 };\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 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 };\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 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 ...(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":[]}
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":[]}
package/dist/client.mjs CHANGED
@@ -5,7 +5,7 @@ import {
5
5
  dispatchWorker,
6
6
  getQueueStartUrl,
7
7
  getWorkersTriggerUrl
8
- } from "./chunk-72XGFZCE.mjs";
8
+ } from "./chunk-CILTGUUQ.mjs";
9
9
  import "./chunk-BJTO5JO5.mjs";
10
10
  export {
11
11
  dispatch,
@@ -1,5 +1,29 @@
1
1
  import { SQSEvent, Context } from 'aws-lambda';
2
2
  import { ZodType } from 'zod';
3
+ import { R as RetryContext, S as SmartRetryConfig, Q as QueueStepOutput, b as ChainContext, H as HitlResumeContext, L as LoopContext } from './queue-DaR2UuZi.mjs';
4
+ export { B as BuiltInRetryPattern, C as CustomRetryPattern, a as RetryPattern } from './queue-DaR2UuZi.mjs';
5
+ import './hitlConfig.mjs';
6
+
7
+ /**
8
+ * Token budget tracking for workers.
9
+ * Workers report usage via ctx.reportTokenUsage(); the runtime accumulates
10
+ * and throws TokenBudgetExceededError when the limit is reached.
11
+ */
12
+ interface TokenUsage {
13
+ inputTokens: number;
14
+ outputTokens: number;
15
+ }
16
+ interface TokenBudgetState {
17
+ inputTokens: number;
18
+ outputTokens: number;
19
+ /** null = no budget configured */
20
+ budget: number | null;
21
+ }
22
+ declare class TokenBudgetExceededError extends Error {
23
+ readonly used: number;
24
+ readonly budget: number;
25
+ constructor(used: number, budget: number);
26
+ }
3
27
 
4
28
  /**
5
29
  * Generic Lambda handler wrapper for worker agents.
@@ -34,6 +58,7 @@ interface JobRecord {
34
58
  jobId: string;
35
59
  workerId: string;
36
60
  }>;
61
+ userId?: string;
37
62
  createdAt: string;
38
63
  updatedAt: string;
39
64
  completedAt?: string;
@@ -98,6 +123,8 @@ interface WorkerHandlerParams<INPUT, OUTPUT> {
98
123
  jobId: string;
99
124
  workerId: string;
100
125
  requestId?: string;
126
+ /** ID of the user who triggered this job. Pass via DispatchOptions.userId from your API route. */
127
+ userId?: string;
101
128
  /**
102
129
  * Job store interface for updating and retrieving job state.
103
130
  * Uses MongoDB directly when configured; never HTTP/origin URL.
@@ -116,49 +143,114 @@ interface WorkerHandlerParams<INPUT, OUTPUT> {
116
143
  messageId?: string;
117
144
  output?: unknown;
118
145
  }>;
146
+ /**
147
+ * Report token usage after an LLM call. Accumulates across all calls in this job.
148
+ * Throws TokenBudgetExceededError if the configured maxTokens budget is exceeded.
149
+ * Also persists usage to the job store for observability.
150
+ *
151
+ * @example
152
+ * ```ts
153
+ * const result = await anthropic.messages.create({ ... });
154
+ * await ctx.reportTokenUsage({
155
+ * inputTokens: result.usage.input_tokens,
156
+ * outputTokens: result.usage.output_tokens,
157
+ * });
158
+ * ```
159
+ */
160
+ reportTokenUsage: (usage: TokenUsage) => Promise<void>;
161
+ /**
162
+ * Get the current token usage and remaining budget for this job.
163
+ * Returns `{ used, budget: null, remaining: null }` when no maxTokens was set.
164
+ */
165
+ getTokenBudget: () => {
166
+ used: number;
167
+ budget: number | null;
168
+ remaining: number | null;
169
+ };
170
+ /**
171
+ * Populated on retry attempts (attempt >= 2). Contains info about the previous failure
172
+ * so the handler can self-correct (e.g. inject the error message into the next prompt).
173
+ * `undefined` on the first attempt — use `if (ctx.retryContext)` to detect retries.
174
+ */
175
+ retryContext?: RetryContext;
119
176
  [key: string]: any;
120
177
  };
121
178
  }
179
+
122
180
  type WorkerHandler<INPUT, OUTPUT> = (params: WorkerHandlerParams<INPUT, OUTPUT>) => Promise<OUTPUT>;
123
181
  /** Result of getNextStep for queue chaining. */
124
182
  interface QueueNextStep {
125
183
  workerId: string;
126
184
  delaySeconds?: number;
127
- mapInputFromPrev?: string;
185
+ requiresApproval?: boolean;
186
+ /** Whether this step has a `chain` function (or built-in string) defined. */
187
+ hasChain?: boolean;
188
+ /** Whether this step has a `resume` function defined. */
189
+ hasResume?: boolean;
190
+ /** Optional HITL metadata from queue step config (UI/tooling only). */
191
+ hitl?: {
192
+ ui?: unknown;
193
+ } | unknown;
194
+ /** Smart retry config for this step. Overrides worker-level retry for this step only. */
195
+ retry?: SmartRetryConfig;
128
196
  }
129
- /** One previous step's output (for mapInputFromPrev context). */
130
- interface QueueStepOutput {
131
- stepIndex: number;
132
- workerId: string;
133
- output: unknown;
197
+ /**
198
+ * @deprecated Use {@link ChainContext} for the normal chain path and
199
+ * {@link HitlResumeContext} for the HITL resume path instead.
200
+ * Kept for backwards compatibility with queue files written against the old API.
201
+ */
202
+ interface MapStepInputContext {
203
+ initialInput: unknown;
204
+ previousOutputs: QueueStepOutput[];
205
+ /** @deprecated Use HitlResumeContext.reviewerInput instead. */
206
+ hitlInput?: unknown;
207
+ /** @deprecated Use HitlResumeContext.pendingInput instead. */
208
+ pendingStepInput?: Record<string, unknown>;
134
209
  }
210
+
135
211
  /** Runtime helpers for queue-aware wrappers (provided by generated registry). */
136
212
  interface QueueRuntime {
137
213
  getNextStep(queueId: string, stepIndex: number): QueueNextStep | undefined;
138
- /** Optional: when provided, mapping can use outputs from any previous step. */
214
+ /** Step config at `stepIndex`. */
215
+ getStepAt?(queueId: string, stepIndex: number): QueueNextStep | undefined;
216
+ /** Optional: when provided, mappers can use outputs from any previous step. */
139
217
  getQueueJob?(queueJobId: string): Promise<{
140
218
  steps: Array<{
141
219
  workerId: string;
142
220
  output?: unknown;
143
221
  }>;
144
222
  } | null>;
145
- /** (initialInput, previousOutputs) – previousOutputs includes outputs for steps 0..stepIndex-1 and current step. */
146
- invokeMapInput?(queueId: string, stepIndex: number, initialInput: unknown, previousOutputs: QueueStepOutput[]): Promise<unknown> | unknown;
223
+ /**
224
+ * Build the input for a step when the queue advances normally (no HITL resume).
225
+ * Calls the step's `chain` function, or the built-in passthrough/continueFromPrevious.
226
+ */
227
+ invokeChain?(queueId: string, stepIndex: number, ctx: ChainContext): Promise<unknown> | unknown;
228
+ /**
229
+ * Build the domain input for a step when it resumes after HITL approval.
230
+ * Calls the step's `resume` function, or merges pendingInput + reviewerInput by default.
231
+ */
232
+ invokeResume?(queueId: string, stepIndex: number, ctx: HitlResumeContext): Promise<unknown> | unknown;
233
+ /**
234
+ * Evaluate whether a looping step should run again.
235
+ * Calls the step's `loop.shouldContinue` function. Returns false if none defined.
236
+ */
237
+ invokeLoop?(queueId: string, stepIndex: number, ctx: LoopContext): Promise<boolean> | boolean;
147
238
  }
148
239
  /**
149
- * Wraps a user handler so that when the job has __workerQueue context (from
150
- * dispatchQueue or queue cron), it dispatches the next worker in the sequence
151
- * after the handler completes. Uses literal worker IDs so the CLI env injection
152
- * picks up WORKER_QUEUE_URL_* for next-step workers.
240
+ * Wraps a user handler so that when the job has `__workerQueue` context (from
241
+ * `dispatchQueue` or queue cron), it dispatches the next worker in the sequence
242
+ * **after** the handler completes.
243
+ *
244
+ * All queue/HITL envelope keys (`__workerQueue`, `__hitlInput`, `__hitlDecision`,
245
+ * `__hitlPending`, `hitl`) are **stripped from `params.input` before the user handler
246
+ * runs** — workers receive clean domain input and do not need to accept these keys
247
+ * in their Zod schemas.
248
+ *
249
+ * **HITL resume:** When `__hitlInput` is present, `invokeResume` is called first to
250
+ * produce the merged domain input. **Chain advancement:** After a step completes,
251
+ * `invokeChain` is called to compute the next step's input.
153
252
  */
154
- declare function wrapHandlerForQueue<INPUT, OUTPUT>(handler: WorkerHandler<INPUT, OUTPUT>, queueRuntime: QueueRuntime): WorkerHandler<INPUT & {
155
- __workerQueue?: {
156
- id: string;
157
- stepIndex: number;
158
- initialInput: unknown;
159
- queueJobId?: string;
160
- };
161
- }, OUTPUT>;
253
+ declare function wrapHandlerForQueue<INPUT, OUTPUT>(handler: WorkerHandler<INPUT, OUTPUT>, queueRuntime: QueueRuntime): WorkerHandler<INPUT, OUTPUT>;
162
254
  interface SQSMessageBody {
163
255
  workerId: string;
164
256
  jobId: string;
@@ -169,6 +261,10 @@ interface SQSMessageBody {
169
261
  jobStoreUrl?: string;
170
262
  metadata?: Record<string, any>;
171
263
  timestamp: string;
264
+ /** ID of the user who triggered this job. Forwarded from dispatch options. */
265
+ userId?: string;
266
+ /** Maximum total tokens (input + output) for this job. Forwarded from DispatchOptions.maxTokens. */
267
+ maxTokens?: number;
172
268
  }
173
269
  interface WebhookPayload {
174
270
  jobId: string;
@@ -190,6 +286,8 @@ interface WebhookPayload {
190
286
  * @param outputSchema - Optional Zod schema for output validation
191
287
  * @returns A Lambda handler function
192
288
  */
193
- declare function createLambdaHandler<INPUT, OUTPUT>(handler: WorkerHandler<INPUT, OUTPUT>, outputSchema?: ZodType<OUTPUT>): (event: SQSEvent, context: Context) => Promise<void>;
289
+ declare function createLambdaHandler<INPUT, OUTPUT>(handler: WorkerHandler<INPUT, OUTPUT>, outputSchema?: ZodType<OUTPUT>, options?: {
290
+ retry?: SmartRetryConfig;
291
+ }): (event: SQSEvent, context: Context) => Promise<void>;
194
292
 
195
- export { type DispatchWorkerOptions, type JobRecord, type JobStore, type JobStoreUpdate, type QueueNextStep, type QueueRuntime, type QueueStepOutput, type SQSMessageBody, SQS_MAX_DELAY_SECONDS, type WebhookPayload, type WorkerHandler, type WorkerHandlerParams, type WorkerLogger, createLambdaHandler, createWorkerLogger, wrapHandlerForQueue };
293
+ export { ChainContext, type DispatchWorkerOptions, HitlResumeContext, type JobRecord, type JobStore, type JobStoreUpdate, LoopContext, type MapStepInputContext, type QueueNextStep, type QueueRuntime, QueueStepOutput, RetryContext, type SQSMessageBody, SQS_MAX_DELAY_SECONDS, SmartRetryConfig, TokenBudgetExceededError, type TokenBudgetState, type TokenUsage, type WebhookPayload, type WorkerHandler, type WorkerHandlerParams, type WorkerLogger, createLambdaHandler, createWorkerLogger, wrapHandlerForQueue };