@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.
- package/CHANGELOG.md +12 -0
- package/README.md +19 -0
- package/dist/agent/directTools.d.ts +2 -2
- package/dist/auth/errors.d.ts +1 -1
- package/dist/auth/middleware/AuthMiddleware.d.ts +1 -1
- package/dist/auth/providers/BaseAuthProvider.d.ts +1 -1
- package/dist/autoresearch/config.d.ts +11 -0
- package/dist/autoresearch/config.js +108 -0
- package/dist/autoresearch/errors.d.ts +40 -0
- package/dist/autoresearch/errors.js +20 -0
- package/dist/autoresearch/index.d.ts +23 -0
- package/dist/autoresearch/index.js +34 -0
- package/dist/autoresearch/phasePolicy.d.ts +9 -0
- package/dist/autoresearch/phasePolicy.js +69 -0
- package/dist/autoresearch/promptCompiler.d.ts +15 -0
- package/dist/autoresearch/promptCompiler.js +120 -0
- package/dist/autoresearch/repoPolicy.d.ts +32 -0
- package/dist/autoresearch/repoPolicy.js +128 -0
- package/dist/autoresearch/resultRecorder.d.ts +20 -0
- package/dist/autoresearch/resultRecorder.js +130 -0
- package/dist/autoresearch/runner.d.ts +10 -0
- package/dist/autoresearch/runner.js +102 -0
- package/dist/autoresearch/stateStore.d.ts +12 -0
- package/dist/autoresearch/stateStore.js +163 -0
- package/dist/autoresearch/summaryParser.d.ts +16 -0
- package/dist/autoresearch/summaryParser.js +94 -0
- package/dist/autoresearch/tools.d.ts +257 -0
- package/dist/autoresearch/tools.js +617 -0
- package/dist/autoresearch/worker.d.ts +71 -0
- package/dist/autoresearch/worker.js +417 -0
- package/dist/browser/neurolink.min.js +340 -324
- package/dist/cli/commands/autoresearch.d.ts +41 -0
- package/dist/cli/commands/autoresearch.js +487 -0
- package/dist/cli/commands/config.d.ts +1 -1
- package/dist/cli/commands/task.d.ts +2 -0
- package/dist/cli/commands/task.js +32 -3
- package/dist/cli/loop/optionsSchema.d.ts +1 -1
- package/dist/cli/parser.js +4 -1
- package/dist/core/baseProvider.js +18 -0
- package/dist/core/factory.d.ts +2 -2
- package/dist/core/factory.js +4 -4
- package/dist/evaluation/errors/EvaluationError.d.ts +1 -1
- package/dist/factories/providerFactory.d.ts +4 -4
- package/dist/factories/providerFactory.js +20 -7
- package/dist/factories/providerRegistry.d.ts +5 -0
- package/dist/factories/providerRegistry.js +45 -26
- package/dist/lib/agent/directTools.d.ts +2 -2
- package/dist/lib/auth/errors.d.ts +1 -1
- package/dist/lib/auth/middleware/AuthMiddleware.d.ts +1 -1
- package/dist/lib/auth/providers/BaseAuthProvider.d.ts +1 -1
- package/dist/lib/autoresearch/config.d.ts +11 -0
- package/dist/lib/autoresearch/config.js +109 -0
- package/dist/lib/autoresearch/errors.d.ts +40 -0
- package/dist/lib/autoresearch/errors.js +21 -0
- package/dist/lib/autoresearch/index.d.ts +23 -0
- package/dist/lib/autoresearch/index.js +35 -0
- package/dist/lib/autoresearch/phasePolicy.d.ts +9 -0
- package/dist/lib/autoresearch/phasePolicy.js +70 -0
- package/dist/lib/autoresearch/promptCompiler.d.ts +15 -0
- package/dist/lib/autoresearch/promptCompiler.js +121 -0
- package/dist/lib/autoresearch/repoPolicy.d.ts +32 -0
- package/dist/lib/autoresearch/repoPolicy.js +129 -0
- package/dist/lib/autoresearch/resultRecorder.d.ts +20 -0
- package/dist/lib/autoresearch/resultRecorder.js +131 -0
- package/dist/lib/autoresearch/runner.d.ts +10 -0
- package/dist/lib/autoresearch/runner.js +103 -0
- package/dist/lib/autoresearch/stateStore.d.ts +12 -0
- package/dist/lib/autoresearch/stateStore.js +164 -0
- package/dist/lib/autoresearch/summaryParser.d.ts +16 -0
- package/dist/lib/autoresearch/summaryParser.js +95 -0
- package/dist/lib/autoresearch/tools.d.ts +257 -0
- package/dist/lib/autoresearch/tools.js +618 -0
- package/dist/lib/autoresearch/worker.d.ts +71 -0
- package/dist/lib/autoresearch/worker.js +418 -0
- package/dist/lib/core/baseProvider.js +18 -0
- package/dist/lib/core/factory.d.ts +2 -2
- package/dist/lib/core/factory.js +4 -4
- package/dist/lib/evaluation/errors/EvaluationError.d.ts +1 -1
- package/dist/lib/factories/providerFactory.d.ts +4 -4
- package/dist/lib/factories/providerFactory.js +20 -7
- package/dist/lib/factories/providerRegistry.d.ts +5 -0
- package/dist/lib/factories/providerRegistry.js +45 -26
- package/dist/lib/files/fileTools.d.ts +1 -1
- package/dist/lib/neurolink.d.ts +21 -0
- package/dist/lib/neurolink.js +91 -8
- package/dist/lib/providers/amazonBedrock.d.ts +6 -1
- package/dist/lib/providers/amazonBedrock.js +14 -2
- package/dist/lib/providers/amazonSagemaker.d.ts +7 -1
- package/dist/lib/providers/amazonSagemaker.js +21 -3
- package/dist/lib/providers/anthropic.d.ts +4 -1
- package/dist/lib/providers/anthropic.js +18 -5
- package/dist/lib/providers/azureOpenai.d.ts +2 -1
- package/dist/lib/providers/azureOpenai.js +10 -5
- package/dist/lib/providers/googleAiStudio.d.ts +4 -1
- package/dist/lib/providers/googleAiStudio.js +6 -7
- package/dist/lib/providers/googleVertex.d.ts +3 -1
- package/dist/lib/providers/googleVertex.js +96 -17
- package/dist/lib/providers/huggingFace.d.ts +2 -1
- package/dist/lib/providers/huggingFace.js +4 -4
- package/dist/lib/providers/litellm.d.ts +5 -1
- package/dist/lib/providers/litellm.js +16 -11
- package/dist/lib/providers/mistral.d.ts +2 -1
- package/dist/lib/providers/mistral.js +2 -2
- package/dist/lib/providers/ollama.d.ts +3 -1
- package/dist/lib/providers/ollama.js +2 -2
- package/dist/lib/providers/openAI.d.ts +5 -1
- package/dist/lib/providers/openAI.js +15 -5
- package/dist/lib/providers/openRouter.d.ts +5 -1
- package/dist/lib/providers/openRouter.js +19 -7
- package/dist/lib/providers/openaiCompatible.d.ts +4 -1
- package/dist/lib/providers/openaiCompatible.js +18 -4
- package/dist/lib/tasks/autoresearchTaskExecutor.d.ts +32 -0
- package/dist/lib/tasks/autoresearchTaskExecutor.js +303 -0
- package/dist/lib/tasks/errors.d.ts +3 -1
- package/dist/lib/tasks/errors.js +1 -0
- package/dist/lib/tasks/taskExecutor.d.ts +4 -2
- package/dist/lib/tasks/taskExecutor.js +8 -1
- package/dist/lib/tasks/taskManager.js +27 -3
- package/dist/lib/tasks/tools/taskTools.d.ts +1 -1
- package/dist/lib/telemetry/attributes.d.ts +15 -0
- package/dist/lib/telemetry/attributes.js +16 -0
- package/dist/lib/telemetry/tracers.d.ts +1 -0
- package/dist/lib/telemetry/tracers.js +1 -0
- package/dist/lib/types/autoresearchTypes.d.ts +194 -0
- package/dist/lib/types/autoresearchTypes.js +18 -0
- package/dist/lib/types/common.d.ts +11 -0
- package/dist/lib/types/configTypes.d.ts +7 -0
- package/dist/lib/types/generateTypes.d.ts +13 -0
- package/dist/lib/types/index.d.ts +16 -14
- package/dist/lib/types/index.js +21 -17
- package/dist/lib/types/providers.d.ts +75 -0
- package/dist/lib/types/streamTypes.d.ts +7 -1
- package/dist/lib/types/taskTypes.d.ts +38 -0
- package/dist/lib/workflow/config.d.ts +3 -3
- package/dist/neurolink.d.ts +21 -0
- package/dist/neurolink.js +91 -8
- package/dist/providers/amazonBedrock.d.ts +6 -1
- package/dist/providers/amazonBedrock.js +14 -2
- package/dist/providers/amazonSagemaker.d.ts +7 -1
- package/dist/providers/amazonSagemaker.js +21 -3
- package/dist/providers/anthropic.d.ts +4 -1
- package/dist/providers/anthropic.js +18 -5
- package/dist/providers/azureOpenai.d.ts +2 -1
- package/dist/providers/azureOpenai.js +10 -5
- package/dist/providers/googleAiStudio.d.ts +4 -1
- package/dist/providers/googleAiStudio.js +6 -7
- package/dist/providers/googleVertex.d.ts +3 -1
- package/dist/providers/googleVertex.js +96 -17
- package/dist/providers/huggingFace.d.ts +2 -1
- package/dist/providers/huggingFace.js +4 -4
- package/dist/providers/litellm.d.ts +5 -1
- package/dist/providers/litellm.js +16 -11
- package/dist/providers/mistral.d.ts +2 -1
- package/dist/providers/mistral.js +2 -2
- package/dist/providers/ollama.d.ts +3 -1
- package/dist/providers/ollama.js +2 -2
- package/dist/providers/openAI.d.ts +5 -1
- package/dist/providers/openAI.js +15 -5
- package/dist/providers/openRouter.d.ts +5 -1
- package/dist/providers/openRouter.js +19 -7
- package/dist/providers/openaiCompatible.d.ts +4 -1
- package/dist/providers/openaiCompatible.js +18 -4
- package/dist/rag/errors/RAGError.d.ts +1 -1
- package/dist/tasks/autoresearchTaskExecutor.d.ts +32 -0
- package/dist/tasks/autoresearchTaskExecutor.js +302 -0
- package/dist/tasks/errors.d.ts +3 -1
- package/dist/tasks/errors.js +1 -0
- package/dist/tasks/taskExecutor.d.ts +4 -2
- package/dist/tasks/taskExecutor.js +8 -1
- package/dist/tasks/taskManager.js +27 -3
- package/dist/tasks/tools/taskTools.d.ts +1 -1
- package/dist/telemetry/attributes.d.ts +15 -0
- package/dist/telemetry/attributes.js +16 -0
- package/dist/telemetry/tracers.d.ts +1 -0
- package/dist/telemetry/tracers.js +1 -0
- package/dist/types/autoresearchTypes.d.ts +194 -0
- package/dist/types/autoresearchTypes.js +17 -0
- package/dist/types/common.d.ts +11 -0
- package/dist/types/configTypes.d.ts +7 -0
- package/dist/types/generateTypes.d.ts +13 -0
- package/dist/types/index.d.ts +16 -14
- package/dist/types/index.js +21 -17
- package/dist/types/providers.d.ts +75 -0
- package/dist/types/streamTypes.d.ts +7 -1
- package/dist/types/taskTypes.d.ts +38 -0
- 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
|
+
}
|
package/dist/tasks/errors.d.ts
CHANGED
|
@@ -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;
|
package/dist/tasks/errors.js
CHANGED
|
@@ -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 {
|
|
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
|
-
|
|
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
|
-
|
|
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" | "
|
|
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
|
};
|