@juspay/neurolink 9.40.0 → 9.41.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/CHANGELOG.md +6 -0
- package/dist/browser/neurolink.min.js +440 -433
- package/dist/cli/commands/task.d.ts +56 -0
- package/dist/cli/commands/task.js +835 -0
- package/dist/cli/parser.js +4 -1
- package/dist/lib/neurolink.d.ts +16 -0
- package/dist/lib/neurolink.js +119 -2
- package/dist/lib/tasks/backends/bullmqBackend.d.ts +32 -0
- package/dist/lib/tasks/backends/bullmqBackend.js +189 -0
- package/dist/lib/tasks/backends/nodeTimeoutBackend.d.ts +27 -0
- package/dist/lib/tasks/backends/nodeTimeoutBackend.js +141 -0
- package/dist/lib/tasks/backends/taskBackendRegistry.d.ts +31 -0
- package/dist/lib/tasks/backends/taskBackendRegistry.js +66 -0
- package/dist/lib/tasks/errors.d.ts +31 -0
- package/dist/lib/tasks/errors.js +18 -0
- package/dist/lib/tasks/store/fileTaskStore.d.ts +43 -0
- package/dist/lib/tasks/store/fileTaskStore.js +179 -0
- package/dist/lib/tasks/store/redisTaskStore.d.ts +42 -0
- package/dist/lib/tasks/store/redisTaskStore.js +189 -0
- package/dist/lib/tasks/taskExecutor.d.ts +21 -0
- package/dist/lib/tasks/taskExecutor.js +166 -0
- package/dist/lib/tasks/taskManager.d.ts +60 -0
- package/dist/lib/tasks/taskManager.js +393 -0
- package/dist/lib/tasks/tools/taskTools.d.ts +135 -0
- package/dist/lib/tasks/tools/taskTools.js +274 -0
- package/dist/lib/types/configTypes.d.ts +3 -0
- package/dist/lib/types/generateTypes.d.ts +13 -0
- package/dist/lib/types/index.d.ts +1 -0
- package/dist/lib/types/taskTypes.d.ts +275 -0
- package/dist/lib/types/taskTypes.js +37 -0
- package/dist/neurolink.d.ts +16 -0
- package/dist/neurolink.js +119 -2
- package/dist/tasks/backends/bullmqBackend.d.ts +32 -0
- package/dist/tasks/backends/bullmqBackend.js +188 -0
- package/dist/tasks/backends/nodeTimeoutBackend.d.ts +27 -0
- package/dist/tasks/backends/nodeTimeoutBackend.js +140 -0
- package/dist/tasks/backends/taskBackendRegistry.d.ts +31 -0
- package/dist/tasks/backends/taskBackendRegistry.js +65 -0
- package/dist/tasks/errors.d.ts +31 -0
- package/dist/tasks/errors.js +17 -0
- package/dist/tasks/store/fileTaskStore.d.ts +43 -0
- package/dist/tasks/store/fileTaskStore.js +178 -0
- package/dist/tasks/store/redisTaskStore.d.ts +42 -0
- package/dist/tasks/store/redisTaskStore.js +188 -0
- package/dist/tasks/taskExecutor.d.ts +21 -0
- package/dist/tasks/taskExecutor.js +165 -0
- package/dist/tasks/taskManager.d.ts +60 -0
- package/dist/tasks/taskManager.js +392 -0
- package/dist/tasks/tools/taskTools.d.ts +135 -0
- package/dist/tasks/tools/taskTools.js +273 -0
- package/dist/types/configTypes.d.ts +3 -0
- package/dist/types/generateTypes.d.ts +13 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/taskTypes.d.ts +275 -0
- package/dist/types/taskTypes.js +36 -0
- package/package.json +3 -1
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Built-in agent tools for TaskManager.
|
|
3
|
+
*
|
|
4
|
+
* These tools allow the AI to self-schedule, manage, and inspect tasks
|
|
5
|
+
* during conversations. Created per-instance via `createTaskTools()` factory,
|
|
6
|
+
* following the same pattern as `createFileTools()` in files/fileTools.ts.
|
|
7
|
+
*
|
|
8
|
+
* @module tasks/tools/taskTools
|
|
9
|
+
*/
|
|
10
|
+
import { tool } from "ai";
|
|
11
|
+
import { z } from "zod";
|
|
12
|
+
import { logger } from "../../utils/logger.js";
|
|
13
|
+
import { TaskError } from "../errors.js";
|
|
14
|
+
/**
|
|
15
|
+
* Parse a schedule object from tool input.
|
|
16
|
+
* Accepts: { type: "cron", expression } | { type: "interval", every } | { type: "once", at }
|
|
17
|
+
*/
|
|
18
|
+
function parseSchedule(input) {
|
|
19
|
+
const type = input.type;
|
|
20
|
+
if (type === "cron") {
|
|
21
|
+
if (!input.expression || typeof input.expression !== "string") {
|
|
22
|
+
throw TaskError.create("SCHEDULE_FAILED", "Cron schedule requires an 'expression' field");
|
|
23
|
+
}
|
|
24
|
+
return {
|
|
25
|
+
type: "cron",
|
|
26
|
+
expression: input.expression,
|
|
27
|
+
...(input.timezone ? { timezone: input.timezone } : {}),
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
if (type === "interval") {
|
|
31
|
+
if (typeof input.every !== "number" ||
|
|
32
|
+
!isFinite(input.every) ||
|
|
33
|
+
input.every <= 0) {
|
|
34
|
+
throw TaskError.create("SCHEDULE_FAILED", "Interval schedule requires a positive 'every' field (milliseconds)");
|
|
35
|
+
}
|
|
36
|
+
return { type: "interval", every: input.every };
|
|
37
|
+
}
|
|
38
|
+
if (type === "once") {
|
|
39
|
+
if (!input.at) {
|
|
40
|
+
throw TaskError.create("SCHEDULE_FAILED", "Once schedule requires an 'at' field (ISO 8601 date string)");
|
|
41
|
+
}
|
|
42
|
+
return { type: "once", at: input.at };
|
|
43
|
+
}
|
|
44
|
+
throw TaskError.create("SCHEDULE_FAILED", `Invalid schedule type: "${type}". Must be "cron", "interval", or "once".`);
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Create task management tools bound to a TaskManager instance.
|
|
48
|
+
*
|
|
49
|
+
* These tools follow the same factory pattern as `createFileTools()` in
|
|
50
|
+
* `src/lib/files/fileTools.ts`. The `manager` is captured via closure,
|
|
51
|
+
* eliminating the need for module-level singleton state.
|
|
52
|
+
*
|
|
53
|
+
* @param manager - The TaskManager instance to bind to
|
|
54
|
+
* @returns Record of tool name to tool definition
|
|
55
|
+
*
|
|
56
|
+
* @example
|
|
57
|
+
* ```typescript
|
|
58
|
+
* const manager = new TaskManager(neurolink, config);
|
|
59
|
+
* const tools = createTaskTools(manager);
|
|
60
|
+
* // tools.createTask, tools.listTasks, tools.getTaskRuns, etc.
|
|
61
|
+
* ```
|
|
62
|
+
*/
|
|
63
|
+
export function createTaskTools(manager) {
|
|
64
|
+
return {
|
|
65
|
+
createTask: tool({
|
|
66
|
+
description: 'Schedule a recurring or one-shot task that runs a prompt on a schedule. Use schedule type "cron" for calendar-based (e.g. "0 9 * * *"), "interval" for fixed frequency (every N milliseconds), or "once" for a single future execution.',
|
|
67
|
+
inputSchema: z.object({
|
|
68
|
+
name: z.string().describe("Human-readable task name"),
|
|
69
|
+
prompt: z.string().describe("The prompt to execute on each run"),
|
|
70
|
+
schedule: z
|
|
71
|
+
.object({
|
|
72
|
+
type: z.enum(["cron", "interval", "once"]),
|
|
73
|
+
expression: z
|
|
74
|
+
.string()
|
|
75
|
+
.optional()
|
|
76
|
+
.describe('Cron expression (for type "cron"), e.g. "0 9 * * *"'),
|
|
77
|
+
timezone: z
|
|
78
|
+
.string()
|
|
79
|
+
.optional()
|
|
80
|
+
.describe('IANA timezone for cron, e.g. "America/New_York"'),
|
|
81
|
+
every: z
|
|
82
|
+
.number()
|
|
83
|
+
.optional()
|
|
84
|
+
.describe('Interval in milliseconds (for type "interval")'),
|
|
85
|
+
at: z
|
|
86
|
+
.string()
|
|
87
|
+
.optional()
|
|
88
|
+
.describe('ISO 8601 timestamp (for type "once")'),
|
|
89
|
+
})
|
|
90
|
+
.describe("When to run the task"),
|
|
91
|
+
mode: z
|
|
92
|
+
.enum(["isolated", "continuation"])
|
|
93
|
+
.optional()
|
|
94
|
+
.describe('Execution mode. "isolated" = fresh context per run (default). "continuation" = preserves conversation history across runs.'),
|
|
95
|
+
}),
|
|
96
|
+
execute: async ({ name, prompt, schedule, mode }) => {
|
|
97
|
+
try {
|
|
98
|
+
const parsedSchedule = parseSchedule(schedule);
|
|
99
|
+
const task = await manager.create({
|
|
100
|
+
name,
|
|
101
|
+
prompt,
|
|
102
|
+
schedule: parsedSchedule,
|
|
103
|
+
mode: mode,
|
|
104
|
+
});
|
|
105
|
+
return {
|
|
106
|
+
success: true,
|
|
107
|
+
taskId: task.id,
|
|
108
|
+
name: task.name,
|
|
109
|
+
status: task.status,
|
|
110
|
+
mode: task.mode,
|
|
111
|
+
nextRunAt: task.nextRunAt,
|
|
112
|
+
schedule: task.schedule,
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
catch (error) {
|
|
116
|
+
logger.error("[taskTools] createTask failed", {
|
|
117
|
+
error: String(error),
|
|
118
|
+
});
|
|
119
|
+
return {
|
|
120
|
+
success: false,
|
|
121
|
+
error: error instanceof Error ? error.message : String(error),
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
},
|
|
125
|
+
}),
|
|
126
|
+
listTasks: tool({
|
|
127
|
+
description: "List all scheduled tasks and their current status.",
|
|
128
|
+
inputSchema: z.object({
|
|
129
|
+
status: z
|
|
130
|
+
.enum([
|
|
131
|
+
"active",
|
|
132
|
+
"paused",
|
|
133
|
+
"completed",
|
|
134
|
+
"failed",
|
|
135
|
+
"cancelled",
|
|
136
|
+
"pending",
|
|
137
|
+
])
|
|
138
|
+
.optional()
|
|
139
|
+
.describe("Filter by task status"),
|
|
140
|
+
}),
|
|
141
|
+
execute: async ({ status }) => {
|
|
142
|
+
try {
|
|
143
|
+
const tasks = await manager.list(status
|
|
144
|
+
? {
|
|
145
|
+
status: status,
|
|
146
|
+
}
|
|
147
|
+
: undefined);
|
|
148
|
+
return {
|
|
149
|
+
success: true,
|
|
150
|
+
count: tasks.length,
|
|
151
|
+
tasks: tasks.map((t) => ({
|
|
152
|
+
taskId: t.id,
|
|
153
|
+
name: t.name,
|
|
154
|
+
status: t.status,
|
|
155
|
+
mode: t.mode,
|
|
156
|
+
schedule: t.schedule,
|
|
157
|
+
runCount: t.runCount,
|
|
158
|
+
lastRunAt: t.lastRunAt,
|
|
159
|
+
nextRunAt: t.nextRunAt,
|
|
160
|
+
})),
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
catch (error) {
|
|
164
|
+
logger.error("[taskTools] listTasks failed", {
|
|
165
|
+
error: String(error),
|
|
166
|
+
});
|
|
167
|
+
return {
|
|
168
|
+
success: false,
|
|
169
|
+
error: error instanceof Error ? error.message : String(error),
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
},
|
|
173
|
+
}),
|
|
174
|
+
getTaskRuns: tool({
|
|
175
|
+
description: "Get the run history of a scheduled task, showing recent executions and their results.",
|
|
176
|
+
inputSchema: z.object({
|
|
177
|
+
taskId: z.string().describe("The task ID"),
|
|
178
|
+
limit: z
|
|
179
|
+
.number()
|
|
180
|
+
.optional()
|
|
181
|
+
.describe("Max results to return (default: 10)"),
|
|
182
|
+
}),
|
|
183
|
+
execute: async ({ taskId, limit }) => {
|
|
184
|
+
try {
|
|
185
|
+
const runs = await manager.runs(taskId, { limit: limit ?? 10 });
|
|
186
|
+
return {
|
|
187
|
+
success: true,
|
|
188
|
+
taskId,
|
|
189
|
+
count: runs.length,
|
|
190
|
+
runs: runs.map((r) => ({
|
|
191
|
+
runId: r.runId,
|
|
192
|
+
status: r.status,
|
|
193
|
+
output: r.output
|
|
194
|
+
? r.output.length > 500
|
|
195
|
+
? r.output.slice(0, 500) + "..."
|
|
196
|
+
: r.output
|
|
197
|
+
: undefined,
|
|
198
|
+
durationMs: r.durationMs,
|
|
199
|
+
timestamp: r.timestamp,
|
|
200
|
+
error: r.error,
|
|
201
|
+
})),
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
catch (error) {
|
|
205
|
+
logger.error("[taskTools] getTaskRuns failed", {
|
|
206
|
+
error: String(error),
|
|
207
|
+
});
|
|
208
|
+
return {
|
|
209
|
+
success: false,
|
|
210
|
+
error: error instanceof Error ? error.message : String(error),
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
},
|
|
214
|
+
}),
|
|
215
|
+
deleteTask: tool({
|
|
216
|
+
description: "Cancel and permanently remove a scheduled task.",
|
|
217
|
+
inputSchema: z.object({
|
|
218
|
+
taskId: z.string().describe("The task ID to delete"),
|
|
219
|
+
}),
|
|
220
|
+
execute: async ({ taskId }) => {
|
|
221
|
+
try {
|
|
222
|
+
const task = await manager.get(taskId);
|
|
223
|
+
if (!task) {
|
|
224
|
+
return { success: false, error: `Task not found: ${taskId}` };
|
|
225
|
+
}
|
|
226
|
+
await manager.delete(taskId);
|
|
227
|
+
return { success: true, deletedTask: task.name, taskId };
|
|
228
|
+
}
|
|
229
|
+
catch (error) {
|
|
230
|
+
logger.error("[taskTools] deleteTask failed", {
|
|
231
|
+
error: String(error),
|
|
232
|
+
});
|
|
233
|
+
return {
|
|
234
|
+
success: false,
|
|
235
|
+
error: error instanceof Error ? error.message : String(error),
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
},
|
|
239
|
+
}),
|
|
240
|
+
runTaskNow: tool({
|
|
241
|
+
description: "Immediately execute a scheduled task outside of its normal schedule. Returns the run result.",
|
|
242
|
+
inputSchema: z.object({
|
|
243
|
+
taskId: z.string().describe("The task ID to run"),
|
|
244
|
+
}),
|
|
245
|
+
execute: async ({ taskId }) => {
|
|
246
|
+
try {
|
|
247
|
+
const result = await manager.run(taskId);
|
|
248
|
+
return {
|
|
249
|
+
success: true,
|
|
250
|
+
runId: result.runId,
|
|
251
|
+
status: result.status,
|
|
252
|
+
output: result.output
|
|
253
|
+
? result.output.length > 1000
|
|
254
|
+
? result.output.slice(0, 1000) + "..."
|
|
255
|
+
: result.output
|
|
256
|
+
: undefined,
|
|
257
|
+
durationMs: result.durationMs,
|
|
258
|
+
error: result.error,
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
catch (error) {
|
|
262
|
+
logger.error("[taskTools] runTaskNow failed", {
|
|
263
|
+
error: String(error),
|
|
264
|
+
});
|
|
265
|
+
return {
|
|
266
|
+
success: false,
|
|
267
|
+
error: error instanceof Error ? error.message : String(error),
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
},
|
|
271
|
+
}),
|
|
272
|
+
};
|
|
273
|
+
}
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* Centralized configuration type definitions following the established architecture pattern
|
|
4
4
|
*/
|
|
5
5
|
import { MCPToolRegistry } from "../mcp/toolRegistry.js";
|
|
6
|
+
import type { TaskManagerConfig } from "./taskTypes.js";
|
|
6
7
|
import type { HITLConfig } from "../types/hitlTypes.js";
|
|
7
8
|
import type { ConversationMemoryConfig } from "./conversation.js";
|
|
8
9
|
import type { ObservabilityConfig } from "./observability.js";
|
|
@@ -36,6 +37,8 @@ export type NeurolinkConstructorConfig = {
|
|
|
36
37
|
mcp?: MCPEnhancementsConfig;
|
|
37
38
|
/** Authentication provider configuration */
|
|
38
39
|
auth?: NeuroLinkAuthConfig;
|
|
40
|
+
/** TaskManager configuration (scheduled and self-running tasks) */
|
|
41
|
+
tasks?: TaskManagerConfig;
|
|
39
42
|
};
|
|
40
43
|
/**
|
|
41
44
|
* Configuration for MCP enhancement modules wired into generate()/stream() paths.
|
|
@@ -315,10 +315,23 @@ export type GenerateOptions = {
|
|
|
315
315
|
context?: StandardRecord;
|
|
316
316
|
evaluationDomain?: string;
|
|
317
317
|
toolUsageContext?: string;
|
|
318
|
+
/**
|
|
319
|
+
* @deprecated Use `conversationMessages` instead. This field uses a simple `{role, content}` shape
|
|
320
|
+
* that is not consumed by `buildMessagesArray()` — messages passed here will NOT reach the AI model
|
|
321
|
+
* as proper conversation turns. `conversationMessages` uses the full `ChatMessage` type and is
|
|
322
|
+
* correctly wired through the entire generate pipeline.
|
|
323
|
+
*/
|
|
318
324
|
conversationHistory?: Array<{
|
|
319
325
|
role: string;
|
|
320
326
|
content: string;
|
|
321
327
|
}>;
|
|
328
|
+
/**
|
|
329
|
+
* Previous conversation as a ChatMessage array.
|
|
330
|
+
* Messages are injected as proper multi-turn conversation history before the current prompt,
|
|
331
|
+
* so the AI model sees them as real prior exchanges (not text dumped into the prompt).
|
|
332
|
+
* Used by task continuation mode and available to external callers.
|
|
333
|
+
*/
|
|
334
|
+
conversationMessages?: ChatMessage[];
|
|
322
335
|
factoryConfig?: {
|
|
323
336
|
domainType?: string;
|
|
324
337
|
domainConfig?: StandardRecord;
|
package/dist/types/index.d.ts
CHANGED
|
@@ -46,3 +46,4 @@ export type { ClientConfig, RequestOptions as ClientRequestOptions, RetryConfig
|
|
|
46
46
|
export type { TokenRefresher } from "./subscriptionTypes.js";
|
|
47
47
|
export * from "./proxyTypes.js";
|
|
48
48
|
export type { AuthProviderType, AuthProviderConfig, MastraAuthProvider, BetterAuthConfig, Auth0Config, ClerkConfig, FirebaseConfig, SupabaseConfig, WorkOSConfig, JWTConfig, OAuth2Config, CognitoConfig, KeycloakConfig, CustomAuthConfig, BaseAuthProviderConfig, AuthUser, AuthSession, TokenType, TokenValidationResult as AuthTokenValidationResult, TokenClaims, JWK, JWKS, TokenRefreshResult, TokenValidationConfig, TokenExtractionConfig, SessionValidationResult, SessionStorage, AuthorizationResult, AuthRequestContext, AuthenticatedContext, TokenExtractionStrategy, SessionConfig, SessionStorageType, RBACConfig, PermissionDefinition, AuthCacheConfig, AuthMiddlewareOptions, AuthMiddlewareConfig, RBACMiddlewareConfig, AuthErrorCode, AuthErrorInfo, AuthErrorInfo as AuthErrorType, AuthEventType, AuthEventData, AuthEventHandler, AuthProviderFactoryFn, AuthProviderRegistration, AuthHealthCheck, AuthProviderHealthCheck, AuthEvents, AuthProviderMetadata, AuthProviderHealthStatus, AuthTokenValidator, AuthUserAuthorizer, AuthSessionManager, AuthRequestHandler, AuthUserManager, AuthLifecycle, } from "./authTypes.js";
|
|
49
|
+
export type { Task, TaskDefinition, TaskSchedule, TaskScheduleType, CronSchedule, IntervalSchedule, OnceSchedule, TaskExecutionMode, TaskStatus, TaskRunResult, TaskRunError, TaskStore, TaskBackend, TaskBackendName, TaskManagerConfig, TaskRetentionConfig, ConversationEntry as TaskConversationEntry, } from "./taskTypes.js";
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type definitions for the NeuroLink TaskManager system.
|
|
3
|
+
*
|
|
4
|
+
* TaskManager provides scheduled and self-running task capabilities,
|
|
5
|
+
* enabling AI agents to execute prompts on cron, interval, or one-shot schedules.
|
|
6
|
+
*/
|
|
7
|
+
import type { Cron } from "croner";
|
|
8
|
+
import type { createClient } from "redis";
|
|
9
|
+
import type { ThinkingLevel } from "./configTypes.js";
|
|
10
|
+
export type TaskScheduleType = "cron" | "interval" | "once";
|
|
11
|
+
export type CronSchedule = {
|
|
12
|
+
type: "cron";
|
|
13
|
+
/** Standard 5-field cron expression, e.g. "0 9 * * *" */
|
|
14
|
+
expression: string;
|
|
15
|
+
/** IANA timezone, e.g. "America/New_York" */
|
|
16
|
+
timezone?: string;
|
|
17
|
+
};
|
|
18
|
+
export type IntervalSchedule = {
|
|
19
|
+
type: "interval";
|
|
20
|
+
/** Interval in milliseconds */
|
|
21
|
+
every: number;
|
|
22
|
+
};
|
|
23
|
+
export type OnceSchedule = {
|
|
24
|
+
type: "once";
|
|
25
|
+
/** ISO 8601 timestamp or Date object */
|
|
26
|
+
at: Date | string;
|
|
27
|
+
};
|
|
28
|
+
export type TaskSchedule = CronSchedule | IntervalSchedule | OnceSchedule;
|
|
29
|
+
/**
|
|
30
|
+
* - "isolated": Each run gets a fresh context. No memory of previous runs.
|
|
31
|
+
* - "continuation": Conversation history is preserved across runs.
|
|
32
|
+
*/
|
|
33
|
+
export type TaskExecutionMode = "isolated" | "continuation";
|
|
34
|
+
export type TaskStatus = "pending" | "active" | "paused" | "completed" | "failed" | "cancelled";
|
|
35
|
+
export type TaskDefinition = {
|
|
36
|
+
name: string;
|
|
37
|
+
prompt: string;
|
|
38
|
+
schedule: TaskSchedule;
|
|
39
|
+
mode?: TaskExecutionMode;
|
|
40
|
+
provider?: string;
|
|
41
|
+
model?: string;
|
|
42
|
+
thinkingLevel?: ThinkingLevel;
|
|
43
|
+
systemPrompt?: string;
|
|
44
|
+
/** Enable/disable tools for this task. Default: true */
|
|
45
|
+
tools?: boolean;
|
|
46
|
+
maxTokens?: number;
|
|
47
|
+
temperature?: number;
|
|
48
|
+
/** Max number of executions. Omit for unlimited. */
|
|
49
|
+
maxRuns?: number;
|
|
50
|
+
/** Per-run timeout in ms. Default: 120000 */
|
|
51
|
+
timeout?: number;
|
|
52
|
+
retry?: {
|
|
53
|
+
/** Default: 3 */
|
|
54
|
+
maxAttempts?: number;
|
|
55
|
+
/** Default: [30000, 60000, 300000] */
|
|
56
|
+
backoffMs?: number[];
|
|
57
|
+
};
|
|
58
|
+
onSuccess?: (result: TaskRunResult) => void | Promise<void>;
|
|
59
|
+
onError?: (error: TaskRunError) => void | Promise<void>;
|
|
60
|
+
/** Called when task reaches a terminal state (completed, failed, cancelled) */
|
|
61
|
+
onComplete?: (task: Task) => void | Promise<void>;
|
|
62
|
+
metadata?: Record<string, unknown>;
|
|
63
|
+
};
|
|
64
|
+
export type Task = {
|
|
65
|
+
id: string;
|
|
66
|
+
name: string;
|
|
67
|
+
prompt: string;
|
|
68
|
+
schedule: TaskSchedule;
|
|
69
|
+
mode: TaskExecutionMode;
|
|
70
|
+
status: TaskStatus;
|
|
71
|
+
provider?: string;
|
|
72
|
+
model?: string;
|
|
73
|
+
thinkingLevel?: ThinkingLevel;
|
|
74
|
+
systemPrompt?: string;
|
|
75
|
+
tools: boolean;
|
|
76
|
+
maxTokens?: number;
|
|
77
|
+
temperature?: number;
|
|
78
|
+
maxRuns?: number;
|
|
79
|
+
timeout: number;
|
|
80
|
+
retry: {
|
|
81
|
+
maxAttempts: number;
|
|
82
|
+
backoffMs: number[];
|
|
83
|
+
};
|
|
84
|
+
runCount: number;
|
|
85
|
+
lastRunAt?: string;
|
|
86
|
+
nextRunAt?: string;
|
|
87
|
+
createdAt: string;
|
|
88
|
+
updatedAt: string;
|
|
89
|
+
/** Conversation session ID for continuation mode */
|
|
90
|
+
sessionId?: string;
|
|
91
|
+
metadata?: Record<string, unknown>;
|
|
92
|
+
};
|
|
93
|
+
/** Shape of the tasks.json file used by FileTaskStore */
|
|
94
|
+
export type TasksFile = {
|
|
95
|
+
version: number;
|
|
96
|
+
tasks: Record<string, Task>;
|
|
97
|
+
};
|
|
98
|
+
export type TaskRunResult = {
|
|
99
|
+
taskId: string;
|
|
100
|
+
runId: string;
|
|
101
|
+
status: "success" | "error";
|
|
102
|
+
/** AI response text */
|
|
103
|
+
output?: string;
|
|
104
|
+
toolCalls?: Array<{
|
|
105
|
+
name: string;
|
|
106
|
+
input: unknown;
|
|
107
|
+
output: unknown;
|
|
108
|
+
}>;
|
|
109
|
+
tokensUsed?: {
|
|
110
|
+
input: number;
|
|
111
|
+
output: number;
|
|
112
|
+
};
|
|
113
|
+
durationMs: number;
|
|
114
|
+
/** ISO 8601 */
|
|
115
|
+
timestamp: string;
|
|
116
|
+
error?: string;
|
|
117
|
+
};
|
|
118
|
+
export type TaskRunError = {
|
|
119
|
+
taskId: string;
|
|
120
|
+
runId: string;
|
|
121
|
+
error: string;
|
|
122
|
+
attempt: number;
|
|
123
|
+
maxAttempts: number;
|
|
124
|
+
willRetry: boolean;
|
|
125
|
+
/** ISO 8601 */
|
|
126
|
+
timestamp: string;
|
|
127
|
+
};
|
|
128
|
+
/**
|
|
129
|
+
* Abstracts task persistence. Auto-selected based on backend:
|
|
130
|
+
* - BullMQ → RedisTaskStore
|
|
131
|
+
* - NodeTimeout → FileTaskStore
|
|
132
|
+
*/
|
|
133
|
+
export interface TaskStore {
|
|
134
|
+
readonly type: "redis" | "file";
|
|
135
|
+
initialize(): Promise<void>;
|
|
136
|
+
shutdown(): Promise<void>;
|
|
137
|
+
save(task: Task): Promise<void>;
|
|
138
|
+
get(taskId: string): Promise<Task | null>;
|
|
139
|
+
list(filter?: {
|
|
140
|
+
status?: TaskStatus;
|
|
141
|
+
}): Promise<Task[]>;
|
|
142
|
+
update(taskId: string, updates: Partial<Task>): Promise<Task>;
|
|
143
|
+
delete(taskId: string): Promise<void>;
|
|
144
|
+
appendRun(taskId: string, run: TaskRunResult): Promise<void>;
|
|
145
|
+
getRuns(taskId: string, options?: {
|
|
146
|
+
limit?: number;
|
|
147
|
+
status?: string;
|
|
148
|
+
}): Promise<TaskRunResult[]>;
|
|
149
|
+
appendHistory(taskId: string, messages: ConversationEntry[]): Promise<void>;
|
|
150
|
+
getHistory(taskId: string): Promise<ConversationEntry[]>;
|
|
151
|
+
clearHistory(taskId: string): Promise<void>;
|
|
152
|
+
}
|
|
153
|
+
export type ConversationEntry = {
|
|
154
|
+
role: "user" | "assistant";
|
|
155
|
+
content: string;
|
|
156
|
+
timestamp: string;
|
|
157
|
+
};
|
|
158
|
+
export type TaskExecutorFn = (task: Task) => Promise<TaskRunResult>;
|
|
159
|
+
/**
|
|
160
|
+
* Abstracts the scheduling/looping mechanism.
|
|
161
|
+
* Implementations: BullMQ (production), NodeTimeout (development).
|
|
162
|
+
*/
|
|
163
|
+
export interface TaskBackend {
|
|
164
|
+
readonly name: string;
|
|
165
|
+
initialize(): Promise<void>;
|
|
166
|
+
shutdown(): Promise<void>;
|
|
167
|
+
/** Schedule a task for execution */
|
|
168
|
+
schedule(task: Task, executor: TaskExecutorFn): Promise<void>;
|
|
169
|
+
/** Cancel a scheduled task */
|
|
170
|
+
cancel(taskId: string): Promise<void>;
|
|
171
|
+
/** Pause a task's schedule */
|
|
172
|
+
pause(taskId: string): Promise<void>;
|
|
173
|
+
/** Resume a paused task */
|
|
174
|
+
resume(taskId: string): Promise<void>;
|
|
175
|
+
/** Check if backend is operational */
|
|
176
|
+
isHealthy(): Promise<boolean>;
|
|
177
|
+
}
|
|
178
|
+
/** Redis client type used by RedisTaskStore */
|
|
179
|
+
export type RedisClient = ReturnType<typeof createClient>;
|
|
180
|
+
/** Minimal interface for the NeuroLink SDK methods needed by TaskExecutor */
|
|
181
|
+
export type NeuroLinkExecutable = {
|
|
182
|
+
generate(optionsOrPrompt: unknown): Promise<{
|
|
183
|
+
content: string;
|
|
184
|
+
toolExecutions?: Array<{
|
|
185
|
+
name: string;
|
|
186
|
+
input: unknown;
|
|
187
|
+
output: unknown;
|
|
188
|
+
}>;
|
|
189
|
+
usage?: {
|
|
190
|
+
input?: number;
|
|
191
|
+
output?: number;
|
|
192
|
+
};
|
|
193
|
+
}>;
|
|
194
|
+
};
|
|
195
|
+
/** Internal scheduling entry used by NodeTimeoutBackend */
|
|
196
|
+
export type ScheduledEntry = {
|
|
197
|
+
taskId: string;
|
|
198
|
+
executor: TaskExecutorFn;
|
|
199
|
+
task: Task;
|
|
200
|
+
cronJob?: Cron;
|
|
201
|
+
intervalId?: ReturnType<typeof setInterval>;
|
|
202
|
+
timeoutId?: ReturnType<typeof setTimeout>;
|
|
203
|
+
};
|
|
204
|
+
export type TaskBackendName = "bullmq" | "node-timeout";
|
|
205
|
+
export type TaskBackendFactoryFn = (config: TaskManagerConfig) => Promise<TaskBackend>;
|
|
206
|
+
export type TaskRetentionConfig = {
|
|
207
|
+
/** Auto-delete completed tasks after N ms. Default: 30 days */
|
|
208
|
+
completedTTL?: number;
|
|
209
|
+
/** Auto-delete failed tasks after N ms. Default: 7 days */
|
|
210
|
+
failedTTL?: number;
|
|
211
|
+
/** Auto-delete cancelled tasks after N ms. Default: 7 days */
|
|
212
|
+
cancelledTTL?: number;
|
|
213
|
+
/** Auto-expire individual run log entries after N ms. Default: 30 days */
|
|
214
|
+
runLogTTL?: number;
|
|
215
|
+
};
|
|
216
|
+
export type TaskManagerConfig = {
|
|
217
|
+
/** Default: true */
|
|
218
|
+
enabled?: boolean;
|
|
219
|
+
/** Default: "bullmq" */
|
|
220
|
+
backend?: TaskBackendName;
|
|
221
|
+
redis?: {
|
|
222
|
+
host?: string;
|
|
223
|
+
port?: number;
|
|
224
|
+
password?: string;
|
|
225
|
+
db?: number;
|
|
226
|
+
/** Alternative: full Redis URL */
|
|
227
|
+
url?: string;
|
|
228
|
+
};
|
|
229
|
+
/** Default: ".neurolink/tasks/tasks.json" */
|
|
230
|
+
storePath?: string;
|
|
231
|
+
/** Default: ".neurolink/tasks/runs/" */
|
|
232
|
+
logsPath?: string;
|
|
233
|
+
/** Maximum number of tasks that can exist at once. Default: 100 */
|
|
234
|
+
maxTasks?: number;
|
|
235
|
+
/** Default: 5 */
|
|
236
|
+
maxConcurrentRuns?: number;
|
|
237
|
+
/** Max run log entries per task. Default: 2000 */
|
|
238
|
+
maxRunLogs?: number;
|
|
239
|
+
/** Max continuation history entries per task. Default: 200 (100 exchanges) */
|
|
240
|
+
maxHistoryEntries?: number;
|
|
241
|
+
taskRetention?: TaskRetentionConfig;
|
|
242
|
+
};
|
|
243
|
+
export declare const TASK_DEFAULTS: {
|
|
244
|
+
readonly enabled: true;
|
|
245
|
+
readonly maxTasks: 100;
|
|
246
|
+
readonly backend: TaskBackendName;
|
|
247
|
+
readonly mode: TaskExecutionMode;
|
|
248
|
+
readonly timeout: 120000;
|
|
249
|
+
readonly maxConcurrentRuns: 5;
|
|
250
|
+
readonly maxRunLogs: 2000;
|
|
251
|
+
readonly maxHistoryEntries: 200;
|
|
252
|
+
readonly tools: true;
|
|
253
|
+
readonly retry: {
|
|
254
|
+
readonly maxAttempts: 3;
|
|
255
|
+
readonly backoffMs: readonly [30000, 60000, 300000];
|
|
256
|
+
};
|
|
257
|
+
readonly storePath: ".neurolink/tasks/tasks.json";
|
|
258
|
+
readonly logsPath: ".neurolink/tasks/runs/";
|
|
259
|
+
readonly redis: {
|
|
260
|
+
readonly host: "localhost";
|
|
261
|
+
readonly port: 6379;
|
|
262
|
+
};
|
|
263
|
+
readonly retention: {
|
|
264
|
+
readonly completedTTL: number;
|
|
265
|
+
readonly failedTTL: number;
|
|
266
|
+
readonly cancelledTTL: number;
|
|
267
|
+
readonly runLogTTL: number;
|
|
268
|
+
};
|
|
269
|
+
};
|
|
270
|
+
/** State persisted by the CLI task worker daemon */
|
|
271
|
+
export type WorkerState = {
|
|
272
|
+
pid: number;
|
|
273
|
+
startedAt: string;
|
|
274
|
+
logFile: string;
|
|
275
|
+
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type definitions for the NeuroLink TaskManager system.
|
|
3
|
+
*
|
|
4
|
+
* TaskManager provides scheduled and self-running task capabilities,
|
|
5
|
+
* enabling AI agents to execute prompts on cron, interval, or one-shot schedules.
|
|
6
|
+
*/
|
|
7
|
+
// ── Defaults ────────────────────────────────────────────
|
|
8
|
+
const THIRTY_DAYS_MS = 30 * 24 * 60 * 60 * 1000;
|
|
9
|
+
const SEVEN_DAYS_MS = 7 * 24 * 60 * 60 * 1000;
|
|
10
|
+
export const TASK_DEFAULTS = {
|
|
11
|
+
enabled: true,
|
|
12
|
+
maxTasks: 100,
|
|
13
|
+
backend: "bullmq",
|
|
14
|
+
mode: "isolated",
|
|
15
|
+
timeout: 120_000,
|
|
16
|
+
maxConcurrentRuns: 5,
|
|
17
|
+
maxRunLogs: 2000,
|
|
18
|
+
maxHistoryEntries: 200,
|
|
19
|
+
tools: true,
|
|
20
|
+
retry: {
|
|
21
|
+
maxAttempts: 3,
|
|
22
|
+
backoffMs: [30_000, 60_000, 300_000],
|
|
23
|
+
},
|
|
24
|
+
storePath: ".neurolink/tasks/tasks.json",
|
|
25
|
+
logsPath: ".neurolink/tasks/runs/",
|
|
26
|
+
redis: {
|
|
27
|
+
host: "localhost",
|
|
28
|
+
port: 6379,
|
|
29
|
+
},
|
|
30
|
+
retention: {
|
|
31
|
+
completedTTL: THIRTY_DAYS_MS,
|
|
32
|
+
failedTTL: SEVEN_DAYS_MS,
|
|
33
|
+
cancelledTTL: SEVEN_DAYS_MS,
|
|
34
|
+
runLogTTL: THIRTY_DAYS_MS,
|
|
35
|
+
},
|
|
36
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@juspay/neurolink",
|
|
3
|
-
"version": "9.
|
|
3
|
+
"version": "9.41.0",
|
|
4
4
|
"description": "Universal AI Development Platform with working MCP integration, multi-provider support, and professional CLI. Built-in tools operational, 58+ external MCP servers discoverable. Connect to filesystem, GitHub, database operations, and more. Build, test, and deploy AI applications with 13 providers: OpenAI, Anthropic, Google AI, AWS Bedrock, Azure, Hugging Face, Ollama, and Mistral AI.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Juspay Technologies",
|
|
@@ -212,6 +212,8 @@
|
|
|
212
212
|
"@opentelemetry/semantic-conventions": "^1.40.0",
|
|
213
213
|
"adm-zip": "^0.5.16",
|
|
214
214
|
"ai": "^6.0.134",
|
|
215
|
+
"bullmq": "^5.52.2",
|
|
216
|
+
"croner": "^9.1.0",
|
|
215
217
|
"chalk": "^5.6.2",
|
|
216
218
|
"csv-parser": "^3.2.0",
|
|
217
219
|
"dotenv": "^17.3.1",
|