@agentium/queue 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,32 @@
1
+ # @agentium/queue
2
+
3
+ Background job processing for Agentium agents using BullMQ and Redis.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install @agentium/queue bullmq ioredis
9
+ ```
10
+
11
+ ## Quick Start
12
+
13
+ ```typescript
14
+ import { Agent, openai } from "@agentium/core";
15
+ import { AgentQueue, AgentWorker } from "@agentium/queue";
16
+
17
+ const agent = new Agent({ name: "assistant", model: openai("gpt-4o") });
18
+ const connection = { host: "localhost", port: 6379 };
19
+
20
+ const queue = new AgentQueue({ connection });
21
+ const worker = new AgentWorker({ agents: { assistant: agent }, connection });
22
+
23
+ await queue.enqueueAgentRun({ agentName: "assistant", input: "Hello!" });
24
+ ```
25
+
26
+ ## Documentation
27
+
28
+ Full docs at [agentium.mintlify.dev](https://agentium.mintlify.dev)
29
+
30
+ ## License
31
+
32
+ MIT
package/dist/index.cjs ADDED
@@ -0,0 +1,306 @@
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/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ AgentQueue: () => AgentQueue,
24
+ AgentWorker: () => AgentWorker,
25
+ bridgeEventBusToJob: () => bridgeEventBusToJob
26
+ });
27
+ module.exports = __toCommonJS(index_exports);
28
+
29
+ // src/job-producer.ts
30
+ var AgentQueue = class {
31
+ queue;
32
+ queueEvents;
33
+ queueName;
34
+ constructor(config) {
35
+ this.queueName = config.queueName ?? "agentium:jobs";
36
+ const connection = typeof config.connection === "string" ? { url: config.connection } : config.connection;
37
+ try {
38
+ const { Queue, QueueEvents } = require("bullmq");
39
+ this.queue = new Queue(this.queueName, {
40
+ connection,
41
+ defaultJobOptions: config.defaultJobOptions
42
+ });
43
+ this.queueEvents = new QueueEvents(this.queueName, {
44
+ connection
45
+ });
46
+ } catch (err) {
47
+ if (err?.code === "MODULE_NOT_FOUND" || err?.message?.includes("Cannot find module")) {
48
+ throw new Error("bullmq and ioredis are required for AgentQueue. Install them: npm install bullmq ioredis");
49
+ }
50
+ throw err;
51
+ }
52
+ }
53
+ async enqueueAgentRun(opts) {
54
+ const payload = {
55
+ type: "agent",
56
+ agentName: opts.agentName,
57
+ input: opts.input,
58
+ sessionId: opts.sessionId,
59
+ userId: opts.userId
60
+ };
61
+ const jobOpts = {};
62
+ if (opts.priority !== void 0) jobOpts.priority = opts.priority;
63
+ if (opts.delay !== void 0) jobOpts.delay = opts.delay;
64
+ if (opts.attempts !== void 0) jobOpts.attempts = opts.attempts;
65
+ if (opts.backoff) jobOpts.backoff = opts.backoff;
66
+ if (opts.repeat) jobOpts.repeat = opts.repeat;
67
+ const job = await this.queue.add(`agent:${opts.agentName}`, payload, jobOpts);
68
+ return { jobId: job.id };
69
+ }
70
+ async enqueueWorkflow(opts) {
71
+ const payload = {
72
+ type: "workflow",
73
+ workflowName: opts.workflowName,
74
+ initialState: opts.initialState,
75
+ sessionId: opts.sessionId
76
+ };
77
+ const jobOpts = {};
78
+ if (opts.priority !== void 0) jobOpts.priority = opts.priority;
79
+ if (opts.delay !== void 0) jobOpts.delay = opts.delay;
80
+ if (opts.attempts !== void 0) jobOpts.attempts = opts.attempts;
81
+ if (opts.backoff) jobOpts.backoff = opts.backoff;
82
+ if (opts.repeat) jobOpts.repeat = opts.repeat;
83
+ const job = await this.queue.add(`workflow:${opts.workflowName}`, payload, jobOpts);
84
+ return { jobId: job.id };
85
+ }
86
+ async enqueueTeamRun(opts) {
87
+ const payload = {
88
+ type: "team",
89
+ teamName: opts.teamName,
90
+ input: opts.input,
91
+ sessionId: opts.sessionId,
92
+ userId: opts.userId
93
+ };
94
+ const jobOpts = {};
95
+ if (opts.priority !== void 0) jobOpts.priority = opts.priority;
96
+ if (opts.delay !== void 0) jobOpts.delay = opts.delay;
97
+ if (opts.attempts !== void 0) jobOpts.attempts = opts.attempts;
98
+ if (opts.backoff) jobOpts.backoff = opts.backoff;
99
+ if (opts.repeat) jobOpts.repeat = opts.repeat;
100
+ const job = await this.queue.add(`team:${opts.teamName}`, payload, jobOpts);
101
+ return { jobId: job.id };
102
+ }
103
+ async getJobStatus(jobId) {
104
+ const job = await this.queue.getJob(jobId);
105
+ if (!job) {
106
+ throw new Error(`Job ${jobId} not found`);
107
+ }
108
+ const state = await job.getState();
109
+ return {
110
+ jobId: job.id,
111
+ state,
112
+ progress: typeof job.progress === "number" ? job.progress : void 0,
113
+ result: job.returnvalue,
114
+ error: job.failedReason,
115
+ createdAt: new Date(job.timestamp),
116
+ processedAt: job.processedOn ? new Date(job.processedOn) : void 0,
117
+ finishedAt: job.finishedOn ? new Date(job.finishedOn) : void 0
118
+ };
119
+ }
120
+ async cancelJob(jobId) {
121
+ const job = await this.queue.getJob(jobId);
122
+ if (job) {
123
+ await job.remove();
124
+ }
125
+ }
126
+ onCompleted(handler) {
127
+ this.queueEvents.on("completed", ({ jobId, returnvalue }) => {
128
+ handler(jobId, returnvalue);
129
+ });
130
+ }
131
+ onFailed(handler) {
132
+ this.queueEvents.on("failed", ({ jobId, failedReason }) => {
133
+ handler(jobId, new Error(failedReason));
134
+ });
135
+ }
136
+ async schedule(opts) {
137
+ if (!opts.agent && !opts.workflow) {
138
+ throw new Error("schedule() requires either agent or workflow");
139
+ }
140
+ if (opts.agent && opts.workflow) {
141
+ throw new Error("schedule() accepts either agent or workflow, not both");
142
+ }
143
+ const repeat = { pattern: opts.cron, ...opts.timezone ? { tz: opts.timezone } : {} };
144
+ if (opts.agent) {
145
+ return this.enqueueAgentRun({
146
+ agentName: opts.agent.name,
147
+ input: opts.agent.input,
148
+ sessionId: opts.agent.sessionId,
149
+ userId: opts.agent.userId,
150
+ repeat
151
+ });
152
+ }
153
+ if (opts.team) {
154
+ return this.enqueueTeamRun({
155
+ teamName: opts.team.name,
156
+ input: opts.team.input,
157
+ sessionId: opts.team.sessionId,
158
+ userId: opts.team.userId,
159
+ repeat
160
+ });
161
+ }
162
+ return this.enqueueWorkflow({
163
+ workflowName: opts.workflow.name,
164
+ initialState: opts.workflow.initialState,
165
+ repeat
166
+ });
167
+ }
168
+ async unschedule(id) {
169
+ const jobs = await this.queue.getRepeatableJobs();
170
+ const match = jobs.find((j) => j.name === `agent:${id}` || j.name === `workflow:${id}` || j.name === id);
171
+ if (!match) {
172
+ throw new Error(`Schedule "${id}" not found`);
173
+ }
174
+ await this.queue.removeRepeatableByKey(match.key);
175
+ }
176
+ async listSchedules() {
177
+ const jobs = await this.queue.getRepeatableJobs();
178
+ return jobs.map((j) => ({
179
+ id: j.name,
180
+ pattern: j.pattern ?? j.cron,
181
+ timezone: j.tz ?? void 0,
182
+ next: j.next ? new Date(j.next) : /* @__PURE__ */ new Date()
183
+ }));
184
+ }
185
+ async close() {
186
+ const results = await Promise.allSettled([this.queue.close(), this.queueEvents.close()]);
187
+ for (const r of results) {
188
+ if (r.status === "rejected") console.warn("[JobProducer] Error during close:", r.reason);
189
+ }
190
+ }
191
+ };
192
+
193
+ // src/job-worker.ts
194
+ var AgentWorker = class {
195
+ worker;
196
+ constructor(config) {
197
+ const queueName = config.queueName ?? "agentium:jobs";
198
+ const concurrency = config.concurrency ?? 5;
199
+ const connection = typeof config.connection === "string" ? { url: config.connection } : config.connection;
200
+ const defaultJobOptions = {
201
+ attempts: config.attempts ?? 3,
202
+ backoff: {
203
+ type: "exponential",
204
+ delay: config.backoffDelay ?? 1e3
205
+ }
206
+ };
207
+ try {
208
+ const { Worker } = require("bullmq");
209
+ this.worker = new Worker(
210
+ queueName,
211
+ async (job) => {
212
+ const payload = job.data;
213
+ if (payload.type === "agent") {
214
+ const agent = config.agentRegistry[payload.agentName];
215
+ if (!agent) {
216
+ throw new Error(`Agent "${payload.agentName}" not found in registry`);
217
+ }
218
+ const onChunk = (_evt) => {
219
+ job.updateProgress(typeof job.progress === "number" ? job.progress + 1 : 1);
220
+ };
221
+ agent.eventBus.on("run.stream.chunk", onChunk);
222
+ try {
223
+ const result = await agent.run(payload.input, {
224
+ sessionId: payload.sessionId,
225
+ userId: payload.userId
226
+ });
227
+ return result;
228
+ } finally {
229
+ agent.eventBus.off("run.stream.chunk", onChunk);
230
+ }
231
+ }
232
+ if (payload.type === "workflow") {
233
+ const workflow = config.workflowRegistry?.[payload.workflowName];
234
+ if (!workflow) {
235
+ throw new Error(`Workflow "${payload.workflowName}" not found in registry`);
236
+ }
237
+ const result = await workflow.run({
238
+ sessionId: payload.sessionId
239
+ });
240
+ return result;
241
+ }
242
+ if (payload.type === "team") {
243
+ const team = config.teamRegistry?.[payload.teamName];
244
+ if (!team) {
245
+ throw new Error(`Team "${payload.teamName}" not found in registry`);
246
+ }
247
+ const result = await team.run(payload.input, {
248
+ sessionId: payload.sessionId,
249
+ userId: payload.userId
250
+ });
251
+ return result;
252
+ }
253
+ throw new Error(`Unknown job type: ${payload.type}`);
254
+ },
255
+ {
256
+ connection,
257
+ concurrency,
258
+ defaultJobOptions
259
+ }
260
+ );
261
+ } catch (err) {
262
+ if (err?.code === "MODULE_NOT_FOUND" || err?.message?.includes("Cannot find module")) {
263
+ throw new Error("bullmq and ioredis are required for AgentWorker. Install them: npm install bullmq ioredis");
264
+ }
265
+ throw err;
266
+ }
267
+ }
268
+ start() {
269
+ }
270
+ async stop(timeoutMs = 3e4) {
271
+ await Promise.race([
272
+ this.worker.close(),
273
+ new Promise((_, reject) => setTimeout(() => reject(new Error("Worker drain timeout")), timeoutMs))
274
+ ]).catch((err) => {
275
+ console.warn("[JobWorker] Error during stop:", err?.message ?? err);
276
+ });
277
+ }
278
+ };
279
+
280
+ // src/progress.ts
281
+ function bridgeEventBusToJob(eventBus, job, runId) {
282
+ let progressCount = 0;
283
+ const onChunk = (evt) => {
284
+ if (evt.runId !== runId) return;
285
+ progressCount++;
286
+ job.updateProgress(progressCount).catch(() => {
287
+ });
288
+ };
289
+ const onToolCall = (evt) => {
290
+ if (evt.runId !== runId) return;
291
+ job.log(`Tool call: ${evt.toolName}`).catch(() => {
292
+ });
293
+ };
294
+ eventBus.on("run.stream.chunk", onChunk);
295
+ eventBus.on("tool.call", onToolCall);
296
+ return () => {
297
+ eventBus.off("run.stream.chunk", onChunk);
298
+ eventBus.off("tool.call", onToolCall);
299
+ };
300
+ }
301
+ // Annotate the CommonJS export names for ESM import in node:
302
+ 0 && (module.exports = {
303
+ AgentQueue,
304
+ AgentWorker,
305
+ bridgeEventBusToJob
306
+ });
@@ -0,0 +1,171 @@
1
+ import * as _agentium_core from '@agentium/core';
2
+ import { RunOutput, Agent, Workflow, EventBus } from '@agentium/core';
3
+
4
+ type AgentJobPayload = {
5
+ type: "agent";
6
+ agentName: string;
7
+ input: string;
8
+ sessionId?: string;
9
+ userId?: string;
10
+ };
11
+ type WorkflowJobPayload = {
12
+ type: "workflow";
13
+ workflowName: string;
14
+ initialState?: Record<string, unknown>;
15
+ sessionId?: string;
16
+ };
17
+ type TeamJobPayload = {
18
+ type: "team";
19
+ teamName: string;
20
+ input: string;
21
+ sessionId?: string;
22
+ userId?: string;
23
+ };
24
+ type JobPayload = AgentJobPayload | WorkflowJobPayload | TeamJobPayload;
25
+ type ScheduleInfo = {
26
+ id: string;
27
+ pattern: string;
28
+ timezone?: string;
29
+ next: Date;
30
+ };
31
+ type JobStatus = {
32
+ jobId: string;
33
+ state: "waiting" | "active" | "completed" | "failed" | "delayed";
34
+ progress?: number;
35
+ result?: RunOutput;
36
+ error?: string;
37
+ createdAt: Date;
38
+ processedAt?: Date;
39
+ finishedAt?: Date;
40
+ };
41
+
42
+ interface QueueConfig {
43
+ connection: {
44
+ host: string;
45
+ port: number;
46
+ password?: string;
47
+ db?: number;
48
+ tls?: boolean;
49
+ } | string;
50
+ queueName?: string;
51
+ defaultJobOptions?: Record<string, unknown>;
52
+ }
53
+ declare class AgentQueue {
54
+ private queue;
55
+ private queueEvents;
56
+ private queueName;
57
+ constructor(config: QueueConfig);
58
+ enqueueAgentRun(opts: {
59
+ agentName: string;
60
+ input: string;
61
+ sessionId?: string;
62
+ userId?: string;
63
+ priority?: number;
64
+ delay?: number;
65
+ attempts?: number;
66
+ backoff?: {
67
+ type: "exponential" | "fixed";
68
+ delay: number;
69
+ };
70
+ repeat?: {
71
+ pattern: string;
72
+ timezone?: string;
73
+ };
74
+ }): Promise<{
75
+ jobId: string;
76
+ }>;
77
+ enqueueWorkflow(opts: {
78
+ workflowName: string;
79
+ initialState?: Record<string, unknown>;
80
+ sessionId?: string;
81
+ priority?: number;
82
+ delay?: number;
83
+ attempts?: number;
84
+ backoff?: {
85
+ type: "exponential" | "fixed";
86
+ delay: number;
87
+ };
88
+ repeat?: {
89
+ pattern: string;
90
+ timezone?: string;
91
+ };
92
+ }): Promise<{
93
+ jobId: string;
94
+ }>;
95
+ enqueueTeamRun(opts: {
96
+ teamName: string;
97
+ input: string;
98
+ sessionId?: string;
99
+ userId?: string;
100
+ priority?: number;
101
+ delay?: number;
102
+ attempts?: number;
103
+ backoff?: {
104
+ type: "exponential" | "fixed";
105
+ delay: number;
106
+ };
107
+ repeat?: {
108
+ pattern: string;
109
+ timezone?: string;
110
+ };
111
+ }): Promise<{
112
+ jobId: string;
113
+ }>;
114
+ getJobStatus(jobId: string): Promise<JobStatus>;
115
+ cancelJob(jobId: string): Promise<void>;
116
+ onCompleted(handler: (jobId: string, result: RunOutput) => void): void;
117
+ onFailed(handler: (jobId: string, error: Error) => void): void;
118
+ schedule(opts: {
119
+ id: string;
120
+ cron: string;
121
+ timezone?: string;
122
+ agent?: {
123
+ name: string;
124
+ input: string;
125
+ sessionId?: string;
126
+ userId?: string;
127
+ };
128
+ workflow?: {
129
+ name: string;
130
+ initialState?: Record<string, unknown>;
131
+ };
132
+ team?: {
133
+ name: string;
134
+ input: string;
135
+ sessionId?: string;
136
+ userId?: string;
137
+ };
138
+ }): Promise<{
139
+ jobId: string;
140
+ }>;
141
+ unschedule(id: string): Promise<void>;
142
+ listSchedules(): Promise<ScheduleInfo[]>;
143
+ close(): Promise<void>;
144
+ }
145
+
146
+ interface WorkerConfig {
147
+ connection: {
148
+ host: string;
149
+ port: number;
150
+ password?: string;
151
+ db?: number;
152
+ tls?: boolean;
153
+ } | string;
154
+ queueName?: string;
155
+ concurrency?: number;
156
+ attempts?: number;
157
+ backoffDelay?: number;
158
+ agentRegistry: Record<string, Agent>;
159
+ workflowRegistry?: Record<string, Workflow<any>>;
160
+ teamRegistry?: Record<string, _agentium_core.Team>;
161
+ }
162
+ declare class AgentWorker {
163
+ private worker;
164
+ constructor(config: WorkerConfig);
165
+ start(): void;
166
+ stop(timeoutMs?: number): Promise<void>;
167
+ }
168
+
169
+ declare function bridgeEventBusToJob(eventBus: EventBus, job: any, runId: string): () => void;
170
+
171
+ export { type AgentJobPayload, AgentQueue, AgentWorker, type JobPayload, type JobStatus, type QueueConfig, type ScheduleInfo, type TeamJobPayload, type WorkerConfig, type WorkflowJobPayload, bridgeEventBusToJob };
@@ -0,0 +1,171 @@
1
+ import * as _agentium_core from '@agentium/core';
2
+ import { RunOutput, Agent, Workflow, EventBus } from '@agentium/core';
3
+
4
+ type AgentJobPayload = {
5
+ type: "agent";
6
+ agentName: string;
7
+ input: string;
8
+ sessionId?: string;
9
+ userId?: string;
10
+ };
11
+ type WorkflowJobPayload = {
12
+ type: "workflow";
13
+ workflowName: string;
14
+ initialState?: Record<string, unknown>;
15
+ sessionId?: string;
16
+ };
17
+ type TeamJobPayload = {
18
+ type: "team";
19
+ teamName: string;
20
+ input: string;
21
+ sessionId?: string;
22
+ userId?: string;
23
+ };
24
+ type JobPayload = AgentJobPayload | WorkflowJobPayload | TeamJobPayload;
25
+ type ScheduleInfo = {
26
+ id: string;
27
+ pattern: string;
28
+ timezone?: string;
29
+ next: Date;
30
+ };
31
+ type JobStatus = {
32
+ jobId: string;
33
+ state: "waiting" | "active" | "completed" | "failed" | "delayed";
34
+ progress?: number;
35
+ result?: RunOutput;
36
+ error?: string;
37
+ createdAt: Date;
38
+ processedAt?: Date;
39
+ finishedAt?: Date;
40
+ };
41
+
42
+ interface QueueConfig {
43
+ connection: {
44
+ host: string;
45
+ port: number;
46
+ password?: string;
47
+ db?: number;
48
+ tls?: boolean;
49
+ } | string;
50
+ queueName?: string;
51
+ defaultJobOptions?: Record<string, unknown>;
52
+ }
53
+ declare class AgentQueue {
54
+ private queue;
55
+ private queueEvents;
56
+ private queueName;
57
+ constructor(config: QueueConfig);
58
+ enqueueAgentRun(opts: {
59
+ agentName: string;
60
+ input: string;
61
+ sessionId?: string;
62
+ userId?: string;
63
+ priority?: number;
64
+ delay?: number;
65
+ attempts?: number;
66
+ backoff?: {
67
+ type: "exponential" | "fixed";
68
+ delay: number;
69
+ };
70
+ repeat?: {
71
+ pattern: string;
72
+ timezone?: string;
73
+ };
74
+ }): Promise<{
75
+ jobId: string;
76
+ }>;
77
+ enqueueWorkflow(opts: {
78
+ workflowName: string;
79
+ initialState?: Record<string, unknown>;
80
+ sessionId?: string;
81
+ priority?: number;
82
+ delay?: number;
83
+ attempts?: number;
84
+ backoff?: {
85
+ type: "exponential" | "fixed";
86
+ delay: number;
87
+ };
88
+ repeat?: {
89
+ pattern: string;
90
+ timezone?: string;
91
+ };
92
+ }): Promise<{
93
+ jobId: string;
94
+ }>;
95
+ enqueueTeamRun(opts: {
96
+ teamName: string;
97
+ input: string;
98
+ sessionId?: string;
99
+ userId?: string;
100
+ priority?: number;
101
+ delay?: number;
102
+ attempts?: number;
103
+ backoff?: {
104
+ type: "exponential" | "fixed";
105
+ delay: number;
106
+ };
107
+ repeat?: {
108
+ pattern: string;
109
+ timezone?: string;
110
+ };
111
+ }): Promise<{
112
+ jobId: string;
113
+ }>;
114
+ getJobStatus(jobId: string): Promise<JobStatus>;
115
+ cancelJob(jobId: string): Promise<void>;
116
+ onCompleted(handler: (jobId: string, result: RunOutput) => void): void;
117
+ onFailed(handler: (jobId: string, error: Error) => void): void;
118
+ schedule(opts: {
119
+ id: string;
120
+ cron: string;
121
+ timezone?: string;
122
+ agent?: {
123
+ name: string;
124
+ input: string;
125
+ sessionId?: string;
126
+ userId?: string;
127
+ };
128
+ workflow?: {
129
+ name: string;
130
+ initialState?: Record<string, unknown>;
131
+ };
132
+ team?: {
133
+ name: string;
134
+ input: string;
135
+ sessionId?: string;
136
+ userId?: string;
137
+ };
138
+ }): Promise<{
139
+ jobId: string;
140
+ }>;
141
+ unschedule(id: string): Promise<void>;
142
+ listSchedules(): Promise<ScheduleInfo[]>;
143
+ close(): Promise<void>;
144
+ }
145
+
146
+ interface WorkerConfig {
147
+ connection: {
148
+ host: string;
149
+ port: number;
150
+ password?: string;
151
+ db?: number;
152
+ tls?: boolean;
153
+ } | string;
154
+ queueName?: string;
155
+ concurrency?: number;
156
+ attempts?: number;
157
+ backoffDelay?: number;
158
+ agentRegistry: Record<string, Agent>;
159
+ workflowRegistry?: Record<string, Workflow<any>>;
160
+ teamRegistry?: Record<string, _agentium_core.Team>;
161
+ }
162
+ declare class AgentWorker {
163
+ private worker;
164
+ constructor(config: WorkerConfig);
165
+ start(): void;
166
+ stop(timeoutMs?: number): Promise<void>;
167
+ }
168
+
169
+ declare function bridgeEventBusToJob(eventBus: EventBus, job: any, runId: string): () => void;
170
+
171
+ export { type AgentJobPayload, AgentQueue, AgentWorker, type JobPayload, type JobStatus, type QueueConfig, type ScheduleInfo, type TeamJobPayload, type WorkerConfig, type WorkflowJobPayload, bridgeEventBusToJob };
package/dist/index.js ADDED
@@ -0,0 +1,284 @@
1
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
2
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
3
+ }) : x)(function(x) {
4
+ if (typeof require !== "undefined") return require.apply(this, arguments);
5
+ throw Error('Dynamic require of "' + x + '" is not supported');
6
+ });
7
+
8
+ // src/job-producer.ts
9
+ var AgentQueue = class {
10
+ queue;
11
+ queueEvents;
12
+ queueName;
13
+ constructor(config) {
14
+ this.queueName = config.queueName ?? "agentium:jobs";
15
+ const connection = typeof config.connection === "string" ? { url: config.connection } : config.connection;
16
+ try {
17
+ const { Queue, QueueEvents } = __require("bullmq");
18
+ this.queue = new Queue(this.queueName, {
19
+ connection,
20
+ defaultJobOptions: config.defaultJobOptions
21
+ });
22
+ this.queueEvents = new QueueEvents(this.queueName, {
23
+ connection
24
+ });
25
+ } catch (err) {
26
+ if (err?.code === "MODULE_NOT_FOUND" || err?.message?.includes("Cannot find module")) {
27
+ throw new Error("bullmq and ioredis are required for AgentQueue. Install them: npm install bullmq ioredis");
28
+ }
29
+ throw err;
30
+ }
31
+ }
32
+ async enqueueAgentRun(opts) {
33
+ const payload = {
34
+ type: "agent",
35
+ agentName: opts.agentName,
36
+ input: opts.input,
37
+ sessionId: opts.sessionId,
38
+ userId: opts.userId
39
+ };
40
+ const jobOpts = {};
41
+ if (opts.priority !== void 0) jobOpts.priority = opts.priority;
42
+ if (opts.delay !== void 0) jobOpts.delay = opts.delay;
43
+ if (opts.attempts !== void 0) jobOpts.attempts = opts.attempts;
44
+ if (opts.backoff) jobOpts.backoff = opts.backoff;
45
+ if (opts.repeat) jobOpts.repeat = opts.repeat;
46
+ const job = await this.queue.add(`agent:${opts.agentName}`, payload, jobOpts);
47
+ return { jobId: job.id };
48
+ }
49
+ async enqueueWorkflow(opts) {
50
+ const payload = {
51
+ type: "workflow",
52
+ workflowName: opts.workflowName,
53
+ initialState: opts.initialState,
54
+ sessionId: opts.sessionId
55
+ };
56
+ const jobOpts = {};
57
+ if (opts.priority !== void 0) jobOpts.priority = opts.priority;
58
+ if (opts.delay !== void 0) jobOpts.delay = opts.delay;
59
+ if (opts.attempts !== void 0) jobOpts.attempts = opts.attempts;
60
+ if (opts.backoff) jobOpts.backoff = opts.backoff;
61
+ if (opts.repeat) jobOpts.repeat = opts.repeat;
62
+ const job = await this.queue.add(`workflow:${opts.workflowName}`, payload, jobOpts);
63
+ return { jobId: job.id };
64
+ }
65
+ async enqueueTeamRun(opts) {
66
+ const payload = {
67
+ type: "team",
68
+ teamName: opts.teamName,
69
+ input: opts.input,
70
+ sessionId: opts.sessionId,
71
+ userId: opts.userId
72
+ };
73
+ const jobOpts = {};
74
+ if (opts.priority !== void 0) jobOpts.priority = opts.priority;
75
+ if (opts.delay !== void 0) jobOpts.delay = opts.delay;
76
+ if (opts.attempts !== void 0) jobOpts.attempts = opts.attempts;
77
+ if (opts.backoff) jobOpts.backoff = opts.backoff;
78
+ if (opts.repeat) jobOpts.repeat = opts.repeat;
79
+ const job = await this.queue.add(`team:${opts.teamName}`, payload, jobOpts);
80
+ return { jobId: job.id };
81
+ }
82
+ async getJobStatus(jobId) {
83
+ const job = await this.queue.getJob(jobId);
84
+ if (!job) {
85
+ throw new Error(`Job ${jobId} not found`);
86
+ }
87
+ const state = await job.getState();
88
+ return {
89
+ jobId: job.id,
90
+ state,
91
+ progress: typeof job.progress === "number" ? job.progress : void 0,
92
+ result: job.returnvalue,
93
+ error: job.failedReason,
94
+ createdAt: new Date(job.timestamp),
95
+ processedAt: job.processedOn ? new Date(job.processedOn) : void 0,
96
+ finishedAt: job.finishedOn ? new Date(job.finishedOn) : void 0
97
+ };
98
+ }
99
+ async cancelJob(jobId) {
100
+ const job = await this.queue.getJob(jobId);
101
+ if (job) {
102
+ await job.remove();
103
+ }
104
+ }
105
+ onCompleted(handler) {
106
+ this.queueEvents.on("completed", ({ jobId, returnvalue }) => {
107
+ handler(jobId, returnvalue);
108
+ });
109
+ }
110
+ onFailed(handler) {
111
+ this.queueEvents.on("failed", ({ jobId, failedReason }) => {
112
+ handler(jobId, new Error(failedReason));
113
+ });
114
+ }
115
+ async schedule(opts) {
116
+ if (!opts.agent && !opts.workflow) {
117
+ throw new Error("schedule() requires either agent or workflow");
118
+ }
119
+ if (opts.agent && opts.workflow) {
120
+ throw new Error("schedule() accepts either agent or workflow, not both");
121
+ }
122
+ const repeat = { pattern: opts.cron, ...opts.timezone ? { tz: opts.timezone } : {} };
123
+ if (opts.agent) {
124
+ return this.enqueueAgentRun({
125
+ agentName: opts.agent.name,
126
+ input: opts.agent.input,
127
+ sessionId: opts.agent.sessionId,
128
+ userId: opts.agent.userId,
129
+ repeat
130
+ });
131
+ }
132
+ if (opts.team) {
133
+ return this.enqueueTeamRun({
134
+ teamName: opts.team.name,
135
+ input: opts.team.input,
136
+ sessionId: opts.team.sessionId,
137
+ userId: opts.team.userId,
138
+ repeat
139
+ });
140
+ }
141
+ return this.enqueueWorkflow({
142
+ workflowName: opts.workflow.name,
143
+ initialState: opts.workflow.initialState,
144
+ repeat
145
+ });
146
+ }
147
+ async unschedule(id) {
148
+ const jobs = await this.queue.getRepeatableJobs();
149
+ const match = jobs.find((j) => j.name === `agent:${id}` || j.name === `workflow:${id}` || j.name === id);
150
+ if (!match) {
151
+ throw new Error(`Schedule "${id}" not found`);
152
+ }
153
+ await this.queue.removeRepeatableByKey(match.key);
154
+ }
155
+ async listSchedules() {
156
+ const jobs = await this.queue.getRepeatableJobs();
157
+ return jobs.map((j) => ({
158
+ id: j.name,
159
+ pattern: j.pattern ?? j.cron,
160
+ timezone: j.tz ?? void 0,
161
+ next: j.next ? new Date(j.next) : /* @__PURE__ */ new Date()
162
+ }));
163
+ }
164
+ async close() {
165
+ const results = await Promise.allSettled([this.queue.close(), this.queueEvents.close()]);
166
+ for (const r of results) {
167
+ if (r.status === "rejected") console.warn("[JobProducer] Error during close:", r.reason);
168
+ }
169
+ }
170
+ };
171
+
172
+ // src/job-worker.ts
173
+ var AgentWorker = class {
174
+ worker;
175
+ constructor(config) {
176
+ const queueName = config.queueName ?? "agentium:jobs";
177
+ const concurrency = config.concurrency ?? 5;
178
+ const connection = typeof config.connection === "string" ? { url: config.connection } : config.connection;
179
+ const defaultJobOptions = {
180
+ attempts: config.attempts ?? 3,
181
+ backoff: {
182
+ type: "exponential",
183
+ delay: config.backoffDelay ?? 1e3
184
+ }
185
+ };
186
+ try {
187
+ const { Worker } = __require("bullmq");
188
+ this.worker = new Worker(
189
+ queueName,
190
+ async (job) => {
191
+ const payload = job.data;
192
+ if (payload.type === "agent") {
193
+ const agent = config.agentRegistry[payload.agentName];
194
+ if (!agent) {
195
+ throw new Error(`Agent "${payload.agentName}" not found in registry`);
196
+ }
197
+ const onChunk = (_evt) => {
198
+ job.updateProgress(typeof job.progress === "number" ? job.progress + 1 : 1);
199
+ };
200
+ agent.eventBus.on("run.stream.chunk", onChunk);
201
+ try {
202
+ const result = await agent.run(payload.input, {
203
+ sessionId: payload.sessionId,
204
+ userId: payload.userId
205
+ });
206
+ return result;
207
+ } finally {
208
+ agent.eventBus.off("run.stream.chunk", onChunk);
209
+ }
210
+ }
211
+ if (payload.type === "workflow") {
212
+ const workflow = config.workflowRegistry?.[payload.workflowName];
213
+ if (!workflow) {
214
+ throw new Error(`Workflow "${payload.workflowName}" not found in registry`);
215
+ }
216
+ const result = await workflow.run({
217
+ sessionId: payload.sessionId
218
+ });
219
+ return result;
220
+ }
221
+ if (payload.type === "team") {
222
+ const team = config.teamRegistry?.[payload.teamName];
223
+ if (!team) {
224
+ throw new Error(`Team "${payload.teamName}" not found in registry`);
225
+ }
226
+ const result = await team.run(payload.input, {
227
+ sessionId: payload.sessionId,
228
+ userId: payload.userId
229
+ });
230
+ return result;
231
+ }
232
+ throw new Error(`Unknown job type: ${payload.type}`);
233
+ },
234
+ {
235
+ connection,
236
+ concurrency,
237
+ defaultJobOptions
238
+ }
239
+ );
240
+ } catch (err) {
241
+ if (err?.code === "MODULE_NOT_FOUND" || err?.message?.includes("Cannot find module")) {
242
+ throw new Error("bullmq and ioredis are required for AgentWorker. Install them: npm install bullmq ioredis");
243
+ }
244
+ throw err;
245
+ }
246
+ }
247
+ start() {
248
+ }
249
+ async stop(timeoutMs = 3e4) {
250
+ await Promise.race([
251
+ this.worker.close(),
252
+ new Promise((_, reject) => setTimeout(() => reject(new Error("Worker drain timeout")), timeoutMs))
253
+ ]).catch((err) => {
254
+ console.warn("[JobWorker] Error during stop:", err?.message ?? err);
255
+ });
256
+ }
257
+ };
258
+
259
+ // src/progress.ts
260
+ function bridgeEventBusToJob(eventBus, job, runId) {
261
+ let progressCount = 0;
262
+ const onChunk = (evt) => {
263
+ if (evt.runId !== runId) return;
264
+ progressCount++;
265
+ job.updateProgress(progressCount).catch(() => {
266
+ });
267
+ };
268
+ const onToolCall = (evt) => {
269
+ if (evt.runId !== runId) return;
270
+ job.log(`Tool call: ${evt.toolName}`).catch(() => {
271
+ });
272
+ };
273
+ eventBus.on("run.stream.chunk", onChunk);
274
+ eventBus.on("tool.call", onToolCall);
275
+ return () => {
276
+ eventBus.off("run.stream.chunk", onChunk);
277
+ eventBus.off("tool.call", onToolCall);
278
+ };
279
+ }
280
+ export {
281
+ AgentQueue,
282
+ AgentWorker,
283
+ bridgeEventBusToJob
284
+ };
package/package.json ADDED
@@ -0,0 +1,57 @@
1
+ {
2
+ "name": "@agentium/queue",
3
+ "version": "1.0.0",
4
+ "description": "Background job processing for Agentium agents using BullMQ",
5
+ "license": "MIT",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "https://github.com/agentiumOS/agentium.git",
9
+ "directory": "packages/queue"
10
+ },
11
+ "keywords": [
12
+ "ai",
13
+ "agents",
14
+ "queue",
15
+ "bullmq",
16
+ "redis",
17
+ "background-jobs"
18
+ ],
19
+ "type": "module",
20
+ "main": "./dist/index.cjs",
21
+ "module": "./dist/index.js",
22
+ "types": "./dist/index.d.ts",
23
+ "exports": {
24
+ ".": {
25
+ "types": "./dist/index.d.ts",
26
+ "import": "./dist/index.js",
27
+ "require": "./dist/index.cjs",
28
+ "default": "./dist/index.js"
29
+ }
30
+ },
31
+ "files": [
32
+ "dist"
33
+ ],
34
+ "scripts": {
35
+ "build": "tsup src/index.ts --format esm,cjs --dts --shims --clean",
36
+ "dev": "tsup src/index.ts --format esm,cjs --dts --shims --watch",
37
+ "prepublishOnly": "npm run build"
38
+ },
39
+ "devDependencies": {
40
+ "@types/node": "^25.3.1",
41
+ "tsup": "^8.0.0",
42
+ "typescript": "^5.6.0"
43
+ },
44
+ "peerDependencies": {
45
+ "@agentium/core": "^1.0.0",
46
+ "bullmq": "^5.0.0",
47
+ "ioredis": "^5.0.0"
48
+ },
49
+ "peerDependenciesMeta": {
50
+ "bullmq": {
51
+ "optional": true
52
+ },
53
+ "ioredis": {
54
+ "optional": true
55
+ }
56
+ }
57
+ }