@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,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,37 @@
|
|
|
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
|
+
};
|
|
37
|
+
//# sourceMappingURL=taskTypes.js.map
|
package/dist/neurolink.d.ts
CHANGED
|
@@ -25,11 +25,14 @@ import type { ObservabilityConfig } from "./types/observability.js";
|
|
|
25
25
|
import type { StreamOptions, StreamResult } from "./types/streamTypes.js";
|
|
26
26
|
import type { ToolExecutionContext, ToolExecutionSummary, ToolInfo, ToolRegistrationOptions } from "./types/tools.js";
|
|
27
27
|
import type { BatchOperationResult } from "./types/typeAliases.js";
|
|
28
|
+
import { TaskManager } from "./tasks/taskManager.js";
|
|
28
29
|
export declare class NeuroLink {
|
|
29
30
|
private mcpInitialized;
|
|
30
31
|
private mcpSkipped;
|
|
31
32
|
private mcpInitPromise;
|
|
32
33
|
private emitter;
|
|
34
|
+
private _taskManager?;
|
|
35
|
+
private _taskManagerConfig?;
|
|
33
36
|
private toolRegistry;
|
|
34
37
|
private autoDiscoveredServerInfos;
|
|
35
38
|
private externalServerManager;
|
|
@@ -148,6 +151,13 @@ export declare class NeuroLink {
|
|
|
148
151
|
*/
|
|
149
152
|
private get _metricsTraceContext();
|
|
150
153
|
constructor(config?: NeurolinkConstructorConfig);
|
|
154
|
+
/**
|
|
155
|
+
* TaskManager — scheduled and self-running tasks.
|
|
156
|
+
* Lazy-initialized on first access. Configurable via constructor `tasks` option.
|
|
157
|
+
* The actual async initialization (Redis connect, backend start) happens
|
|
158
|
+
* lazily inside TaskManager on first operation.
|
|
159
|
+
*/
|
|
160
|
+
get tasks(): TaskManager;
|
|
151
161
|
/**
|
|
152
162
|
* Initialize provider registry with security settings
|
|
153
163
|
*/
|
|
@@ -173,6 +183,12 @@ export declare class NeuroLink {
|
|
|
173
183
|
* and registers them as direct tools so they're available to LLMs.
|
|
174
184
|
*/
|
|
175
185
|
private registerFileTools;
|
|
186
|
+
/**
|
|
187
|
+
* Register task management tools bound to a TaskManager instance.
|
|
188
|
+
* Follows the same factory + registry pattern as registerFileTools().
|
|
189
|
+
* Called when TaskManager is created (eagerly or lazily via the `tasks` getter).
|
|
190
|
+
*/
|
|
191
|
+
private registerTaskTools;
|
|
176
192
|
/**
|
|
177
193
|
* Register memory retrieval tools that allow the AI to access
|
|
178
194
|
* conversation history, including full tool outputs.
|
package/dist/neurolink.js
CHANGED
|
@@ -75,6 +75,8 @@ import { isNonNullObject } from "./utils/typeUtils.js";
|
|
|
75
75
|
import { resolveModel } from "./utils/modelAliasResolver.js";
|
|
76
76
|
import { getWorkflow } from "./workflow/core/workflowRegistry.js";
|
|
77
77
|
import { runWorkflow } from "./workflow/core/workflowRunner.js";
|
|
78
|
+
import { TaskManager } from "./tasks/taskManager.js";
|
|
79
|
+
import { createTaskTools } from "./tasks/tools/taskTools.js";
|
|
78
80
|
/**
|
|
79
81
|
* NL-002: Classify MCP error messages into categories for AI disambiguation.
|
|
80
82
|
* Returns a human-readable error category based on error message content.
|
|
@@ -187,6 +189,9 @@ export class NeuroLink {
|
|
|
187
189
|
mcpSkipped = false;
|
|
188
190
|
mcpInitPromise = null;
|
|
189
191
|
emitter = new EventEmitter();
|
|
192
|
+
// TaskManager — lazy-initialized on first access via `this.tasks`
|
|
193
|
+
_taskManager;
|
|
194
|
+
_taskManagerConfig;
|
|
190
195
|
toolRegistry;
|
|
191
196
|
autoDiscoveredServerInfos = [];
|
|
192
197
|
// External MCP server management
|
|
@@ -459,6 +464,28 @@ export class NeuroLink {
|
|
|
459
464
|
if (config?.auth) {
|
|
460
465
|
this.pendingAuthConfig = config.auth;
|
|
461
466
|
}
|
|
467
|
+
// Store task config for lazy initialization
|
|
468
|
+
this._taskManagerConfig = config?.tasks;
|
|
469
|
+
// Eagerly create TaskManager and register tools if config is provided
|
|
470
|
+
if (this._taskManagerConfig) {
|
|
471
|
+
this._taskManager = new TaskManager(this, this._taskManagerConfig);
|
|
472
|
+
this._taskManager.setEmitter(this.emitter);
|
|
473
|
+
this.registerTaskTools(this._taskManager);
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
/**
|
|
477
|
+
* TaskManager — scheduled and self-running tasks.
|
|
478
|
+
* Lazy-initialized on first access. Configurable via constructor `tasks` option.
|
|
479
|
+
* The actual async initialization (Redis connect, backend start) happens
|
|
480
|
+
* lazily inside TaskManager on first operation.
|
|
481
|
+
*/
|
|
482
|
+
get tasks() {
|
|
483
|
+
if (!this._taskManager) {
|
|
484
|
+
this._taskManager = new TaskManager(this, this._taskManagerConfig);
|
|
485
|
+
this._taskManager.setEmitter(this.emitter);
|
|
486
|
+
this.registerTaskTools(this._taskManager);
|
|
487
|
+
}
|
|
488
|
+
return this._taskManager;
|
|
462
489
|
}
|
|
463
490
|
/**
|
|
464
491
|
* Initialize provider registry with security settings
|
|
@@ -716,6 +743,52 @@ export class NeuroLink {
|
|
|
716
743
|
logger.debug(`[NeuroLink] Registered ${Object.keys(fileTools).length} file reference tools`);
|
|
717
744
|
});
|
|
718
745
|
}
|
|
746
|
+
/**
|
|
747
|
+
* Register task management tools bound to a TaskManager instance.
|
|
748
|
+
* Follows the same factory + registry pattern as registerFileTools().
|
|
749
|
+
* Called when TaskManager is created (eagerly or lazily via the `tasks` getter).
|
|
750
|
+
*/
|
|
751
|
+
registerTaskTools(manager) {
|
|
752
|
+
const taskTools = createTaskTools(manager);
|
|
753
|
+
for (const [toolName, toolDef] of Object.entries(taskTools)) {
|
|
754
|
+
const toolId = `direct.${toolName}`;
|
|
755
|
+
const toolInfo = {
|
|
756
|
+
name: toolName,
|
|
757
|
+
description: toolDef.description || `Task tool: ${toolName}`,
|
|
758
|
+
inputSchema: {},
|
|
759
|
+
serverId: "direct",
|
|
760
|
+
category: "built-in",
|
|
761
|
+
};
|
|
762
|
+
// registerTool is async but its core logic is synchronous (Map.set).
|
|
763
|
+
// We fire-and-forget here but tools are available immediately after
|
|
764
|
+
// the synchronous validation + map insertion completes.
|
|
765
|
+
void this.toolRegistry.registerTool(toolId, toolInfo, {
|
|
766
|
+
execute: async (params) => {
|
|
767
|
+
try {
|
|
768
|
+
const result = await toolDef.execute(params, {
|
|
769
|
+
toolCallId: "task-tool",
|
|
770
|
+
messages: [],
|
|
771
|
+
});
|
|
772
|
+
return {
|
|
773
|
+
success: true,
|
|
774
|
+
data: result,
|
|
775
|
+
metadata: { toolName, serverId: "direct", executionTime: 0 },
|
|
776
|
+
};
|
|
777
|
+
}
|
|
778
|
+
catch (error) {
|
|
779
|
+
return {
|
|
780
|
+
success: false,
|
|
781
|
+
error: error instanceof Error ? error.message : String(error),
|
|
782
|
+
metadata: { toolName, serverId: "direct", executionTime: 0 },
|
|
783
|
+
};
|
|
784
|
+
}
|
|
785
|
+
},
|
|
786
|
+
description: toolDef.description,
|
|
787
|
+
inputSchema: {},
|
|
788
|
+
});
|
|
789
|
+
}
|
|
790
|
+
logger.debug(`[NeuroLink] Registered ${Object.keys(taskTools).length} task tools`);
|
|
791
|
+
}
|
|
719
792
|
/**
|
|
720
793
|
* Register memory retrieval tools that allow the AI to access
|
|
721
794
|
* conversation history, including full tool outputs.
|
|
@@ -1906,6 +1979,18 @@ Current user's request: ${currentInput}`;
|
|
|
1906
1979
|
logger.warn("[NeuroLink] MCP servers shutdown failed:", error);
|
|
1907
1980
|
}
|
|
1908
1981
|
}
|
|
1982
|
+
// Shutdown TaskManager
|
|
1983
|
+
if (this._taskManager) {
|
|
1984
|
+
try {
|
|
1985
|
+
await withTimeout(this._taskManager.shutdown(), 5000, new Error("TaskManager shutdown timed out"));
|
|
1986
|
+
}
|
|
1987
|
+
catch (error) {
|
|
1988
|
+
logger.warn("[NeuroLink] TaskManager shutdown error:", error);
|
|
1989
|
+
}
|
|
1990
|
+
finally {
|
|
1991
|
+
this._taskManager = undefined;
|
|
1992
|
+
}
|
|
1993
|
+
}
|
|
1909
1994
|
// Close conversation memory manager (release Redis connections, etc.)
|
|
1910
1995
|
if (this.conversationMemory?.close) {
|
|
1911
1996
|
try {
|
|
@@ -2521,6 +2606,8 @@ Current user's request: ${currentInput}`;
|
|
|
2521
2606
|
abortSignal: options.abortSignal,
|
|
2522
2607
|
skipToolPromptInjection: options.skipToolPromptInjection,
|
|
2523
2608
|
middleware: options.middleware,
|
|
2609
|
+
// Pass through conversation messages for task continuation and external callers
|
|
2610
|
+
conversationMessages: options.conversationMessages,
|
|
2524
2611
|
};
|
|
2525
2612
|
// Auto-map top-level sessionId/userId to context for convenience
|
|
2526
2613
|
// Tests and users may pass sessionId/userId as top-level options
|
|
@@ -2745,7 +2832,15 @@ Current user's request: ${currentInput}`;
|
|
|
2745
2832
|
// Execute workflow
|
|
2746
2833
|
const workflowResult = await runWorkflow(workflowConfig, {
|
|
2747
2834
|
prompt: options.input.text,
|
|
2748
|
-
conversationHistory: options.
|
|
2835
|
+
conversationHistory: options.conversationMessages
|
|
2836
|
+
?.filter((m) => m.role === "user" || m.role === "assistant")
|
|
2837
|
+
.map((m) => ({
|
|
2838
|
+
role: m.role,
|
|
2839
|
+
content: typeof m.content === "string"
|
|
2840
|
+
? m.content
|
|
2841
|
+
: JSON.stringify(m.content),
|
|
2842
|
+
})) ??
|
|
2843
|
+
options.conversationHistory,
|
|
2749
2844
|
timeout: options.timeout,
|
|
2750
2845
|
verbose: false,
|
|
2751
2846
|
metadata: options.context,
|
|
@@ -2836,7 +2931,15 @@ Current user's request: ${currentInput}`;
|
|
|
2836
2931
|
// Execute workflow with progressive streaming
|
|
2837
2932
|
const workflowStream = runWorkflowWithStreaming(workflowConfig, {
|
|
2838
2933
|
prompt: options.input.text,
|
|
2839
|
-
conversationHistory: options.
|
|
2934
|
+
conversationHistory: options.conversationMessages
|
|
2935
|
+
?.filter((m) => m.role === "user" || m.role === "assistant")
|
|
2936
|
+
.map((m) => ({
|
|
2937
|
+
role: m.role,
|
|
2938
|
+
content: typeof m.content === "string"
|
|
2939
|
+
? m.content
|
|
2940
|
+
: JSON.stringify(m.content),
|
|
2941
|
+
})) ??
|
|
2942
|
+
options.conversationHistory,
|
|
2840
2943
|
timeout: options.timeout,
|
|
2841
2944
|
verbose: false,
|
|
2842
2945
|
metadata: options.context,
|
|
@@ -5046,6 +5149,7 @@ Current user's request: ${currentInput}`;
|
|
|
5046
5149
|
model: options.model,
|
|
5047
5150
|
temperature: options.temperature,
|
|
5048
5151
|
maxTokens: options.maxTokens,
|
|
5152
|
+
conversationMessages: options.conversationMessages,
|
|
5049
5153
|
});
|
|
5050
5154
|
// Create a wrapper around the fallback stream that accumulates content
|
|
5051
5155
|
let fallbackAccumulatedContent = "";
|
|
@@ -8396,6 +8500,19 @@ Current user's request: ${currentInput}`;
|
|
|
8396
8500
|
cleanupErrors.push(err);
|
|
8397
8501
|
logger.warn("[NeuroLink] Error clearing caches:", error);
|
|
8398
8502
|
}
|
|
8503
|
+
// 5b. Shutdown TaskManager
|
|
8504
|
+
if (this._taskManager) {
|
|
8505
|
+
try {
|
|
8506
|
+
logger.debug("[NeuroLink] Shutting down TaskManager...");
|
|
8507
|
+
await withTimeout(this._taskManager.shutdown(), 5000, new Error("TaskManager shutdown timed out"));
|
|
8508
|
+
}
|
|
8509
|
+
catch (error) {
|
|
8510
|
+
logger.warn("[NeuroLink] TaskManager shutdown error:", error);
|
|
8511
|
+
}
|
|
8512
|
+
finally {
|
|
8513
|
+
this._taskManager = undefined;
|
|
8514
|
+
}
|
|
8515
|
+
}
|
|
8399
8516
|
// 6. Reset initialization flags
|
|
8400
8517
|
try {
|
|
8401
8518
|
logger.debug("[NeuroLink] Resetting initialization state...");
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BullMQ Backend — Production-grade task scheduling via Redis.
|
|
3
|
+
*
|
|
4
|
+
* - Cron tasks → BullMQ repeatable jobs with cron pattern
|
|
5
|
+
* - Interval tasks → BullMQ repeatable jobs with `every` option
|
|
6
|
+
* - One-shot tasks → BullMQ delayed jobs
|
|
7
|
+
* - Survives process restarts (Redis-persisted)
|
|
8
|
+
*/
|
|
9
|
+
import { type Task, type TaskBackend, type TaskExecutorFn, type TaskManagerConfig } from "../../types/taskTypes.js";
|
|
10
|
+
export declare class BullMQBackend implements TaskBackend {
|
|
11
|
+
readonly name = "bullmq";
|
|
12
|
+
private queue;
|
|
13
|
+
private worker;
|
|
14
|
+
private executors;
|
|
15
|
+
private config;
|
|
16
|
+
constructor(config: TaskManagerConfig);
|
|
17
|
+
initialize(): Promise<void>;
|
|
18
|
+
shutdown(): Promise<void>;
|
|
19
|
+
schedule(task: Task, executor: TaskExecutorFn): Promise<void>;
|
|
20
|
+
cancel(taskId: string): Promise<void>;
|
|
21
|
+
pause(taskId: string): Promise<void>;
|
|
22
|
+
resume(taskId: string): Promise<void>;
|
|
23
|
+
isHealthy(): Promise<boolean>;
|
|
24
|
+
/**
|
|
25
|
+
* Returns a connection options object for BullMQ / ioredis.
|
|
26
|
+
* When a URL is provided we parse it fully, preserving TLS (`rediss://`),
|
|
27
|
+
* ACL username, password, db index, and any query-string parameters so
|
|
28
|
+
* nothing is silently dropped.
|
|
29
|
+
*/
|
|
30
|
+
private getConnectionConfig;
|
|
31
|
+
private ensureInitialized;
|
|
32
|
+
}
|