@juspay/neurolink 9.51.4 → 9.53.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.
Files changed (186) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +19 -0
  3. package/dist/agent/directTools.d.ts +2 -2
  4. package/dist/auth/errors.d.ts +1 -1
  5. package/dist/auth/middleware/AuthMiddleware.d.ts +1 -1
  6. package/dist/auth/providers/BaseAuthProvider.d.ts +1 -1
  7. package/dist/autoresearch/config.d.ts +11 -0
  8. package/dist/autoresearch/config.js +108 -0
  9. package/dist/autoresearch/errors.d.ts +40 -0
  10. package/dist/autoresearch/errors.js +20 -0
  11. package/dist/autoresearch/index.d.ts +23 -0
  12. package/dist/autoresearch/index.js +34 -0
  13. package/dist/autoresearch/phasePolicy.d.ts +9 -0
  14. package/dist/autoresearch/phasePolicy.js +69 -0
  15. package/dist/autoresearch/promptCompiler.d.ts +15 -0
  16. package/dist/autoresearch/promptCompiler.js +120 -0
  17. package/dist/autoresearch/repoPolicy.d.ts +32 -0
  18. package/dist/autoresearch/repoPolicy.js +128 -0
  19. package/dist/autoresearch/resultRecorder.d.ts +20 -0
  20. package/dist/autoresearch/resultRecorder.js +130 -0
  21. package/dist/autoresearch/runner.d.ts +10 -0
  22. package/dist/autoresearch/runner.js +102 -0
  23. package/dist/autoresearch/stateStore.d.ts +12 -0
  24. package/dist/autoresearch/stateStore.js +163 -0
  25. package/dist/autoresearch/summaryParser.d.ts +16 -0
  26. package/dist/autoresearch/summaryParser.js +94 -0
  27. package/dist/autoresearch/tools.d.ts +257 -0
  28. package/dist/autoresearch/tools.js +617 -0
  29. package/dist/autoresearch/worker.d.ts +71 -0
  30. package/dist/autoresearch/worker.js +417 -0
  31. package/dist/browser/neurolink.min.js +340 -324
  32. package/dist/cli/commands/autoresearch.d.ts +41 -0
  33. package/dist/cli/commands/autoresearch.js +487 -0
  34. package/dist/cli/commands/config.d.ts +1 -1
  35. package/dist/cli/commands/task.d.ts +2 -0
  36. package/dist/cli/commands/task.js +32 -3
  37. package/dist/cli/loop/optionsSchema.d.ts +1 -1
  38. package/dist/cli/parser.js +4 -1
  39. package/dist/core/baseProvider.js +18 -0
  40. package/dist/core/factory.d.ts +2 -2
  41. package/dist/core/factory.js +4 -4
  42. package/dist/evaluation/errors/EvaluationError.d.ts +1 -1
  43. package/dist/factories/providerFactory.d.ts +4 -4
  44. package/dist/factories/providerFactory.js +20 -7
  45. package/dist/factories/providerRegistry.d.ts +5 -0
  46. package/dist/factories/providerRegistry.js +45 -26
  47. package/dist/lib/agent/directTools.d.ts +2 -2
  48. package/dist/lib/auth/errors.d.ts +1 -1
  49. package/dist/lib/auth/middleware/AuthMiddleware.d.ts +1 -1
  50. package/dist/lib/auth/providers/BaseAuthProvider.d.ts +1 -1
  51. package/dist/lib/autoresearch/config.d.ts +11 -0
  52. package/dist/lib/autoresearch/config.js +109 -0
  53. package/dist/lib/autoresearch/errors.d.ts +40 -0
  54. package/dist/lib/autoresearch/errors.js +21 -0
  55. package/dist/lib/autoresearch/index.d.ts +23 -0
  56. package/dist/lib/autoresearch/index.js +35 -0
  57. package/dist/lib/autoresearch/phasePolicy.d.ts +9 -0
  58. package/dist/lib/autoresearch/phasePolicy.js +70 -0
  59. package/dist/lib/autoresearch/promptCompiler.d.ts +15 -0
  60. package/dist/lib/autoresearch/promptCompiler.js +121 -0
  61. package/dist/lib/autoresearch/repoPolicy.d.ts +32 -0
  62. package/dist/lib/autoresearch/repoPolicy.js +129 -0
  63. package/dist/lib/autoresearch/resultRecorder.d.ts +20 -0
  64. package/dist/lib/autoresearch/resultRecorder.js +131 -0
  65. package/dist/lib/autoresearch/runner.d.ts +10 -0
  66. package/dist/lib/autoresearch/runner.js +103 -0
  67. package/dist/lib/autoresearch/stateStore.d.ts +12 -0
  68. package/dist/lib/autoresearch/stateStore.js +164 -0
  69. package/dist/lib/autoresearch/summaryParser.d.ts +16 -0
  70. package/dist/lib/autoresearch/summaryParser.js +95 -0
  71. package/dist/lib/autoresearch/tools.d.ts +257 -0
  72. package/dist/lib/autoresearch/tools.js +618 -0
  73. package/dist/lib/autoresearch/worker.d.ts +71 -0
  74. package/dist/lib/autoresearch/worker.js +418 -0
  75. package/dist/lib/core/baseProvider.js +18 -0
  76. package/dist/lib/core/factory.d.ts +2 -2
  77. package/dist/lib/core/factory.js +4 -4
  78. package/dist/lib/evaluation/errors/EvaluationError.d.ts +1 -1
  79. package/dist/lib/factories/providerFactory.d.ts +4 -4
  80. package/dist/lib/factories/providerFactory.js +20 -7
  81. package/dist/lib/factories/providerRegistry.d.ts +5 -0
  82. package/dist/lib/factories/providerRegistry.js +45 -26
  83. package/dist/lib/files/fileTools.d.ts +1 -1
  84. package/dist/lib/neurolink.d.ts +21 -0
  85. package/dist/lib/neurolink.js +91 -8
  86. package/dist/lib/providers/amazonBedrock.d.ts +6 -1
  87. package/dist/lib/providers/amazonBedrock.js +14 -2
  88. package/dist/lib/providers/amazonSagemaker.d.ts +7 -1
  89. package/dist/lib/providers/amazonSagemaker.js +21 -3
  90. package/dist/lib/providers/anthropic.d.ts +4 -1
  91. package/dist/lib/providers/anthropic.js +18 -5
  92. package/dist/lib/providers/azureOpenai.d.ts +2 -1
  93. package/dist/lib/providers/azureOpenai.js +10 -5
  94. package/dist/lib/providers/googleAiStudio.d.ts +4 -1
  95. package/dist/lib/providers/googleAiStudio.js +6 -7
  96. package/dist/lib/providers/googleVertex.d.ts +3 -1
  97. package/dist/lib/providers/googleVertex.js +96 -17
  98. package/dist/lib/providers/huggingFace.d.ts +2 -1
  99. package/dist/lib/providers/huggingFace.js +4 -4
  100. package/dist/lib/providers/litellm.d.ts +5 -1
  101. package/dist/lib/providers/litellm.js +16 -11
  102. package/dist/lib/providers/mistral.d.ts +2 -1
  103. package/dist/lib/providers/mistral.js +2 -2
  104. package/dist/lib/providers/ollama.d.ts +3 -1
  105. package/dist/lib/providers/ollama.js +2 -2
  106. package/dist/lib/providers/openAI.d.ts +5 -1
  107. package/dist/lib/providers/openAI.js +15 -5
  108. package/dist/lib/providers/openRouter.d.ts +5 -1
  109. package/dist/lib/providers/openRouter.js +19 -7
  110. package/dist/lib/providers/openaiCompatible.d.ts +4 -1
  111. package/dist/lib/providers/openaiCompatible.js +18 -4
  112. package/dist/lib/tasks/autoresearchTaskExecutor.d.ts +32 -0
  113. package/dist/lib/tasks/autoresearchTaskExecutor.js +303 -0
  114. package/dist/lib/tasks/errors.d.ts +3 -1
  115. package/dist/lib/tasks/errors.js +1 -0
  116. package/dist/lib/tasks/taskExecutor.d.ts +4 -2
  117. package/dist/lib/tasks/taskExecutor.js +8 -1
  118. package/dist/lib/tasks/taskManager.js +27 -3
  119. package/dist/lib/tasks/tools/taskTools.d.ts +1 -1
  120. package/dist/lib/telemetry/attributes.d.ts +15 -0
  121. package/dist/lib/telemetry/attributes.js +16 -0
  122. package/dist/lib/telemetry/tracers.d.ts +1 -0
  123. package/dist/lib/telemetry/tracers.js +1 -0
  124. package/dist/lib/types/autoresearchTypes.d.ts +194 -0
  125. package/dist/lib/types/autoresearchTypes.js +18 -0
  126. package/dist/lib/types/common.d.ts +11 -0
  127. package/dist/lib/types/configTypes.d.ts +7 -0
  128. package/dist/lib/types/generateTypes.d.ts +13 -0
  129. package/dist/lib/types/index.d.ts +16 -14
  130. package/dist/lib/types/index.js +21 -17
  131. package/dist/lib/types/providers.d.ts +75 -0
  132. package/dist/lib/types/streamTypes.d.ts +7 -1
  133. package/dist/lib/types/taskTypes.d.ts +38 -0
  134. package/dist/lib/workflow/config.d.ts +3 -3
  135. package/dist/neurolink.d.ts +21 -0
  136. package/dist/neurolink.js +91 -8
  137. package/dist/providers/amazonBedrock.d.ts +6 -1
  138. package/dist/providers/amazonBedrock.js +14 -2
  139. package/dist/providers/amazonSagemaker.d.ts +7 -1
  140. package/dist/providers/amazonSagemaker.js +21 -3
  141. package/dist/providers/anthropic.d.ts +4 -1
  142. package/dist/providers/anthropic.js +18 -5
  143. package/dist/providers/azureOpenai.d.ts +2 -1
  144. package/dist/providers/azureOpenai.js +10 -5
  145. package/dist/providers/googleAiStudio.d.ts +4 -1
  146. package/dist/providers/googleAiStudio.js +6 -7
  147. package/dist/providers/googleVertex.d.ts +3 -1
  148. package/dist/providers/googleVertex.js +96 -17
  149. package/dist/providers/huggingFace.d.ts +2 -1
  150. package/dist/providers/huggingFace.js +4 -4
  151. package/dist/providers/litellm.d.ts +5 -1
  152. package/dist/providers/litellm.js +16 -11
  153. package/dist/providers/mistral.d.ts +2 -1
  154. package/dist/providers/mistral.js +2 -2
  155. package/dist/providers/ollama.d.ts +3 -1
  156. package/dist/providers/ollama.js +2 -2
  157. package/dist/providers/openAI.d.ts +5 -1
  158. package/dist/providers/openAI.js +15 -5
  159. package/dist/providers/openRouter.d.ts +5 -1
  160. package/dist/providers/openRouter.js +19 -7
  161. package/dist/providers/openaiCompatible.d.ts +4 -1
  162. package/dist/providers/openaiCompatible.js +18 -4
  163. package/dist/rag/errors/RAGError.d.ts +1 -1
  164. package/dist/tasks/autoresearchTaskExecutor.d.ts +32 -0
  165. package/dist/tasks/autoresearchTaskExecutor.js +302 -0
  166. package/dist/tasks/errors.d.ts +3 -1
  167. package/dist/tasks/errors.js +1 -0
  168. package/dist/tasks/taskExecutor.d.ts +4 -2
  169. package/dist/tasks/taskExecutor.js +8 -1
  170. package/dist/tasks/taskManager.js +27 -3
  171. package/dist/tasks/tools/taskTools.d.ts +1 -1
  172. package/dist/telemetry/attributes.d.ts +15 -0
  173. package/dist/telemetry/attributes.js +16 -0
  174. package/dist/telemetry/tracers.d.ts +1 -0
  175. package/dist/telemetry/tracers.js +1 -0
  176. package/dist/types/autoresearchTypes.d.ts +194 -0
  177. package/dist/types/autoresearchTypes.js +17 -0
  178. package/dist/types/common.d.ts +11 -0
  179. package/dist/types/configTypes.d.ts +7 -0
  180. package/dist/types/generateTypes.d.ts +13 -0
  181. package/dist/types/index.d.ts +16 -14
  182. package/dist/types/index.js +21 -17
  183. package/dist/types/providers.d.ts +75 -0
  184. package/dist/types/streamTypes.d.ts +7 -1
  185. package/dist/types/taskTypes.d.ts +38 -0
  186. package/package.json +3 -2
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Autoresearch task executor — bridges TaskManager with the
3
+ * autoresearch experiment loop.
4
+ *
5
+ * Each tick:
6
+ * 1. Loads/creates a ResearchWorker for the task's tag
7
+ * 2. Gets the phase-appropriate tool filter
8
+ * 3. Calls NeuroLink.generate() with research tools + prompt
9
+ * 4. Advances phase based on which tools the AI called
10
+ * 5. Returns a TaskRunResult
11
+ *
12
+ * Workers are cached by tag to avoid re-initialization on each tick.
13
+ * Forwards the NeuroLink emitter to each worker for lifecycle events.
14
+ */
15
+ import type { AutoresearchEmitter } from "../types/autoresearchTypes.js";
16
+ import type { NeuroLinkExecutable, Task, TaskRunResult } from "../types/taskTypes.js";
17
+ /**
18
+ * Clear all cached workers. Called by TaskManager.shutdown().
19
+ */
20
+ export declare function clearWorkerCache(): void;
21
+ /**
22
+ * Execute one autoresearch tick for a task.
23
+ *
24
+ * Returns a TaskRunResult-shaped object.
25
+ * If the task is missing autoresearch config, returns an error result
26
+ * instead of throwing (so the scheduler can record the failure).
27
+ *
28
+ * @param emitter - Optional emitter to forward autoresearch lifecycle events
29
+ */
30
+ export declare function executeAutoresearchTick(task: Task & {
31
+ autoresearch?: unknown;
32
+ }, neurolink: NeuroLinkExecutable, emitter?: AutoresearchEmitter): Promise<TaskRunResult>;
@@ -0,0 +1,302 @@
1
+ /**
2
+ * Autoresearch task executor — bridges TaskManager with the
3
+ * autoresearch experiment loop.
4
+ *
5
+ * Each tick:
6
+ * 1. Loads/creates a ResearchWorker for the task's tag
7
+ * 2. Gets the phase-appropriate tool filter
8
+ * 3. Calls NeuroLink.generate() with research tools + prompt
9
+ * 4. Advances phase based on which tools the AI called
10
+ * 5. Returns a TaskRunResult
11
+ *
12
+ * Workers are cached by tag to avoid re-initialization on each tick.
13
+ * Forwards the NeuroLink emitter to each worker for lifecycle events.
14
+ */
15
+ import { ResearchWorker } from "../autoresearch/worker.js";
16
+ import { withTimeout } from "../utils/errorHandling.js";
17
+ import { logger } from "../utils/logger.js";
18
+ // ── Worker cache ────────────────────────────────────────
19
+ const workerCache = new Map();
20
+ /**
21
+ * Clear all cached workers. Called by TaskManager.shutdown().
22
+ */
23
+ export function clearWorkerCache() {
24
+ workerCache.clear();
25
+ logger.debug("[AutoresearchExecutor] Worker cache cleared");
26
+ }
27
+ // ── Phase advancement ──────────────────────────────────
28
+ /**
29
+ * Determine the next phase based on which tools the AI called.
30
+ *
31
+ * The worker path (`runExperimentCycle`) drives phases internally.
32
+ * The task path must replicate that advancement here. The mapping:
33
+ *
34
+ * bootstrap → (get_context called) → propose
35
+ * propose → (read_file called) → edit
36
+ * edit → (write_candidate) → commit
37
+ * commit → (commit_candidate) → run
38
+ * run → (run_experiment) → evaluate
39
+ * evaluate → (parse_log) → record
40
+ * record → (record called) → accept_or_revert
41
+ * accept_or_revert → (accept/revert) → propose
42
+ *
43
+ * Returns null if no advancement is indicated (e.g. no recognised tools called).
44
+ */
45
+ function inferNextPhase(currentPhase, calledTools) {
46
+ if (calledTools.length === 0) {
47
+ return null;
48
+ }
49
+ // Compute phase advancement per-tool against the running accumulated phase
50
+ function phaseAfterTool(toolName, phase) {
51
+ switch (toolName) {
52
+ case "research_get_context":
53
+ return phase === "bootstrap" || phase === "propose" ? "propose" : phase;
54
+ case "research_read_file":
55
+ return phase === "propose" ? "edit" : phase;
56
+ case "research_write_candidate":
57
+ return "commit";
58
+ case "research_commit_candidate":
59
+ return "run";
60
+ case "research_run_experiment":
61
+ return "evaluate";
62
+ case "research_parse_log":
63
+ return "record";
64
+ case "research_record":
65
+ return "accept_or_revert";
66
+ case "research_accept":
67
+ return "propose";
68
+ case "research_revert":
69
+ return "propose";
70
+ default:
71
+ return phase; // informational tools don't advance
72
+ }
73
+ }
74
+ // Walk the called tools in order, accumulating phase progression
75
+ let nextPhase = currentPhase;
76
+ for (const toolName of calledTools) {
77
+ nextPhase = phaseAfterTool(toolName, nextPhase);
78
+ }
79
+ return nextPhase !== currentPhase ? nextPhase : null;
80
+ }
81
+ /**
82
+ * Get or create a ResearchWorker for the given task.
83
+ * When an emitter is provided, it is injected into the worker
84
+ * so that autoresearch:* events flow through the central event bus.
85
+ */
86
+ async function getOrCreateWorker(task, emitter) {
87
+ const tag = `task-${task.id}`;
88
+ const cached = workerCache.get(tag);
89
+ if (cached) {
90
+ // Re-inject emitter in case it changed (e.g. after re-initialization)
91
+ if (emitter) {
92
+ cached.setEmitter(emitter);
93
+ }
94
+ // Attempt to resume existing state
95
+ try {
96
+ await cached.resume();
97
+ return cached;
98
+ }
99
+ catch (err) {
100
+ const msg = err instanceof Error ? err.message : String(err);
101
+ // Only re-initialize if state is genuinely missing; surface other errors
102
+ if (msg.includes("STATE_NOT_FOUND") || msg.includes("No state file")) {
103
+ workerCache.delete(tag);
104
+ }
105
+ else {
106
+ throw err;
107
+ }
108
+ }
109
+ }
110
+ // Validated at caller (line 184 returns early if missing)
111
+ const config = task.autoresearch;
112
+ const worker = new ResearchWorker({
113
+ repoPath: config.repoPath,
114
+ mutablePaths: config.mutablePaths,
115
+ runCommand: config.runCommand,
116
+ metric: config.metric,
117
+ ...(config.immutablePaths ? { immutablePaths: config.immutablePaths } : {}),
118
+ ...(config.timeoutMs ? { timeoutMs: config.timeoutMs } : {}),
119
+ ...(config.provider ? { provider: config.provider } : {}),
120
+ ...(config.model ? { model: config.model } : {}),
121
+ ...(config.thinkingLevel ? { thinkingLevel: config.thinkingLevel } : {}),
122
+ ...(config.maxExperiments ? { maxExperiments: config.maxExperiments } : {}),
123
+ // Forward artifact layout fields so scheduled runs match standalone config
124
+ ...(config.programPath ? { programPath: config.programPath } : {}),
125
+ ...(config.resultsPath ? { resultsPath: config.resultsPath } : {}),
126
+ ...(config.statePath ? { statePath: config.statePath } : {}),
127
+ ...(config.logPath ? { logPath: config.logPath } : {}),
128
+ ...(config.branchPrefix ? { branchPrefix: config.branchPrefix } : {}),
129
+ ...(config.memoryMetric ? { memoryMetric: config.memoryMetric } : {}),
130
+ });
131
+ // Inject emitter before initialization so init events are captured
132
+ if (emitter) {
133
+ worker.setEmitter(emitter);
134
+ }
135
+ // Try resuming existing state first; only initialize if state is genuinely missing
136
+ try {
137
+ await worker.resume();
138
+ }
139
+ catch (err) {
140
+ const msg = err instanceof Error ? err.message : String(err);
141
+ if (msg.includes("STATE_NOT_FOUND") || msg.includes("No state file")) {
142
+ await worker.initialize(tag);
143
+ }
144
+ else {
145
+ throw err;
146
+ }
147
+ }
148
+ workerCache.set(tag, worker);
149
+ return worker;
150
+ }
151
+ /**
152
+ * Execute one autoresearch tick for a task.
153
+ *
154
+ * Returns a TaskRunResult-shaped object.
155
+ * If the task is missing autoresearch config, returns an error result
156
+ * instead of throwing (so the scheduler can record the failure).
157
+ *
158
+ * @param emitter - Optional emitter to forward autoresearch lifecycle events
159
+ */
160
+ export async function executeAutoresearchTick(task, neurolink, emitter) {
161
+ const startTime = Date.now();
162
+ const runId = `ar_${Date.now()}`;
163
+ // Validate config presence
164
+ if (!task.autoresearch) {
165
+ return {
166
+ taskId: task.id,
167
+ runId,
168
+ status: "error",
169
+ error: `Task ${task.id} has type=autoresearch but no autoresearch config`,
170
+ durationMs: Date.now() - startTime,
171
+ timestamp: new Date().toISOString(),
172
+ };
173
+ }
174
+ try {
175
+ const worker = await getOrCreateWorker(task, emitter);
176
+ // Get phase-appropriate tools and filter
177
+ const tools = worker.getTools();
178
+ const toolFilter = await worker.getToolFilterForCurrentPhase();
179
+ const systemPrompt = await worker.getSystemPrompt();
180
+ const cyclePrompt = await worker.getCyclePrompt();
181
+ // Register research tools on NeuroLink if it supports registerTool
182
+ const sdk = neurolink;
183
+ if (typeof sdk.registerTool === "function") {
184
+ for (const [name, tool] of Object.entries(tools)) {
185
+ sdk.registerTool(name, tool);
186
+ }
187
+ }
188
+ // Get the current phase's forced tool (if any)
189
+ const phasePolicy = await worker.getPhaseToolPolicy();
190
+ // Extract provider/model from autoresearch config
191
+ const arConfig = task.autoresearch;
192
+ // Create an AbortController so the tick respects the task timeout.
193
+ // Each tick should complete within a bounded time; 20 tool-call steps
194
+ // is enough for one explore→write→experiment→record cycle.
195
+ const tickTimeoutMs = (task.timeout ?? 120_000);
196
+ const abortController = new AbortController();
197
+ const abortTimer = setTimeout(() => abortController.abort(), tickTimeoutMs);
198
+ // Call generate
199
+ const generateOptions = {
200
+ input: { text: cyclePrompt },
201
+ systemPrompt,
202
+ tools: true,
203
+ skipToolPromptInjection: true,
204
+ maxSteps: 20, // Bound tool-call steps per tick to avoid runaway loops
205
+ abortSignal: abortController.signal,
206
+ // Forward provider/model from autoresearch config
207
+ ...(arConfig?.provider ? { provider: arConfig.provider } : {}),
208
+ ...(arConfig?.model ? { model: arConfig.model } : {}),
209
+ ...(arConfig?.thinkingLevel || task.thinkingLevel
210
+ ? {
211
+ thinkingConfig: {
212
+ thinkingLevel: arConfig?.thinkingLevel ?? task.thinkingLevel,
213
+ },
214
+ }
215
+ : {}),
216
+ ...(task.timeout ? { timeout: task.timeout } : {}),
217
+ // Phase tool restrictions
218
+ toolFilter,
219
+ // Use prepareStep to force tool only on first step, not all steps
220
+ ...(phasePolicy.forcedTool
221
+ ? {
222
+ prepareStep: ({ stepNumber }) => {
223
+ if (stepNumber === 0) {
224
+ return {
225
+ toolChoice: {
226
+ type: "tool",
227
+ toolName: phasePolicy.forcedTool,
228
+ },
229
+ };
230
+ }
231
+ return {};
232
+ },
233
+ }
234
+ : {}),
235
+ // Observability
236
+ requestId: runId,
237
+ ...(task.metadata?.maxBudgetUsd
238
+ ? { maxBudgetUsd: task.metadata.maxBudgetUsd }
239
+ : {}),
240
+ };
241
+ let result;
242
+ try {
243
+ result = await withTimeout(neurolink.generate(generateOptions), tickTimeoutMs, new Error(`Autoresearch tick exceeded ${tickTimeoutMs}ms timeout`));
244
+ }
245
+ finally {
246
+ clearTimeout(abortTimer);
247
+ }
248
+ // ── Phase advancement ──────────────────────────────
249
+ // The deterministic worker path (runExperimentCycle) advances
250
+ // phases internally. The task path must do it here, based on
251
+ // which tools the AI actually called this tick.
252
+ const calledTools = (result.toolExecutions ?? [])
253
+ .map((te) => {
254
+ const t = te;
255
+ return typeof t.name === "string" ? t.name : "";
256
+ })
257
+ .filter(Boolean);
258
+ const currentState = await worker.getState();
259
+ const currentPhase = currentState?.currentPhase ?? "bootstrap";
260
+ const nextPhase = inferNextPhase(currentPhase, calledTools);
261
+ if (nextPhase) {
262
+ await worker.advancePhase(nextPhase);
263
+ logger.debug("[AutoresearchExecutor] Phase advanced", {
264
+ from: currentPhase,
265
+ to: nextPhase,
266
+ calledTools,
267
+ });
268
+ }
269
+ return {
270
+ taskId: task.id,
271
+ runId,
272
+ status: "success",
273
+ output: result.content,
274
+ toolCalls: result.toolExecutions?.map((te) => ({
275
+ name: te.name,
276
+ input: te.input,
277
+ output: te.output,
278
+ })),
279
+ tokensUsed: result.usage
280
+ ? { input: result.usage.input ?? 0, output: result.usage.output ?? 0 }
281
+ : undefined,
282
+ durationMs: Date.now() - startTime,
283
+ timestamp: new Date().toISOString(),
284
+ };
285
+ }
286
+ catch (error) {
287
+ const msg = error instanceof Error ? error.message : String(error);
288
+ logger.error("[AutoresearchExecutor] Tick failed", {
289
+ taskId: task.id,
290
+ runId,
291
+ error: msg,
292
+ });
293
+ return {
294
+ taskId: task.id,
295
+ runId,
296
+ status: "error",
297
+ error: msg,
298
+ durationMs: Date.now() - startTime,
299
+ timestamp: new Date().toISOString(),
300
+ };
301
+ }
302
+ }
@@ -12,6 +12,7 @@ export declare const TaskErrorCodes: {
12
12
  readonly TASK_LIMIT_REACHED: "TASK-005";
13
13
  readonly TASK_DISABLED: "TASK-006";
14
14
  readonly SCHEDULE_FAILED: "TASK-007";
15
+ readonly TASK_VALIDATION_FAILED: "TASK-008";
15
16
  };
