@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,418 @@
1
+ /**
2
+ * Research worker — orchestrates the full experiment loop.
3
+ *
4
+ * Wires tools, state, policy, and NeuroLink generate() into a
5
+ * single experiment cycle. Can run standalone or via TaskManager.
6
+ *
7
+ * Emits autoresearch:* lifecycle events through an injected emitter
8
+ * and wraps key operations in OpenTelemetry spans for observability.
9
+ */
10
+ import { execFileSync } from "node:child_process";
11
+ import { ATTR } from "../telemetry/attributes.js";
12
+ import { tracers } from "../telemetry/tracers.js";
13
+ import { withSpan } from "../telemetry/withSpan.js";
14
+ import { withTimeout } from "../utils/errorHandling.js";
15
+ import { logger } from "../utils/logger.js";
16
+ import { resolveConfig, validateConfig } from "./config.js";
17
+ import { AutoresearchError } from "./errors.js";
18
+ import { getPhaseToolPolicy } from "./phasePolicy.js";
19
+ import { PromptCompiler } from "./promptCompiler.js";
20
+ import { RepoPolicy } from "./repoPolicy.js";
21
+ import { ResultRecorder } from "./resultRecorder.js";
22
+ import { ExperimentRunner } from "./runner.js";
23
+ import { ResearchStateStore } from "./stateStore.js";
24
+ import { createResearchTools } from "./tools.js";
25
+ function isBetter(candidate, best, direction) {
26
+ return direction === "lower" ? candidate < best : candidate > best;
27
+ }
28
+ function decideOutcome(metric, crashed, timedOut, bestMetric, direction) {
29
+ if (timedOut) {
30
+ return "timeout";
31
+ }
32
+ if (crashed || metric === null) {
33
+ return "crash";
34
+ }
35
+ if (bestMetric === null) {
36
+ return "keep";
37
+ } // First run is baseline
38
+ return isBetter(metric, bestMetric, direction) ? "keep" : "discard";
39
+ }
40
+ export class ResearchWorker {
41
+ config;
42
+ stateStore;
43
+ repoPolicy;
44
+ runner;
45
+ recorder;
46
+ promptCompiler;
47
+ initialized = false;
48
+ /** Event emitter injected by NeuroLink/TaskManager for lifecycle events. */
49
+ emitter;
50
+ constructor(configInput) {
51
+ this.config = resolveConfig(configInput);
52
+ this.stateStore = new ResearchStateStore(this.config.repoPath, this.config.statePath);
53
+ this.repoPolicy = new RepoPolicy(this.config);
54
+ this.runner = new ExperimentRunner(this.config);
55
+ this.recorder = new ResultRecorder(this.config);
56
+ this.promptCompiler = new PromptCompiler(this.config);
57
+ }
58
+ // ── Emitter integration ──────────────────────────────────
59
+ /** Set the event emitter (called by NeuroLink/TaskManager during integration). */
60
+ setEmitter(emitter) {
61
+ this.emitter = emitter;
62
+ }
63
+ /** Emit a lifecycle event. Safe to call when no emitter is set. */
64
+ emit(event, ...args) {
65
+ this.emitter?.emit(event, ...args);
66
+ }
67
+ // ── Lifecycle ────────────────────────────────────────────
68
+ /** Initialize: validate config, ensure branch, create state */
69
+ async initialize(tag) {
70
+ return withSpan({
71
+ name: "autoresearch.initialize",
72
+ tracer: tracers.autoresearch,
73
+ attributes: {
74
+ [ATTR.AR_TAG]: tag,
75
+ [ATTR.AR_BRANCH]: `${this.config.branchPrefix}${tag}`,
76
+ },
77
+ }, async (span) => {
78
+ validateConfig(this.config);
79
+ const branch = `${this.config.branchPrefix}${tag}`;
80
+ // Create branch if it doesn't exist
81
+ try {
82
+ const currentBranch = execFileSync("git", ["rev-parse", "--abbrev-ref", "HEAD"], {
83
+ cwd: this.config.repoPath,
84
+ encoding: "utf-8",
85
+ }).trim();
86
+ if (currentBranch !== branch) {
87
+ try {
88
+ execFileSync("git", ["checkout", "-b", branch], {
89
+ cwd: this.config.repoPath,
90
+ stdio: "ignore",
91
+ });
92
+ }
93
+ catch {
94
+ // Branch may already exist
95
+ execFileSync("git", ["checkout", branch], {
96
+ cwd: this.config.repoPath,
97
+ stdio: "ignore",
98
+ });
99
+ }
100
+ }
101
+ }
102
+ catch (error) {
103
+ this.emitError(tag, "BRANCH_ERROR", `Failed to setup branch ${branch}`);
104
+ throw AutoresearchError.create("BRANCH_ERROR", `Failed to setup branch ${branch}`, {
105
+ cause: error instanceof Error ? error : undefined,
106
+ });
107
+ }
108
+ // Initialize state
109
+ const state = await this.stateStore.initialize(tag, branch);
110
+ // Ensure results file exists
111
+ await this.recorder.ensureResultsFile();
112
+ this.initialized = true;
113
+ span.setAttribute(ATTR.AR_PHASE, state.currentPhase);
114
+ logger.info("[Autoresearch] Worker initialized", { tag, branch });
115
+ this.emit("autoresearch:initialized", {
116
+ tag,
117
+ branch,
118
+ config: {
119
+ repoPath: this.config.repoPath,
120
+ runCommand: this.config.runCommand,
121
+ metric: this.config.metric,
122
+ timeoutMs: this.config.timeoutMs,
123
+ },
124
+ });
125
+ return state;
126
+ });
127
+ }
128
+ /** Load existing state (for resuming) */
129
+ async resume() {
130
+ return withSpan({
131
+ name: "autoresearch.resume",
132
+ tracer: tracers.autoresearch,
133
+ }, async (span) => {
134
+ const state = await this.stateStore.load();
135
+ if (!state) {
136
+ throw AutoresearchError.create("STATE_NOT_FOUND", "No state file found. Run initialize() first.");
137
+ }
138
+ validateConfig(this.config);
139
+ // Ensure we're on the correct branch (may have changed after restart)
140
+ try {
141
+ const currentBranch = execFileSync("git", ["rev-parse", "--abbrev-ref", "HEAD"], { cwd: this.config.repoPath, encoding: "utf-8" }).trim();
142
+ if (currentBranch !== state.branch) {
143
+ execFileSync("git", ["checkout", state.branch], {
144
+ cwd: this.config.repoPath,
145
+ stdio: "ignore",
146
+ });
147
+ }
148
+ }
149
+ catch (branchErr) {
150
+ logger.error("[Autoresearch] Failed to restore branch", {
151
+ expected: state.branch,
152
+ error: branchErr instanceof Error
153
+ ? branchErr.message
154
+ : String(branchErr),
155
+ });
156
+ throw AutoresearchError.create("BRANCH_ERROR", `Failed to checkout branch ${state.branch} during resume`, { cause: branchErr instanceof Error ? branchErr : undefined });
157
+ }
158
+ this.initialized = true;
159
+ span.setAttribute(ATTR.AR_TAG, state.tag);
160
+ span.setAttribute(ATTR.AR_BRANCH, state.branch);
161
+ span.setAttribute(ATTR.AR_RUN_COUNT, state.runCount);
162
+ span.setAttribute(ATTR.AR_PHASE, state.currentPhase);
163
+ this.emit("autoresearch:resumed", {
164
+ tag: state.tag,
165
+ branch: state.branch,
166
+ runCount: state.runCount,
167
+ currentPhase: state.currentPhase,
168
+ });
169
+ return state;
170
+ });
171
+ }
172
+ /** Run one full experiment cycle without AI — just the deterministic parts */
173
+ async runExperimentCycle(description) {
174
+ if (!this.initialized) {
175
+ throw AutoresearchError.create("WORKER_NOT_INITIALIZED", "Call initialize() or resume() first");
176
+ }
177
+ const state = await this.stateStore.load();
178
+ if (!state) {
179
+ throw AutoresearchError.create("STATE_NOT_FOUND", "State file missing");
180
+ }
181
+ return withSpan({
182
+ name: "autoresearch.experiment_cycle",
183
+ tracer: tracers.autoresearch,
184
+ attributes: {
185
+ [ATTR.AR_TAG]: state.tag,
186
+ [ATTR.AR_RUN_COUNT]: state.runCount,
187
+ [ATTR.AR_DESCRIPTION]: description,
188
+ },
189
+ }, async (span) => {
190
+ const cycleStart = Date.now();
191
+ this.emit("autoresearch:experiment-started", {
192
+ tag: state.tag,
193
+ runCount: state.runCount,
194
+ description,
195
+ });
196
+ await this.advancePhase("run");
197
+ // Run the experiment
198
+ logger.info("[Autoresearch] Running experiment", {
199
+ runCount: state.runCount,
200
+ description,
201
+ });
202
+ const summary = await withSpan({
203
+ name: "autoresearch.experiment_run",
204
+ tracer: tracers.autoresearch,
205
+ attributes: { [ATTR.AR_TAG]: state.tag },
206
+ }, async () => {
207
+ return withTimeout(this.runner.run(), this.config.timeoutMs + 30_000, new Error("Experiment runner exceeded safety timeout"));
208
+ });
209
+ await this.advancePhase("evaluate");
210
+ // Deterministic decision
211
+ const status = decideOutcome(summary.metric, summary.crashed, summary.timedOut, state.bestMetric, this.config.metric.direction);
212
+ // Get commit hash
213
+ const commit = this.repoPolicy.getHeadCommit() || "unknown";
214
+ // Build record
215
+ const record = {
216
+ commit,
217
+ metric: summary.metric,
218
+ memoryGb: summary.memoryValue,
219
+ status,
220
+ description,
221
+ timestamp: new Date().toISOString(),
222
+ };
223
+ // Record result
224
+ await this.recorder.appendTsv(record);
225
+ await this.recorder.appendJsonl(record);
226
+ await this.advancePhase("accept_or_revert");
227
+ // Update state based on outcome
228
+ if (status === "keep") {
229
+ await this.stateStore.update({
230
+ acceptedCommit: commit,
231
+ bestMetric: summary.metric,
232
+ baselineMetric: state.baselineMetric ?? summary.metric,
233
+ keepCount: state.keepCount + 1,
234
+ runCount: state.runCount + 1,
235
+ lastStatus: status,
236
+ candidateCommit: null,
237
+ });
238
+ // Emit metric-improved if this beats a previous best
239
+ if (summary.metric !== null &&
240
+ state.bestMetric !== null &&
241
+ isBetter(summary.metric, state.bestMetric, this.config.metric.direction)) {
242
+ this.emit("autoresearch:metric-improved", {
243
+ tag: state.tag,
244
+ previousBest: state.bestMetric,
245
+ newBest: summary.metric,
246
+ commit,
247
+ direction: this.config.metric.direction,
248
+ runCount: state.runCount + 1,
249
+ });
250
+ }
251
+ }
252
+ else {
253
+ // Revert on discard/crash/timeout
254
+ if (state.acceptedCommit) {
255
+ this.emit("autoresearch:revert", {
256
+ tag: state.tag,
257
+ targetCommit: state.acceptedCommit,
258
+ reason: status,
259
+ runCount: state.runCount,
260
+ });
261
+ try {
262
+ execFileSync("git", ["reset", "--hard", state.acceptedCommit], {
263
+ cwd: this.config.repoPath,
264
+ stdio: "ignore",
265
+ });
266
+ }
267
+ catch (error) {
268
+ const errorMsg = error instanceof Error ? error.message : String(error);
269
+ logger.error("[Autoresearch] Revert failed — state NOT updated", {
270
+ error: errorMsg,
271
+ });
272
+ this.emit("autoresearch:revert-failed", {
273
+ tag: state.tag,
274
+ targetCommit: state.acceptedCommit,
275
+ error: errorMsg,
276
+ runCount: state.runCount,
277
+ });
278
+ this.emitError(state.tag, "REVERT_FAILED", `Failed to revert to ${state.acceptedCommit}. Manual intervention required.`, state.currentPhase, state.runCount);
279
+ // Do NOT advance state — repo is in unknown state
280
+ throw AutoresearchError.create("REVERT_FAILED", `Failed to revert to ${state.acceptedCommit}. Manual intervention required.`, {
281
+ cause: error instanceof Error ? error : undefined,
282
+ });
283
+ }
284
+ }
285
+ // Only reach here if revert succeeded (or no acceptedCommit to revert to)
286
+ // currentPhase advancement happens unconditionally below via advancePhase("propose")
287
+ await this.stateStore.update({
288
+ runCount: state.runCount + 1,
289
+ lastStatus: status,
290
+ candidateCommit: null,
291
+ });
292
+ }
293
+ const durationMs = Date.now() - cycleStart;
294
+ // Set span attributes for the completed cycle
295
+ span.setAttribute(ATTR.AR_STATUS, status);
296
+ if (summary.metric !== null) {
297
+ span.setAttribute(ATTR.AR_METRIC, summary.metric);
298
+ }
299
+ span.setAttribute(ATTR.AR_COMMIT, commit);
300
+ span.setAttribute(ATTR.AR_DURATION_MS, durationMs);
301
+ logger.info("[Autoresearch] Experiment complete", {
302
+ status,
303
+ metric: summary.metric,
304
+ runCount: state.runCount + 1,
305
+ });
306
+ this.emit("autoresearch:experiment-completed", {
307
+ tag: state.tag,
308
+ runCount: state.runCount + 1,
309
+ status,
310
+ metric: summary.metric,
311
+ commit,
312
+ description,
313
+ durationMs,
314
+ });
315
+ // Emit state-updated with final state snapshot
316
+ const updatedState = await this.stateStore.load();
317
+ if (updatedState) {
318
+ this.emit("autoresearch:state-updated", {
319
+ tag: updatedState.tag,
320
+ phase: updatedState.currentPhase,
321
+ runCount: updatedState.runCount,
322
+ keepCount: updatedState.keepCount,
323
+ bestMetric: updatedState.bestMetric,
324
+ });
325
+ }
326
+ await this.advancePhase("propose");
327
+ return record;
328
+ });
329
+ }
330
+ /** Get the tools record for use with NeuroLink.generate() */
331
+ getTools() {
332
+ return createResearchTools({
333
+ config: this.config,
334
+ stateStore: this.stateStore,
335
+ repoPolicy: this.repoPolicy,
336
+ runner: this.runner,
337
+ recorder: this.recorder,
338
+ });
339
+ }
340
+ /** Build system prompt */
341
+ async getSystemPrompt() {
342
+ return this.promptCompiler.buildSystemPrompt();
343
+ }
344
+ /** Build cycle prompt */
345
+ async getCyclePrompt() {
346
+ const state = await this.stateStore.load();
347
+ if (!state) {
348
+ throw AutoresearchError.create("STATE_NOT_FOUND", "No state");
349
+ }
350
+ const results = await this.recorder.readAll();
351
+ return this.promptCompiler.buildCyclePrompt(state, results);
352
+ }
353
+ /** Get current state */
354
+ async getState() {
355
+ return this.stateStore.load();
356
+ }
357
+ /** Get results stats */
358
+ async getStats() {
359
+ return this.recorder.getStats();
360
+ }
361
+ /** Get config */
362
+ getConfig() {
363
+ return this.config;
364
+ }
365
+ // ── Phase management (Phase 1b/1c) ──────────────────────
366
+ /**
367
+ * Single authority for phase transitions.
368
+ * Persists the new phase to the state store and emits phase-changed event.
369
+ */
370
+ async advancePhase(phase) {
371
+ const currentState = await this.stateStore.load();
372
+ const fromPhase = currentState?.currentPhase ?? "bootstrap";
373
+ await this.stateStore.update({ currentPhase: phase });
374
+ logger.debug("[Autoresearch] Phase advanced", { phase });
375
+ if (fromPhase !== phase) {
376
+ this.emit("autoresearch:phase-changed", {
377
+ from: fromPhase,
378
+ to: phase,
379
+ runCount: currentState?.runCount ?? 0,
380
+ tag: currentState?.tag ?? "",
381
+ });
382
+ }
383
+ }
384
+ /**
385
+ * Returns the phase tool policy for the current phase.
386
+ * Reads the phase from persisted state.
387
+ */
388
+ async getPhaseToolPolicy() {
389
+ const state = await this.stateStore.load();
390
+ if (!state) {
391
+ throw AutoresearchError.create("STATE_NOT_FOUND", "No state for getPhaseToolPolicy");
392
+ }
393
+ return getPhaseToolPolicy(state.currentPhase);
394
+ }
395
+ /**
396
+ * Returns a tool filter object for the current phase, compatible
397
+ * with NeuroLink generate()'s toolFilter option.
398
+ *
399
+ * Returns { include: string[] } listing only the tools allowed
400
+ * in the current phase.
401
+ */
402
+ async getToolFilterForCurrentPhase() {
403
+ const policy = await this.getPhaseToolPolicy();
404
+ return { include: [...policy.activeTools] };
405
+ }
406
+ // ── Private helpers ──────────────────────────────────────
407
+ /** Emit an autoresearch:error event. */
408
+ emitError(tag, code, message, phase, runCount) {
409
+ this.emit("autoresearch:error", {
410
+ tag,
411
+ error: message,
412
+ code,
413
+ phase,
414
+ runCount,
415
+ });
416
+ }
417
+ }
418
+ //# sourceMappingURL=worker.js.map
@@ -776,6 +776,24 @@ export class BaseProvider {
776
776
  },
