@juspay/neurolink 9.39.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 +12 -0
- package/dist/browser/neurolink.min.js +445 -431
- 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 +22 -1
- package/dist/lib/neurolink.js +195 -14
- 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 +42 -0
- package/dist/lib/types/index.d.ts +2 -1
- package/dist/lib/types/streamTypes.d.ts +7 -0
- package/dist/lib/types/taskTypes.d.ts +275 -0
- package/dist/lib/types/taskTypes.js +37 -0
- package/dist/neurolink.d.ts +22 -1
- package/dist/neurolink.js +195 -14
- 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 +42 -0
- package/dist/types/index.d.ts +2 -1
- package/dist/types/streamTypes.d.ts +7 -0
- package/dist/types/taskTypes.d.ts +275 -0
- package/dist/types/taskTypes.js +36 -0
- package/package.json +4 -2
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RedisTaskStore — Redis-backed persistence for TaskManager.
|
|
3
|
+
* Used automatically when backend is "bullmq".
|
|
4
|
+
*
|
|
5
|
+
* Key patterns:
|
|
6
|
+
* neurolink:tasks (Hash) — all task definitions
|
|
7
|
+
* neurolink:task:{id}:runs (List) — run log entries (newest first)
|
|
8
|
+
* neurolink:task:{id}:history (List) — continuation mode conversation history
|
|
9
|
+
*/
|
|
10
|
+
import { createClient } from "redis";
|
|
11
|
+
import { logger } from "../../utils/logger.js";
|
|
12
|
+
import { TaskError } from "../errors.js";
|
|
13
|
+
import { TASK_DEFAULTS, } from "../../types/taskTypes.js";
|
|
14
|
+
const KEY_PREFIX = "neurolink:";
|
|
15
|
+
const TASKS_HASH = `${KEY_PREFIX}tasks`;
|
|
16
|
+
function taskRunsKey(taskId) {
|
|
17
|
+
return `${KEY_PREFIX}task:${taskId}:runs`;
|
|
18
|
+
}
|
|
19
|
+
function taskHistoryKey(taskId) {
|
|
20
|
+
return `${KEY_PREFIX}task:${taskId}:history`;
|
|
21
|
+
}
|
|
22
|
+
export class RedisTaskStore {
|
|
23
|
+
config;
|
|
24
|
+
type = "redis";
|
|
25
|
+
client = null;
|
|
26
|
+
maxRunLogs;
|
|
27
|
+
maxHistoryEntries;
|
|
28
|
+
retentionConfig;
|
|
29
|
+
constructor(config) {
|
|
30
|
+
this.config = config;
|
|
31
|
+
this.maxRunLogs = config.maxRunLogs ?? TASK_DEFAULTS.maxRunLogs;
|
|
32
|
+
this.maxHistoryEntries =
|
|
33
|
+
config.maxHistoryEntries ?? TASK_DEFAULTS.maxHistoryEntries;
|
|
34
|
+
this.retentionConfig = {
|
|
35
|
+
...TASK_DEFAULTS.retention,
|
|
36
|
+
...config.taskRetention,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
async initialize() {
|
|
40
|
+
const redis = this.config.redis ?? {};
|
|
41
|
+
const url = redis.url ??
|
|
42
|
+
`redis://${redis.host ?? TASK_DEFAULTS.redis.host}:${redis.port ?? TASK_DEFAULTS.redis.port}/${redis.db ?? 0}`;
|
|
43
|
+
this.client = createClient({
|
|
44
|
+
url,
|
|
45
|
+
...(redis.password ? { password: redis.password } : {}),
|
|
46
|
+
});
|
|
47
|
+
this.client.on("error", (err) => {
|
|
48
|
+
logger.error("[TaskStore:Redis] Connection error", {
|
|
49
|
+
error: String(err),
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
await this.client.connect();
|
|
53
|
+
logger.info("[TaskStore:Redis] Connected");
|
|
54
|
+
}
|
|
55
|
+
async shutdown() {
|
|
56
|
+
if (this.client?.isOpen) {
|
|
57
|
+
await this.client.quit();
|
|
58
|
+
logger.info("[TaskStore:Redis] Disconnected");
|
|
59
|
+
}
|
|
60
|
+
this.client = null;
|
|
61
|
+
}
|
|
62
|
+
// ── Task CRUD ───────────────────────────────────────────
|
|
63
|
+
async save(task) {
|
|
64
|
+
this.ensureConnected();
|
|
65
|
+
await this.client.hSet(TASKS_HASH, task.id, JSON.stringify(task));
|
|
66
|
+
this.applyRetentionTTL(task);
|
|
67
|
+
}
|
|
68
|
+
async get(taskId) {
|
|
69
|
+
this.ensureConnected();
|
|
70
|
+
const data = await this.client.hGet(TASKS_HASH, taskId);
|
|
71
|
+
if (!data) {
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
return JSON.parse(String(data));
|
|
75
|
+
}
|
|
76
|
+
async list(filter) {
|
|
77
|
+
this.ensureConnected();
|
|
78
|
+
const all = await this.client.hGetAll(TASKS_HASH);
|
|
79
|
+
let tasks = Object.values(all).map((v) => JSON.parse(String(v)));
|
|
80
|
+
if (filter?.status) {
|
|
81
|
+
tasks = tasks.filter((t) => t.status === filter.status);
|
|
82
|
+
}
|
|
83
|
+
return tasks.sort((a, b) => new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime());
|
|
84
|
+
}
|
|
85
|
+
async update(taskId, updates) {
|
|
86
|
+
this.ensureConnected();
|
|
87
|
+
const existing = await this.get(taskId);
|
|
88
|
+
if (!existing) {
|
|
89
|
+
throw TaskError.create("TASK_NOT_FOUND", `Task not found: ${taskId}`);
|
|
90
|
+
}
|
|
91
|
+
const updated = {
|
|
92
|
+
...existing,
|
|
93
|
+
...updates,
|
|
94
|
+
id: existing.id, // ID is immutable
|
|
95
|
+
updatedAt: new Date().toISOString(),
|
|
96
|
+
};
|
|
97
|
+
await this.client.hSet(TASKS_HASH, taskId, JSON.stringify(updated));
|
|
98
|
+
this.applyRetentionTTL(updated);
|
|
99
|
+
return updated;
|
|
100
|
+
}
|
|
101
|
+
async delete(taskId) {
|
|
102
|
+
this.ensureConnected();
|
|
103
|
+
await Promise.all([
|
|
104
|
+
this.client.hDel(TASKS_HASH, taskId),
|
|
105
|
+
this.client.del(taskRunsKey(taskId)),
|
|
106
|
+
this.client.del(taskHistoryKey(taskId)),
|
|
107
|
+
]);
|
|
108
|
+
}
|
|
109
|
+
// ── Run Logs ──────────────────────────────────────────
|
|
110
|
+
async appendRun(taskId, run) {
|
|
111
|
+
this.ensureConnected();
|
|
112
|
+
const key = taskRunsKey(taskId);
|
|
113
|
+
await this.client.lPush(key, JSON.stringify(run));
|
|
114
|
+
// Trim to keep only the latest maxRunLogs entries
|
|
115
|
+
await this.client.lTrim(key, 0, this.maxRunLogs - 1);
|
|
116
|
+
}
|
|
117
|
+
async getRuns(taskId, options) {
|
|
118
|
+
this.ensureConnected();
|
|
119
|
+
const limit = options?.limit ?? 20;
|
|
120
|
+
const key = taskRunsKey(taskId);
|
|
121
|
+
// When a status filter is applied, we need to fetch more items than `limit`
|
|
122
|
+
// because post-filter may discard many entries. Fetch all (-1) when filtering,
|
|
123
|
+
// otherwise fetch exactly `limit` items.
|
|
124
|
+
const fetchEnd = options?.status ? -1 : limit - 1;
|
|
125
|
+
const items = await this.client.lRange(key, 0, fetchEnd);
|
|
126
|
+
let runs = items.map((v) => JSON.parse(String(v)));
|
|
127
|
+
if (options?.status) {
|
|
128
|
+
runs = runs.filter((r) => r.status === options.status);
|
|
129
|
+
}
|
|
130
|
+
return runs.slice(0, limit);
|
|
131
|
+
}
|
|
132
|
+
// ── Continuation History ──────────────────────────────
|
|
133
|
+
async appendHistory(taskId, messages) {
|
|
134
|
+
this.ensureConnected();
|
|
135
|
+
const key = taskHistoryKey(taskId);
|
|
136
|
+
const serialized = messages.map((m) => JSON.stringify(m));
|
|
137
|
+
if (serialized.length > 0) {
|
|
138
|
+
await this.client.rPush(key, serialized);
|
|
139
|
+
// Trim to keep only the most recent entries, preventing unbounded growth
|
|
140
|
+
await this.client.lTrim(key, -this.maxHistoryEntries, -1);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
async getHistory(taskId) {
|
|
144
|
+
this.ensureConnected();
|
|
145
|
+
const key = taskHistoryKey(taskId);
|
|
146
|
+
const items = await this.client.lRange(key, 0, -1);
|
|
147
|
+
return items.map((v) => JSON.parse(String(v)));
|
|
148
|
+
}
|
|
149
|
+
async clearHistory(taskId) {
|
|
150
|
+
this.ensureConnected();
|
|
151
|
+
await this.client.del(taskHistoryKey(taskId));
|
|
152
|
+
}
|
|
153
|
+
// ── Internal ──────────────────────────────────────────
|
|
154
|
+
ensureConnected() {
|
|
155
|
+
if (!this.client?.isOpen) {
|
|
156
|
+
throw TaskError.create("BACKEND_NOT_INITIALIZED", "[TaskStore:Redis] Not connected. Call initialize() first.");
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Set Redis TTL on terminal-state tasks so they auto-expire.
|
|
161
|
+
* Active and paused tasks never expire.
|
|
162
|
+
*/
|
|
163
|
+
applyRetentionTTL(task) {
|
|
164
|
+
// We don't set EXPIRE on the hash field directly (Redis doesn't support per-field TTL).
|
|
165
|
+
// Instead, run logs and history keys get TTL. The task hash field itself must be
|
|
166
|
+
// cleaned up via manual deletion or BullMQ's built-in job cleanup.
|
|
167
|
+
const ttlMap = {
|
|
168
|
+
completed: this.retentionConfig.completedTTL,
|
|
169
|
+
failed: this.retentionConfig.failedTTL,
|
|
170
|
+
cancelled: this.retentionConfig.cancelledTTL,
|
|
171
|
+
};
|
|
172
|
+
const ttlMs = ttlMap[task.status];
|
|
173
|
+
if (ttlMs) {
|
|
174
|
+
const ttlSeconds = Math.ceil(ttlMs / 1000);
|
|
175
|
+
// Set TTL on associated keys
|
|
176
|
+
this.client.expire(taskRunsKey(task.id), ttlSeconds).catch((err) => {
|
|
177
|
+
logger.debug("[TaskStore:Redis] Failed to set TTL", {
|
|
178
|
+
error: String(err),
|
|
179
|
+
});
|
|
180
|
+
});
|
|
181
|
+
this.client.expire(taskHistoryKey(task.id), ttlSeconds).catch((err) => {
|
|
182
|
+
logger.debug("[TaskStore:Redis] Failed to set TTL", {
|
|
183
|
+
error: String(err),
|
|
184
|
+
});
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TaskExecutor — Runs a single task execution against NeuroLink.generate().
|
|
3
|
+
*
|
|
4
|
+
* Handles:
|
|
5
|
+
* - Isolated mode: fresh generate() call with no history
|
|
6
|
+
* - Continuation mode: loads conversation history, appends new exchange
|
|
7
|
+
* - Retry with exponential backoff for transient errors
|
|
8
|
+
* - Run result construction and logging
|
|
9
|
+
*/
|
|
10
|
+
import type { TaskStore, Task, TaskRunResult, NeuroLinkExecutable } from "../types/taskTypes.js";
|
|
11
|
+
export declare class TaskExecutor {
|
|
12
|
+
private neurolink;
|
|
13
|
+
private store;
|
|
14
|
+
constructor(neurolink: NeuroLinkExecutable, store: TaskStore);
|
|
15
|
+
/**
|
|
16
|
+
* Execute a task once. Called by the backend on each scheduled tick.
|
|
17
|
+
* Returns the run result (success or error).
|
|
18
|
+
*/
|
|
19
|
+
execute(task: Task): Promise<TaskRunResult>;
|
|
20
|
+
private executeOnce;
|
|
21
|
+
}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TaskExecutor — Runs a single task execution against NeuroLink.generate().
|
|
3
|
+
*
|
|
4
|
+
* Handles:
|
|
5
|
+
* - Isolated mode: fresh generate() call with no history
|
|
6
|
+
* - Continuation mode: loads conversation history, appends new exchange
|
|
7
|
+
* - Retry with exponential backoff for transient errors
|
|
8
|
+
* - Run result construction and logging
|
|
9
|
+
*/
|
|
10
|
+
import { nanoid } from "nanoid";
|
|
11
|
+
import { logger } from "../utils/logger.js";
|
|
12
|
+
/** Errors that are transient and should be retried */
|
|
13
|
+
const TRANSIENT_PATTERNS = [
|
|
14
|
+
"rate limit",
|
|
15
|
+
"rate_limit",
|
|
16
|
+
"too many requests",
|
|
17
|
+
"429",
|
|
18
|
+
"503",
|
|
19
|
+
"502",
|
|
20
|
+
"504",
|
|
21
|
+
"timeout",
|
|
22
|
+
"econnreset",
|
|
23
|
+
"econnrefused",
|
|
24
|
+
"network",
|
|
25
|
+
"overloaded",
|
|
26
|
+
];
|
|
27
|
+
function isTransientError(error) {
|
|
28
|
+
const msg = String(error).toLowerCase();
|
|
29
|
+
return TRANSIENT_PATTERNS.some((p) => msg.includes(p));
|
|
30
|
+
}
|
|
31
|
+
export class TaskExecutor {
|
|
32
|
+
neurolink;
|
|
33
|
+
store;
|
|
34
|
+
constructor(neurolink, store) {
|
|
35
|
+
this.neurolink = neurolink;
|
|
36
|
+
this.store = store;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Execute a task once. Called by the backend on each scheduled tick.
|
|
40
|
+
* Returns the run result (success or error).
|
|
41
|
+
*/
|
|
42
|
+
async execute(task) {
|
|
43
|
+
const runId = `run_${nanoid(12)}`;
|
|
44
|
+
const startTime = Date.now();
|
|
45
|
+
let lastError;
|
|
46
|
+
for (let attempt = 1; attempt <= task.retry.maxAttempts; attempt++) {
|
|
47
|
+
try {
|
|
48
|
+
const result = await this.executeOnce(task, runId);
|
|
49
|
+
return result;
|
|
50
|
+
}
|
|
51
|
+
catch (err) {
|
|
52
|
+
lastError = String(err);
|
|
53
|
+
const willRetry = attempt < task.retry.maxAttempts && isTransientError(err);
|
|
54
|
+
logger.warn("[TaskExecutor] Execution attempt failed", {
|
|
55
|
+
taskId: task.id,
|
|
56
|
+
runId,
|
|
57
|
+
attempt,
|
|
58
|
+
willRetry,
|
|
59
|
+
error: lastError,
|
|
60
|
+
});
|
|
61
|
+
if (!willRetry) {
|
|
62
|
+
break;
|
|
63
|
+
}
|
|
64
|
+
// Backoff before retry
|
|
65
|
+
const backoffIndex = Math.min(attempt - 1, task.retry.backoffMs.length - 1);
|
|
66
|
+
const delay = task.retry.backoffMs[backoffIndex];
|
|
67
|
+
await sleep(delay);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
// All retries exhausted or permanent error
|
|
71
|
+
const errorResult = {
|
|
72
|
+
taskId: task.id,
|
|
73
|
+
runId,
|
|
74
|
+
status: "error",
|
|
75
|
+
error: lastError,
|
|
76
|
+
durationMs: Date.now() - startTime,
|
|
77
|
+
timestamp: new Date().toISOString(),
|
|
78
|
+
};
|
|
79
|
+
return errorResult;
|
|
80
|
+
}
|
|
81
|
+
// ── Internal ──────────────────────────────────────────
|
|
82
|
+
async executeOnce(task, runId) {
|
|
83
|
+
const startTime = Date.now();
|
|
84
|
+
// Build generate options
|
|
85
|
+
const generateOptions = {
|
|
86
|
+
input: { text: task.prompt },
|
|
87
|
+
...(task.provider ? { provider: task.provider } : {}),
|
|
88
|
+
...(task.model ? { model: task.model } : {}),
|
|
89
|
+
...(task.systemPrompt ? { systemPrompt: task.systemPrompt } : {}),
|
|
90
|
+
...(task.maxTokens ? { maxTokens: task.maxTokens } : {}),
|
|
91
|
+
...(task.temperature !== undefined
|
|
92
|
+
? { temperature: task.temperature }
|
|
93
|
+
: {}),
|
|
94
|
+
...(task.timeout ? { timeout: task.timeout } : {}),
|
|
95
|
+
...(!task.tools ? { disableTools: true } : {}),
|
|
96
|
+
};
|
|
97
|
+
// Thinking level
|
|
98
|
+
if (task.thinkingLevel) {
|
|
99
|
+
generateOptions.thinkingConfig = { thinkingLevel: task.thinkingLevel };
|
|
100
|
+
}
|
|
101
|
+
// Continuation mode: pass conversation history as proper multi-turn messages
|
|
102
|
+
if (task.mode === "continuation" && task.sessionId) {
|
|
103
|
+
const history = await this.store.getHistory(task.id);
|
|
104
|
+
// Pass history as proper role-based conversation messages
|
|
105
|
+
if (history.length > 0) {
|
|
106
|
+
generateOptions.conversationMessages = history.map((entry, i) => ({
|
|
107
|
+
id: `${task.sessionId}_${i}`,
|
|
108
|
+
role: entry.role,
|
|
109
|
+
content: entry.content,
|
|
110
|
+
timestamp: entry.timestamp,
|
|
111
|
+
}));
|
|
112
|
+
}
|
|
113
|
+
// Add continuation context to system prompt
|
|
114
|
+
const runCount = Math.floor(history.length / 2);
|
|
115
|
+
const continuationHint = runCount > 0
|
|
116
|
+
? `This is a continuation task (run ${runCount + 1}). Your previous ${runCount} exchange(s) are provided as conversation history.`
|
|
117
|
+
: "This is a continuation task. This is the first execution — no prior history exists yet.";
|
|
118
|
+
generateOptions.systemPrompt = task.systemPrompt
|
|
119
|
+
? `${task.systemPrompt}\n\n${continuationHint}`
|
|
120
|
+
: continuationHint;
|
|
121
|
+
}
|
|
122
|
+
// Execute
|
|
123
|
+
const result = await this.neurolink.generate(generateOptions);
|
|
124
|
+
// Build run result
|
|
125
|
+
const runResult = {
|
|
126
|
+
taskId: task.id,
|
|
127
|
+
runId,
|
|
128
|
+
status: "success",
|
|
129
|
+
output: result.content,
|
|
130
|
+
toolCalls: result.toolExecutions?.map((te) => ({
|
|
131
|
+
name: te.name,
|
|
132
|
+
input: te.input,
|
|
133
|
+
output: te.output,
|
|
134
|
+
})),
|
|
135
|
+
tokensUsed: result.usage
|
|
136
|
+
? {
|
|
137
|
+
input: result.usage.input ?? 0,
|
|
138
|
+
output: result.usage.output ?? 0,
|
|
139
|
+
}
|
|
140
|
+
: undefined,
|
|
141
|
+
durationMs: Date.now() - startTime,
|
|
142
|
+
timestamp: new Date().toISOString(),
|
|
143
|
+
};
|
|
144
|
+
// Continuation mode: append this exchange to history
|
|
145
|
+
if (task.mode === "continuation" && task.sessionId) {
|
|
146
|
+
const newEntries = [
|
|
147
|
+
{
|
|
148
|
+
role: "user",
|
|
149
|
+
content: task.prompt,
|
|
150
|
+
timestamp: new Date(startTime).toISOString(),
|
|
151
|
+
},
|
|
152
|
+
{
|
|
153
|
+
role: "assistant",
|
|
154
|
+
content: result.content,
|
|
155
|
+
timestamp: runResult.timestamp,
|
|
156
|
+
},
|
|
157
|
+
];
|
|
158
|
+
await this.store.appendHistory(task.id, newEntries);
|
|
159
|
+
}
|
|
160
|
+
return runResult;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
function sleep(ms) {
|
|
164
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
165
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TaskManager — Main orchestrator for scheduled and self-running tasks.
|
|
3
|
+
*
|
|
4
|
+
* Manages the full task lifecycle: create, schedule, execute, pause, resume, delete.
|
|
5
|
+
* Auto-selects TaskStore and TaskBackend based on config.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* const neurolink = new NeuroLink({ tasks: { backend: "bullmq" } });
|
|
9
|
+
* await neurolink.tasks.create({ name: "monitor", prompt: "...", schedule: { type: "interval", every: 60000 } });
|
|
10
|
+
*/
|
|
11
|
+
import { type NeuroLinkExecutable, type Task, type TaskDefinition, type TaskManagerConfig, type TaskRunResult, type TaskStatus } from "../types/taskTypes.js";
|
|
12
|
+
export declare class TaskManager {
|
|
13
|
+
private neurolink;
|
|
14
|
+
private config;
|
|
15
|
+
private store;
|
|
16
|
+
private backend;
|
|
17
|
+
private executor;
|
|
18
|
+
private initialized;
|
|
19
|
+
private initPromise;
|
|
20
|
+
/** In-memory callback registry (not serializable to store) */
|
|
21
|
+
private callbacks;
|
|
22
|
+
/** Emitter reference — set by NeuroLink on integration */
|
|
23
|
+
private emitter?;
|
|
24
|
+
constructor(neurolink: NeuroLinkExecutable, config?: TaskManagerConfig);
|
|
25
|
+
/** Set the event emitter (called by NeuroLink during integration) */
|
|
26
|
+
setEmitter(emitter: {
|
|
27
|
+
emit(event: string, ...args: unknown[]): boolean;
|
|
28
|
+
}): void;
|
|
29
|
+
private ensureInitialized;
|
|
30
|
+
private doInitialize;
|
|
31
|
+
create(definition: TaskDefinition): Promise<Task>;
|
|
32
|
+
get(taskId: string): Promise<Task | null>;
|
|
33
|
+
list(filter?: {
|
|
34
|
+
status?: TaskStatus;
|
|
35
|
+
}): Promise<Task[]>;
|
|
36
|
+
update(taskId: string, updates: Partial<TaskDefinition>): Promise<Task>;
|
|
37
|
+
/** Run a task immediately (outside of its schedule) */
|
|
38
|
+
run(taskId: string): Promise<TaskRunResult>;
|
|
39
|
+
pause(taskId: string): Promise<Task>;
|
|
40
|
+
resume(taskId: string): Promise<Task>;
|
|
41
|
+
delete(taskId: string): Promise<void>;
|
|
42
|
+
runs(taskId: string, options?: {
|
|
43
|
+
limit?: number;
|
|
44
|
+
status?: string;
|
|
45
|
+
}): Promise<TaskRunResult[]>;
|
|
46
|
+
shutdown(): Promise<void>;
|
|
47
|
+
/** Check if the backend is healthy */
|
|
48
|
+
isHealthy(): Promise<boolean>;
|
|
49
|
+
/**
|
|
50
|
+
* Called by the backend on each scheduled tick.
|
|
51
|
+
* Executes the task, updates state, fires callbacks/events.
|
|
52
|
+
*/
|
|
53
|
+
private onTaskTick;
|
|
54
|
+
/**
|
|
55
|
+
* Re-schedule all active tasks from store.
|
|
56
|
+
* Called on initialization to handle process restarts.
|
|
57
|
+
*/
|
|
58
|
+
private rescheduleActiveTasks;
|
|
59
|
+
private emit;
|
|
60
|
+
}
|