16
17
  export declare const TaskError: {
17
18
  codes: {
@@ -22,8 +23,9 @@ export declare const TaskError: {
22
23
  readonly TASK_LIMIT_REACHED: "TASK-005";
23
24
  readonly TASK_DISABLED: "TASK-006";
24
25
  readonly SCHEDULE_FAILED: "TASK-007";
26
+ readonly TASK_VALIDATION_FAILED: "TASK-008";
25
27
  };
26
- create: (code: "TASK_NOT_FOUND" | "BACKEND_NOT_INITIALIZED" | "BACKEND_UNKNOWN" | "INVALID_TASK_STATUS" | "TASK_LIMIT_REACHED" | "TASK_DISABLED" | "SCHEDULE_FAILED", message: string, options?: {
28
+ create: (code: "TASK_NOT_FOUND" | "BACKEND_NOT_INITIALIZED" | "BACKEND_UNKNOWN" | "INVALID_TASK_STATUS" | "TASK_LIMIT_REACHED" | "TASK_DISABLED" | "SCHEDULE_FAILED" | "TASK_VALIDATION_FAILED", message: string, options?: {
27
29
  retryable?: boolean;
28
30
  details?: Record<string, unknown>;
29
31
  cause?: Error;
@@ -13,5 +13,6 @@ export const TaskErrorCodes = {
13
13
  TASK_LIMIT_REACHED: "TASK-005",
14
14
  TASK_DISABLED: "TASK-006",
15
15
  SCHEDULE_FAILED: "TASK-007",
16
+ TASK_VALIDATION_FAILED: "TASK-008",
16
17
  };
17
18
  export const TaskError = createErrorFactory("Task", TaskErrorCodes);
@@ -7,11 +7,13 @@
7
7
  * - Retry with exponential backoff for transient errors
8
8
  * - Run result construction and logging
9
9
  */
10
- import type { TaskStore, Task, TaskRunResult, NeuroLinkExecutable } from "../types/taskTypes.js";
10
+ import type { AutoresearchEmitter } from "../types/autoresearchTypes.js";
11
+ import type { NeuroLinkExecutable, Task, TaskRunResult, TaskStore } from "../types/taskTypes.js";
11
12
  export declare class TaskExecutor {
12
13
  private neurolink;
13
14
  private store;
14
- constructor(neurolink: NeuroLinkExecutable, store: TaskStore);
15
+ private emitter?;
16
+ constructor(neurolink: NeuroLinkExecutable, store: TaskStore, emitter?: AutoresearchEmitter | undefined);
15
17
  /**
16
18
  * Execute a task once. Called by the backend on each scheduled tick.
17
19
  * Returns the run result (success or error).
@@ -9,6 +9,7 @@
9
9
  */
10
10
  import { nanoid } from "nanoid";
11
11
  import { logger } from "../utils/logger.js";
12
+ import { executeAutoresearchTick } from "./autoresearchTaskExecutor.js";
12
13
  /** Errors that are transient and should be retried */
13
14
  const TRANSIENT_PATTERNS = [
14
15
  "rate limit",
@@ -31,9 +32,11 @@ function isTransientError(error) {
31
32
  export class TaskExecutor {
32
33
  neurolink;
33
34
  store;
34
- constructor(neurolink, store) {
35
+ emitter;
36
+ constructor(neurolink, store, emitter) {
35
37
  this.neurolink = neurolink;
36
38
  this.store = store;
39
+ this.emitter = emitter;
37
40
  }
38
41
  /**
39
42
  * Execute a task once. Called by the backend on each scheduled tick.
@@ -80,6 +83,10 @@ export class TaskExecutor {
80
83
  }
81
84
  // ── Internal ──────────────────────────────────────────
82
85
  async executeOnce(task, runId) {
86
+ // ── Autoresearch routing ──
87
+ if (task.type === "autoresearch") {
88
+ return executeAutoresearchTick(task, this.neurolink, this.emitter);
89
+ }
83
90
  const startTime = Date.now();
84
91
  // Build generate options
85
92
  const generateOptions = {
@@ -9,11 +9,12 @@
9
9
  * await neurolink.tasks.create({ name: "monitor", prompt: "...", schedule: { type: "interval", every: 60000 } });
10
10
  */
11
11
  import { nanoid } from "nanoid";
12
+ import { TASK_DEFAULTS, } from "../types/taskTypes.js";
12
13
  import { logger } from "../utils/logger.js";
14
+ import { clearWorkerCache } from "./autoresearchTaskExecutor.js";
13
15
  import { TaskBackendRegistry } from "./backends/taskBackendRegistry.js";
14
16
  import { TaskError } from "./errors.js";
15
17
  import { TaskExecutor } from "./taskExecutor.js";
16
- import { TASK_DEFAULTS, } from "../types/taskTypes.js";
17
18
  export class TaskManager {
18
19
  neurolink;
19
20
  config;
@@ -60,8 +61,8 @@ export class TaskManager {
60
61
  // Create backend
61
62
  this.backend = await TaskBackendRegistry.create(backendName, this.config);
62
63
  await this.backend.initialize();
63
- // Create executor
64
- this.executor = new TaskExecutor(this.neurolink, this.store);
64
+ // Create executor (pass emitter for autoresearch lifecycle events)
65
+ this.executor = new TaskExecutor(this.neurolink, this.store, this.emitter);
65
66
  // Re-schedule active tasks from store (handles restarts)
66
67
  await this.rescheduleActiveTasks();
67
68
  this.initialized = true;
@@ -103,12 +104,31 @@ export class TaskManager {
103
104
  throw TaskError.create("TASK_LIMIT_REACHED", `Task limit reached (${maxTasks}). Delete existing tasks or increase maxTasks config.`);
104
105
  }
105
106
  const now = new Date().toISOString();
107
+ // Autoresearch validation
108
+ const taskType = definition.type ?? "standard";
109
+ if (taskType === "autoresearch") {
110
+ const ar = definition.autoresearch;
111
+ if (!ar) {
112
+ throw TaskError.create("TASK_VALIDATION_FAILED", 'Tasks with type "autoresearch" require an autoresearch config.');
113
+ }
114
+ if (!ar.repoPath ||
115
+ !ar.runCommand ||
116
+ !ar.mutablePaths?.length ||
117
+ !ar.metric) {
118
+ throw TaskError.create("TASK_VALIDATION_FAILED", "Autoresearch config must include repoPath, runCommand, mutablePaths (non-empty), and metric.");
119
+ }
120
+ }
121
+ // Reject autoresearch config on non-autoresearch tasks
122
+ if (definition.autoresearch && taskType !== "autoresearch") {
123
+ throw TaskError.create("TASK_VALIDATION_FAILED", 'Tasks with autoresearch config must have type "autoresearch".');
124
+ }
106
125
  const task = {
107
126
  id: `task_${nanoid(12)}`,
108
127
  name: definition.name,
109
128
  prompt: definition.prompt,
110
129
  schedule: definition.schedule,
111
130
  mode: definition.mode ?? TASK_DEFAULTS.mode,
131
+ type: taskType,
112
132
  status: "active",
113
133
  tools: definition.tools ?? TASK_DEFAULTS.tools,
114
134
  timeout: definition.timeout ?? TASK_DEFAULTS.timeout,
@@ -138,6 +158,9 @@ export class TaskManager {
138
158
  ? { maxRuns: definition.maxRuns }
139
159
  : {}),
140
160
  ...(definition.metadata ? { metadata: definition.metadata } : {}),
161
+ ...(definition.autoresearch
162
+ ? { autoresearch: definition.autoresearch }
163
+ : {}),
141
164
  };
142
165
  // Generate session ID for continuation mode
143
166
  if (task.mode === "continuation") {
@@ -352,6 +375,7 @@ export class TaskManager {
352
375
  await this.store.shutdown();
353
376
  }
354
377
  this.callbacks.clear();
378
+ clearWorkerCache();
355
379
  this.initialized = false;
356
380
  this.initPromise = null;
357
381
  logger.info("[TaskManager] Shut down");
@@ -58,7 +58,7 @@ export declare function createTaskTools(manager: TaskManager): {
58
58
  schedule?: undefined;
59
59
  }>;
60
60
  listTasks: import("ai").Tool<{
61
- status?: "failed" | "pending" | "active" | "paused" | "completed" | "cancelled" | undefined;
61
+ status?: "failed" | "pending" | "completed" | "cancelled" | "active" | "paused" | undefined;
62
62
  }, {
63
63
  success: boolean;
64
64
  count: number;
@@ -87,4 +87,19 @@ export declare const ATTR: {
87
87
  readonly CONTEXT_TOKENS_AFTER: "context.tokens_after";
88
88
  readonly MW_COUNT: "middleware.count";
89
89
  readonly MW_NAMES: "middleware.names";
90
+ readonly AR_TAG: "autoresearch.tag";
91
+ readonly AR_BRANCH: "autoresearch.branch";
92
+ readonly AR_PHASE: "autoresearch.phase";
93
+ readonly AR_PHASE_FROM: "autoresearch.phase_from";
94
+ readonly AR_PHASE_TO: "autoresearch.phase_to";
95
+ readonly AR_RUN_COUNT: "autoresearch.run_count";
96
+ readonly AR_KEEP_COUNT: "autoresearch.keep_count";
97
+ readonly AR_STATUS: "autoresearch.status";
98
+ readonly AR_METRIC: "autoresearch.metric";
99
+ readonly AR_BEST_METRIC: "autoresearch.best_metric";
100
+ readonly AR_DIRECTION: "autoresearch.metric_direction";
101
+ readonly AR_COMMIT: "autoresearch.commit";
102
+ readonly AR_DURATION_MS: "autoresearch.duration_ms";
103
+ readonly AR_DESCRIPTION: "autoresearch.description";
104
+ readonly AR_ERROR_CODE: "autoresearch.error_code";
90
105
  };
@@ -97,4 +97,20 @@ export const ATTR = {
97
97
  // Middleware
98
98
  MW_COUNT: "middleware.count",
99
99
  MW_NAMES: "middleware.names",
100
+ // Autoresearch
101
+ AR_TAG: "autoresearch.tag",
102
+ AR_BRANCH: "autoresearch.branch",
103
+ AR_PHASE: "autoresearch.phase",
104
+ AR_PHASE_FROM: "autoresearch.phase_from",
105
+ AR_PHASE_TO: "autoresearch.phase_to",
106
+ AR_RUN_COUNT: "autoresearch.run_count",
107
+ AR_KEEP_COUNT: "autoresearch.keep_count",
108
+ AR_STATUS: "autoresearch.status",
109
+ AR_METRIC: "autoresearch.metric",
110
+ AR_BEST_METRIC: "autoresearch.best_metric",
111
+ AR_DIRECTION: "autoresearch.metric_direction",
112
+ AR_COMMIT: "autoresearch.commit",
113
+ AR_DURATION_MS: "autoresearch.duration_ms",
114
+ AR_DESCRIPTION: "autoresearch.description",
115
+ AR_ERROR_CODE: "autoresearch.error_code",
100
116
  };
@@ -13,4 +13,5 @@ export declare const tracers: {
13
13
  readonly middleware: import("@opentelemetry/api").Tracer;
14
14
  readonly processor: import("@opentelemetry/api").Tracer;
15
15
  readonly file: import("@opentelemetry/api").Tracer;
16
+ readonly autoresearch: import("@opentelemetry/api").Tracer;
16
17
  };
@@ -14,4 +14,5 @@ export const tracers = {
14
14
  middleware: trace.getTracer("neurolink.middleware"),
15
15
  processor: trace.getTracer("neurolink.processor"),
16
16
  file: trace.getTracer("neurolink.file"),
17
+ autoresearch: trace.getTracer("neurolink.autoresearch"),
17
18
  };