777
777
  responseTime: 0, // BaseProvider doesn't track response time directly
778
778
  toolsUsed: result.toolsUsed || [],
779
+ // Map toolExecutions from EnhancedGenerateResult shape to TextGenerationResult shape
780
+ // Preserve original timing/status fields when present, fall back to safe defaults
781
+ toolExecutions: result.toolExecutions?.map((te) => {
782
+ const t = te;
783
+ return {
784
+ // Spread original fields first so normalized fields take precedence
785
+ ...te,
786
+ toolName: te.name,
787
+ executionTime: typeof t.executionTime === "number"
788
+ ? t.executionTime
789
+ : typeof t.duration === "number"
790
+ ? t.duration
791
+ : 0,
792
+ success: typeof t.success === "boolean"
793
+ ? t.success
794
+ : t.status === undefined || t.status === "success",
795
+ };
796
+ }),
779
797
  enhancedWithTools: !!(result.toolsUsed && result.toolsUsed.length > 0),
780
798
  analytics: result.analytics,
781
799
  evaluation: result.evaluation,
@@ -1,4 +1,4 @@
1
- import type { AIProvider, SupportedModelName } from "../types/index.js";
1
+ import type { AIProvider, SupportedModelName, NeurolinkCredentials } from "../types/index.js";
2
2
  import { AIProviderName } from "../constants/enums.js";
3
3
  import type { UnknownRecord } from "../types/common.js";
4
4
  import type { ProviderPairResult } from "../types/typeAliases.js";
@@ -27,7 +27,7 @@ export declare class AIProviderFactory {
27
27
  * @param region - Optional region override for cloud providers
28
28
  * @returns AIProvider instance
29
29
  */
30
- static createProvider(providerName: string, modelName?: string | null, enableMCP?: boolean, sdk?: UnknownRecord, region?: string): Promise<AIProvider>;
30
+ static createProvider(providerName: string, modelName?: string | null, enableMCP?: boolean, sdk?: UnknownRecord, region?: string, credentials?: NeurolinkCredentials): Promise<AIProvider>;
31
31
  /**
32
32
  * Create a provider instance with specific provider enum and model
33
33
  * @param provider - Provider enum value
@@ -168,7 +168,7 @@ export class AIProviderFactory {
168
168
  return resolvedModelName;
169
169
  }
170
170
  }
171
- static async createResolvedProvider(providerName, resolvedModelName, sdk, region, functionTag) {
171
+ static async createResolvedProvider(providerName, resolvedModelName, sdk, region, functionTag, credentials) {
172
172
  await withTimeout(ProviderRegistry.registerAllProviders(), 30_000, ErrorFactory.toolTimeout("provider-registration", 30_000));
173
173
  const normalizedName = this.normalizeProviderName(providerName);
174
174
  const finalModelName = resolvedModelName === "default" || resolvedModelName === null
@@ -180,7 +180,7 @@ export class AIProviderFactory {
180
180
  resolvedModelName: resolvedModelName || "not resolved",
181
181
  finalModelName: finalModelName || "using provider default",
182
182
  });
183
- const provider = await withTimeout(ProviderFactory.createProvider(normalizedName, finalModelName, sdk, region), 30_000, ErrorFactory.toolTimeout(`provider-creation:${normalizedName}`, 30_000));
183
+ const provider = await withTimeout(ProviderFactory.createProvider(normalizedName, finalModelName, sdk, region, credentials), 30_000, ErrorFactory.toolTimeout(`provider-creation:${normalizedName}`, 30_000));
184
184
  return { normalizedName, finalModelName, provider };
185
185
  }
186
186
  /**
@@ -192,7 +192,7 @@ export class AIProviderFactory {
192
192
  * @param region - Optional region override for cloud providers
193
193
  * @returns AIProvider instance
194
194
  */
195
- static async createProvider(providerName, modelName, enableMCP = true, sdk, region) {
195
+ static async createProvider(providerName, modelName, enableMCP = true, sdk, region, credentials) {
196
196
  const functionTag = "AIProviderFactory.createProvider";
197
197
  // Providers are registered via ProviderFactory.initialize() on first use
198
198
  return factoryTracer.startActiveSpan("neurolink.factory.createProvider", {
@@ -226,7 +226,7 @@ export class AIProviderFactory {
226
226
  // The dynamic model provider now provides reliable functionality without hanging
227
227
  let resolvedModelName = this.resolveModelFromEnvironment(providerName, modelName, functionTag);
228
228
  resolvedModelName = await this.resolveDynamicModelName(providerName, modelName, resolvedModelName, functionTag);
229
- const { normalizedName, finalModelName, provider } = await this.createResolvedProvider(providerName, resolvedModelName, sdk, region, functionTag);
229
+ const { normalizedName, finalModelName, provider } = await this.createResolvedProvider(providerName, resolvedModelName, sdk, region, functionTag, credentials);
230
230
  // Summary logging in format expected by debugging tools
231
231
  logger.debug(`[AIProviderFactory] Provider creation completed { providerName: '${normalizedName}', modelName: '${finalModelName}' }`);
232
232
  logger.debug(`[AIProviderFactory] Resolved model: ${finalModelName}`);
@@ -69,7 +69,7 @@ export declare const evaluationErrors: {
69
69
  /** Rate limit hit during evaluation */
70
70
  readonly RATE_LIMIT_ERROR: "RATE_LIMIT_ERROR";
71
71
  };
72
- create: (code: "PROVIDER_ERROR" | "CONFIGURATION_ERROR" | "EVALUATION_FAILED" | "PARSE_ERROR" | "STRATEGY_NOT_FOUND" | "CUSTOM_EVALUATOR_ERROR" | "BATCH_EVALUATION_ERROR" | "AGGREGATION_ERROR" | "REGISTRY_ERROR" | "MAX_RETRIES_EXCEEDED" | "TIMEOUT_ERROR" | "RATE_LIMIT_ERROR", message: string, options?: {
72
+ create: (code: "CONFIGURATION_ERROR" | "PROVIDER_ERROR" | "EVALUATION_FAILED" | "PARSE_ERROR" | "STRATEGY_NOT_FOUND" | "CUSTOM_EVALUATOR_ERROR" | "BATCH_EVALUATION_ERROR" | "AGGREGATION_ERROR" | "REGISTRY_ERROR" | "MAX_RETRIES_EXCEEDED" | "TIMEOUT_ERROR" | "RATE_LIMIT_ERROR", message: string, options?: {
73
73
  retryable?: boolean;
74
74
  details?: Record<string, unknown>;
75
75
  cause?: Error;
@@ -1,6 +1,6 @@
1
1
  import type { AIProviderName } from "../constants/enums.js";
2
2
  import type { UnknownRecord } from "../types/common.js";
3
- import type { AIProvider } from "../types/index.js";
3
+ import type { AIProvider, NeurolinkCredentials } from "../types/index.js";
4
4
  /**
5
5
  * Provider constructor interface - supports both sync constructors and async factory functions
6
6
  */
@@ -33,7 +33,7 @@ export declare class ProviderFactory {
33
33
  * @param providerName - Provider name (optional, uses NEUROLINK_PROVIDER env var or 'vertex' as default)
34
34
  * @param modelName - Model name (optional, uses provider-specific env var or registry default)
35
35
  */
36
- static createProvider(providerName?: AIProviderName | string, modelName?: string, sdk?: UnknownRecord, region?: string): Promise<AIProvider>;
36
+ static createProvider(providerName?: AIProviderName | string, modelName?: string, sdk?: UnknownRecord, region?: string, credentials?: NeurolinkCredentials): Promise<AIProvider>;
37
37
  /**
38
38
  * Check if a provider is registered
39
39
  */
@@ -67,10 +67,10 @@ export declare class ProviderFactory {
67
67
  * Create the best available provider for the given name
68
68
  * Used by NeuroLink SDK for streaming and generation
69
69
  */
70
- static createBestProvider(providerName: AIProviderName | string, modelName?: string, enableMCP?: boolean, sdk?: UnknownRecord): Promise<AIProvider>;
70
+ static createBestProvider(providerName: AIProviderName | string, modelName?: string, enableMCP?: boolean, sdk?: UnknownRecord, credentials?: NeurolinkCredentials): Promise<AIProvider>;
71
71
  }
72
72
  /**
73
73
  * Helper function to create providers with backward compatibility
74
74
  */
75
- export declare function createAIProvider(providerName: AIProviderName | string, modelName?: string): Promise<AIProvider>;
75
+ export declare function createAIProvider(providerName: AIProviderName | string, modelName?: string, credentials?: NeurolinkCredentials): Promise<AIProvider>;
76
76
  export {};
@@ -30,7 +30,7 @@ export class ProviderFactory {
30
30
  * @param providerName - Provider name (optional, uses NEUROLINK_PROVIDER env var or 'vertex' as default)
31
31
  * @param modelName - Model name (optional, uses provider-specific env var or registry default)
32
32
  */
33
- static async createProvider(providerName, modelName, sdk, region) {
33
+ static async createProvider(providerName, modelName, sdk, region, credentials) {
34
34
  // Note: Providers are registered explicitly by ProviderRegistry to avoid circular dependencies
35
35
  // Use environment variable or default if not specified
36
36
  const resolvedProviderName = providerName ||
@@ -56,13 +56,26 @@ export class ProviderFactory {
56
56
  // Fallback to registry default if no env var
57
57
  model = model || registration.defaultModel;
58
58
  }
59
+ // Map registered provider names to NeurolinkCredentials keys.
60
+ // Most names match (openai, anthropic, vertex, bedrock, etc.)
61
+ // but some differ (google-ai → googleAiStudio, openai-compatible → openaiCompatible).
62
+ const credentialKeyMap = {
63
+ "google-ai": "googleAiStudio",
64
+ "openai-compatible": "openaiCompatible",
65
+ huggingface: "huggingFace",
66
+ };
67
+ const credKey = credentialKeyMap[normalizedName] ?? normalizedName;
68
+ // Extract provider-scoped credential slice (e.g. credentials.openai for OpenAI)
69
+ const scopedCredentials = credentials
70
+ ? credentials[credKey]
71
+ : undefined;
59
72
  try {
60
73
  if (typeof registration.constructor !== "function") {
61
74
  throw new Error(`Invalid constructor for provider ${providerName}: not a function`);
62
75
  }
63
76
  let result;
64
77
  try {
65
- const factoryResult = registration.constructor(model, resolvedProviderName, sdk, region);
78
+ const factoryResult = registration.constructor(model, resolvedProviderName, sdk, region, scopedCredentials);
66
79
  // Handle both sync and async results
67
80
  result =
68
81
  factoryResult instanceof Promise
@@ -74,7 +87,7 @@ export class ProviderFactory {
74
87
  registration.constructor.prototype.constructor ===
75
88
  registration.constructor) {
76
89
  try {
77
- result = new registration.constructor(model, resolvedProviderName, sdk, region);
90
+ result = new registration.constructor(model, resolvedProviderName, sdk, region, scopedCredentials);
78
91
  }
79
92
  catch (constructorError) {
80
93
  throw new Error(`Both factory function and constructor failed. Factory error: ${factoryError}. Constructor error: ${constructorError}`, { cause: constructorError });
@@ -154,14 +167,14 @@ export class ProviderFactory {
154
167
  * Create the best available provider for the given name
155
168
  * Used by NeuroLink SDK for streaming and generation
156
169
  */
157
- static async createBestProvider(providerName, modelName, enableMCP, sdk) {
158
- return await ProviderFactory.createProvider(providerName, modelName, sdk);
170
+ static async createBestProvider(providerName, modelName, enableMCP, sdk, credentials) {
171
+ return await ProviderFactory.createProvider(providerName, modelName, sdk, undefined, credentials);
159
172
  }
160
173
  }
161
174
  /**
162
175
  * Helper function to create providers with backward compatibility
163
176
  */
164
- export async function createAIProvider(providerName, modelName) {
165
- return await ProviderFactory.createProvider(providerName, modelName);
177
+ export async function createAIProvider(providerName, modelName, credentials) {
178
+ return await ProviderFactory.createProvider(providerName, modelName, undefined, undefined, credentials);
166
179
  }
167
180
  //# sourceMappingURL=providerFactory.js.map
@@ -13,6 +13,11 @@ export declare class ProviderRegistry {
13
13
  static registerAllProviders(): Promise<void>;
14
14
  /**
15
15
  * Internal registration implementation
16
+ *
17
+ * This method is a flat list of 13 provider registrations. Each registration
18
+ * is self-contained and extracting helpers would add indirection without
19
+ * reducing complexity — the function is long because there are many providers,
20
+ * not because any single registration is complex.
16
21
  */
17
22
  private static _doRegister;
18
23
  /**