@juspay/neurolink 10.5.2 → 10.6.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 (94) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/agent/agent.js +39 -8
  3. package/dist/agent/agentNetwork.js +62 -20
  4. package/dist/agent/agentToolRegistrar.d.ts +48 -0
  5. package/dist/agent/agentToolRegistrar.js +336 -0
  6. package/dist/agent/index.d.ts +3 -0
  7. package/dist/agent/index.js +4 -0
  8. package/dist/agent/isolatedAgentRunner.d.ts +73 -0
  9. package/dist/agent/isolatedAgentRunner.js +946 -0
  10. package/dist/agent/structuredRecovery.d.ts +30 -0
  11. package/dist/agent/structuredRecovery.js +104 -0
  12. package/dist/browser/neurolink.min.js +414 -398
  13. package/dist/cli/loop/optionsSchema.d.ts +1 -1
  14. package/dist/core/baseProvider.d.ts +8 -0
  15. package/dist/core/baseProvider.js +40 -19
  16. package/dist/core/modules/GenerationHandler.d.ts +2 -6
  17. package/dist/core/modules/GenerationHandler.js +10 -1
  18. package/dist/core/toolExecutionRecorder.d.ts +76 -0
  19. package/dist/core/toolExecutionRecorder.js +261 -0
  20. package/dist/evaluation/contextBuilder.js +6 -6
  21. package/dist/lib/agent/agent.js +39 -8
  22. package/dist/lib/agent/agentNetwork.js +62 -20
  23. package/dist/lib/agent/agentToolRegistrar.d.ts +48 -0
  24. package/dist/lib/agent/agentToolRegistrar.js +337 -0
  25. package/dist/lib/agent/index.d.ts +3 -0
  26. package/dist/lib/agent/index.js +4 -0
  27. package/dist/lib/agent/isolatedAgentRunner.d.ts +73 -0
  28. package/dist/lib/agent/isolatedAgentRunner.js +947 -0
  29. package/dist/lib/agent/structuredRecovery.d.ts +30 -0
  30. package/dist/lib/agent/structuredRecovery.js +105 -0
  31. package/dist/lib/core/baseProvider.d.ts +8 -0
  32. package/dist/lib/core/baseProvider.js +40 -19
  33. package/dist/lib/core/modules/GenerationHandler.d.ts +2 -6
  34. package/dist/lib/core/modules/GenerationHandler.js +10 -1
  35. package/dist/lib/core/toolExecutionRecorder.d.ts +76 -0
  36. package/dist/lib/core/toolExecutionRecorder.js +262 -0
  37. package/dist/lib/evaluation/contextBuilder.js +6 -6
  38. package/dist/lib/models/modelRegistry.d.ts +23 -0
  39. package/dist/lib/models/modelRegistry.js +62 -1
  40. package/dist/lib/models/modelResolver.js +1 -0
  41. package/dist/lib/neurolink.d.ts +80 -1
  42. package/dist/lib/neurolink.js +199 -37
  43. package/dist/lib/providers/amazonBedrock.js +15 -2
  44. package/dist/lib/providers/anthropic.js +23 -11
  45. package/dist/lib/providers/googleAiStudio.js +8 -1
  46. package/dist/lib/providers/googleNativeGemini3.d.ts +2 -0
  47. package/dist/lib/providers/googleNativeGemini3.js +8 -1
  48. package/dist/lib/providers/googleVertex.js +56 -16
  49. package/dist/lib/providers/openaiChatCompletionsClient.js +18 -5
  50. package/dist/lib/tasks/autoresearchTaskExecutor.js +7 -6
  51. package/dist/lib/tasks/taskExecutor.js +2 -5
  52. package/dist/lib/types/agentNetwork.d.ts +23 -8
  53. package/dist/lib/types/generate.d.ts +62 -5
  54. package/dist/lib/types/index.d.ts +1 -0
  55. package/dist/lib/types/index.js +1 -0
  56. package/dist/lib/types/isolatedAgent.d.ts +365 -0
  57. package/dist/lib/types/isolatedAgent.js +12 -0
  58. package/dist/lib/types/model.d.ts +8 -0
  59. package/dist/lib/types/stream.d.ts +3 -1
  60. package/dist/lib/types/task.d.ts +3 -3
  61. package/dist/lib/utils/logger.d.ts +12 -3
  62. package/dist/lib/utils/logger.js +11 -3
  63. package/dist/lib/utils/transformationUtils.d.ts +14 -0
  64. package/dist/lib/utils/transformationUtils.js +33 -10
  65. package/dist/lib/voice/livekit/realtimeVoiceAgent.js +6 -3
  66. package/dist/models/modelRegistry.d.ts +23 -0
  67. package/dist/models/modelRegistry.js +62 -1
  68. package/dist/models/modelResolver.js +1 -0
  69. package/dist/neurolink.d.ts +80 -1
  70. package/dist/neurolink.js +199 -37
  71. package/dist/providers/amazonBedrock.js +15 -2
  72. package/dist/providers/anthropic.js +23 -11
  73. package/dist/providers/googleAiStudio.js +8 -1
  74. package/dist/providers/googleNativeGemini3.d.ts +2 -0
  75. package/dist/providers/googleNativeGemini3.js +8 -1
  76. package/dist/providers/googleVertex.js +56 -16
  77. package/dist/providers/openaiChatCompletionsClient.js +18 -5
  78. package/dist/tasks/autoresearchTaskExecutor.js +7 -6
  79. package/dist/tasks/taskExecutor.js +2 -5
  80. package/dist/types/agentNetwork.d.ts +23 -8
  81. package/dist/types/generate.d.ts +62 -5
  82. package/dist/types/index.d.ts +1 -0
  83. package/dist/types/index.js +1 -0
  84. package/dist/types/isolatedAgent.d.ts +365 -0
  85. package/dist/types/isolatedAgent.js +11 -0
  86. package/dist/types/model.d.ts +8 -0
  87. package/dist/types/stream.d.ts +3 -1
  88. package/dist/types/task.d.ts +3 -3
  89. package/dist/utils/logger.d.ts +12 -3
  90. package/dist/utils/logger.js +11 -3
  91. package/dist/utils/transformationUtils.d.ts +14 -0
  92. package/dist/utils/transformationUtils.js +33 -10
  93. package/dist/voice/livekit/realtimeVoiceAgent.js +6 -3
  94. package/package.json +3 -2
@@ -33,6 +33,29 @@ export declare function getModelById(id: string): ModelInfo | undefined;
33
33
  * custom models retain the existing behavior until capability data is added.
34
34
  */
35
35
  export declare function modelSupports(capability: keyof ModelCapabilities, provider: string, model: string): boolean;
36
+ /**
37
+ * Whether a model accepts classic sampling parameters
38
+ * (`temperature` / `topP`).
39
+ *
40
+ * Resolution order: an explicit `capabilities.samplingParams` on a matching
41
+ * registry entry wins; otherwise the known rejecting-family patterns decide.
42
+ * Unknown models default to supported (same fail-open contract as
43
+ * `modelSupports`). Providers strip the parameters — including on
44
+ * retry/fallback request rebuilds — when this returns false.
45
+ */
46
+ export declare function modelSupportsSamplingParams(provider: string, model: string | undefined): boolean;
47
+ /**
48
+ * Sampling params to actually send for a model: passthrough when supported,
49
+ * `{}` (with a debug log) when the model rejects them. Apply at every
50
+ * request-build site, including retry/fallback rebuilds.
51
+ */
52
+ export declare function resolveSamplingParams(provider: string, model: string | undefined, params: {
53
+ temperature?: number;
54
+ topP?: number;
55
+ }, site: string): {
56
+ temperature?: number;
57
+ topP?: number;
58
+ };
36
59
  /**
37
60
  * Get models by provider
38
61
  */
@@ -4,6 +4,7 @@
4
4
  * Part of Phase 4.1 - Models Command System
5
5
  */
6
6
  import { DEFAULT_MODEL_ALIASES } from "../types/index.js";
7
+ import { logger } from "../utils/logger.js";
7
8
  import { AIProviderName, OpenAIModels, AzureOpenAIModels, GoogleAIModels, AnthropicModels, BedrockModels, MistralModels, OllamaModels, } from "../constants/enums.js";
8
9
  /**
9
10
  * Comprehensive model registry
@@ -2356,7 +2357,67 @@ export function modelSupports(capability, provider, model) {
2356
2357
  if (!modelInfo || modelInfo.provider !== provider.toLowerCase()) {
2357
2358
  return true;
2358
2359
  }
2359
- return modelInfo.capabilities[capability];
2360
+ // Optional capabilities (e.g. samplingParams) default to supported.
2361
+ return modelInfo.capabilities[capability] ?? true;
2362
+ }
2363
+ /**
2364
+ * Model families known to reject classic sampling parameters
2365
+ * (`temperature` / `topP`) in favour of reasoning-effort controls:
2366
+ * Claude Sonnet 5, Opus 4.7+ (including Opus 5), and the Fable/Mythos 5
2367
+ * tier — notably as served through Vertex. Kept as patterns because gateway
2368
+ * model ids carry arbitrary prefixes/suffixes
2369
+ * (`vertex_ai/claude-sonnet-5@20260203`) that exact registry ids can't cover.
2370
+ */
2371
+ const SAMPLING_PARAM_REJECTING_FAMILIES = [
2372
+ /sonnet[-_.]?5(?![0-9])/i,
2373
+ /opus[-_.]?4[-_.]?(?:[7-9]|\d{2,})(?![0-9])/i,
2374
+ /opus[-_.]?5(?![0-9])/i,
2375
+ /fable/i,
2376
+ /mythos/i,
2377
+ ];
2378
+ /**
2379
+ * Whether a model accepts classic sampling parameters
2380
+ * (`temperature` / `topP`).
2381
+ *
2382
+ * Resolution order: an explicit `capabilities.samplingParams` on a matching
2383
+ * registry entry wins; otherwise the known rejecting-family patterns decide.
2384
+ * Unknown models default to supported (same fail-open contract as
2385
+ * `modelSupports`). Providers strip the parameters — including on
2386
+ * retry/fallback request rebuilds — when this returns false.
2387
+ */
2388
+ export function modelSupportsSamplingParams(provider, model) {
2389
+ const normalizedModel = (model ?? "").toLowerCase();
2390
+ if (!normalizedModel) {
2391
+ return true;
2392
+ }
2393
+ const modelId = MODEL_ALIASES[normalizedModel] ?? normalizedModel;
2394
+ const modelInfo = MODEL_REGISTRY[modelId];
2395
+ if (modelInfo &&
2396
+ modelInfo.provider === provider.toLowerCase() &&
2397
+ modelInfo.capabilities.samplingParams !== undefined) {
2398
+ return modelInfo.capabilities.samplingParams;
2399
+ }
2400
+ return !SAMPLING_PARAM_REJECTING_FAMILIES.some((family) => family.test(modelId) || family.test(normalizedModel));
2401
+ }
2402
+ /**
2403
+ * Sampling params to actually send for a model: passthrough when supported,
2404
+ * `{}` (with a debug log) when the model rejects them. Apply at every
2405
+ * request-build site, including retry/fallback rebuilds.
2406
+ */
2407
+ export function resolveSamplingParams(provider, model, params, site) {
2408
+ if (modelSupportsSamplingParams(provider, model)) {
2409
+ return params;
2410
+ }
2411
+ if (params.temperature !== undefined || params.topP !== undefined) {
2412
+ logger.debug(`[ModelRegistry] Stripping sampling params for ${provider}/${model} at ${site} — model rejects temperature/topP`, {
2413
+ provider,
2414
+ model,
2415
+ site,
2416
+ hadTemperature: params.temperature !== undefined,
2417
+ hadTopP: params.topP !== undefined,
2418
+ });
2419
+ }
2420
+ return {};
2360
2421
  }
2361
2422
  /**
2362
2423
  * Get models by provider
@@ -102,6 +102,7 @@ export class ModelResolver {
102
102
  multimodal: [],
103
103
  streaming: [],
104
104
  jsonMode: [],
105
+ samplingParams: [],
105
106
  };
106
107
  // Group models by capabilities
107
108
  for (const model of models) {
@@ -5,7 +5,7 @@
5
5
  * Enhanced AI provider system with natural MCP tool access.
6
6
  * Uses real MCP infrastructure for tool discovery and execution.
7
7
  */
8
- import type { AgentDefinition, AgentNetworkConfig, NetworkExecutionInput, NetworkExecutionOptions, NetworkExecutionResult, NetworkStreamChunk } from "./types/index.js";
8
+ import type { AgentDefinition, AgentNetworkConfig, AgentRunOptions, AgentRunOutcome, AgentToolRegistrationOptions, IsolatedAgentDefinition, NetworkExecutionInput, NetworkExecutionOptions, NetworkExecutionResult, NetworkStreamChunk, WorkerInstanceOptions } from "./types/index.js";
9
9
  import type { CompactionConfig, CompactionResult, SpanData, ObservabilityConfig, MetricsSummary, MCPToolAnnotations, TraceView, AuthenticatedContext, AuthProvider, JsonObject, NeuroLinkEvents, TypedEventEmitter, MCPEnhancementsConfig, NeuroLinkAuthConfig, NeurolinkConstructorConfig, ChatMessage, ExternalMCPOperationResult, ExternalMCPServerInstance, ExternalMCPToolInfo, GenerateOptions, GenerateResult, ProviderStatus, TextGenerationOptions, TextGenerationResult, MCPExecutableTool, MCPServerInfo, MCPStatus, StreamOptions, StreamResult, ToolExecutionContext, ToolExecutionSummary, ToolInfo, ToolRegistrationOptions, BatchOperationResult, StreamGenerationEndContext, ToolRoutingServerDescriptor, ToolDedupConfig, ToolConfig, KnowledgeEngineStatus } from "./types/index.js";
10
10
  import { ConversationMemoryManager } from "./core/conversationMemoryManager.js";
11
11
  import type { RedisConversationMemoryManager } from "./core/redisConversationMemoryManager.js";
@@ -169,6 +169,11 @@ export declare class NeuroLink {
169
169
  * This context will be merged with any runtime context passed by the AI model
170
170
  */
171
171
  private toolExecutionContext?;
172
+ /**
173
+ * Set when registerAgentTool() has registered at least one delegation
174
+ * tool — gates the per-turn delegation scope in generate().
175
+ */
176
+ private hasAgentTools;
172
177
  /**
173
178
  * Creates a new NeuroLink instance for AI text generation with MCP tool integration.
174
179
  *
@@ -2419,6 +2424,80 @@ export declare class NeuroLink {
2419
2424
  * @since 8.38.0
2420
2425
  */
2421
2426
  createNetwork(config: AgentNetworkConfig): Promise<import("./agent/agentNetwork.js").AgentNetwork>;
2427
+ /**
2428
+ * Create a worker-mode NeuroLink instance for sub-agent execution.
2429
+ *
2430
+ * Worker mode is the framework-provided version of the config block every
2431
+ * consumer used to copy by hand: conversation memory OFF, orchestration
2432
+ * OFF, observability inherited from this instance with
2433
+ * `autoDetectExternalProvider: true` + `skipLangfuseSpanProcessor: true`
2434
+ * (worker spans join the host's tracer without duplicate Langfuse
2435
+ * exports), credentials inherited, the host's tool registry shared (so
2436
+ * worker tool calls reuse the host's connections), and an internal log
2437
+ * bridge attached with a caller-supplied tag.
2438
+ *
2439
+ * Dispose the worker (`worker.dispose()`) when done — `runIsolatedAgent`
2440
+ * does this automatically in a `finally`.
2441
+ *
2442
+ * @param options - Worker options (log tag/sink, registry sharing, config)
2443
+ * @returns A new worker-mode NeuroLink instance
2444
+ * @see {@link WorkerInstanceOptions}
2445
+ */
2446
+ createWorkerInstance(options?: WorkerInstanceOptions): NeuroLink;
2447
+ /**
2448
+ * Run an isolated sub-agent: a worker instance (see
2449
+ * {@link createWorkerInstance}) executes a tool-using research pass under
2450
+ * the turn budget (wrap-up nudge, stall watchdog, honest `stopReason`),
2451
+ * then an extraction pass ALWAYS runs tools-off on its own timeout with a
2452
+ * structured-recovery ladder and corrective re-asks. A non-empty execution
2453
+ * record never produces an empty result (mechanical digest fallback), a
2454
+ * parent `abortSignal` stops everything cleanly, and `options.leg` enables
2455
+ * leashed mode with TTL'd resume handles ({@link continueAgent} /
2456
+ * {@link stopAgent}).
2457
+ *
2458
+ * @param definition - Agent definition (+ optional structured extraction)
2459
+ * @param input - Task input: string or structured object
2460
+ * @param options - Run options (abort, overrides, tool context, events, leg)
2461
+ * @returns The run outcome
2462
+ * @see {@link IsolatedAgentDefinition}
2463
+ * @see {@link AgentRunOptions}
2464
+ * @see {@link AgentRunOutcome}
2465
+ */
2466
+ runIsolatedAgent(definition: IsolatedAgentDefinition, input: string | Record<string, unknown>, options?: AgentRunOptions): Promise<AgentRunOutcome>;
2467
+ /**
2468
+ * Resume a leashed isolated-agent run by handle. `guidance`, when given,
2469
+ * is appended as a user turn before the next leg — the supervisor's
2470
+ * re-steering channel. An expired handle returns its tombstoned final
2471
+ * outcome exactly once.
2472
+ *
2473
+ * @param handle - Handle from an `in_progress` {@link AgentRunOutcome}
2474
+ * @param guidance - Optional supervisor guidance for the next leg
2475
+ * @returns The next leg's outcome (or the final outcome)
2476
+ */
2477
+ continueAgent(handle: string, guidance?: string): Promise<AgentRunOutcome>;
2478
+ /**
2479
+ * Stop a leashed isolated-agent run: dispose its worker and return the
2480
+ * final outcome (mechanical digest over everything gathered so far).
2481
+ *
2482
+ * @param handle - Handle from an `in_progress` {@link AgentRunOutcome}
2483
+ * @returns The final outcome
2484
+ */
2485
+ stopAgent(handle: string): Promise<AgentRunOutcome>;
2486
+ /**
2487
+ * Register an isolated agent as a delegation tool on THIS instance, so
2488
+ * its existing generate() loop can delegate — no second router generate.
2489
+ * Framework policy (per-turn caps, depth withholding, a process-wide
2490
+ * concurrency pool with queue timeout) is enforced in the loop itself,
2491
+ * and every refusal carries its recovery instruction in the error text.
2492
+ *
2493
+ * @param definition - Agent definition (+ optional structured extraction)
2494
+ * @param options - Registration options (name, caps, depth, pool, leg)
2495
+ * @returns The registered tool name
2496
+ * @see {@link AgentToolRegistrationOptions}
2497
+ */
2498
+ registerAgentTool(definition: IsolatedAgentDefinition, options?: AgentToolRegistrationOptions): Promise<{
2499
+ name: string;
2500
+ }>;
2422
2501
  /**
2423
2502
  * Execute an agent network with the given input.
2424
2503
  *
@@ -92,7 +92,8 @@ import { directAgentTools } from "./agent/directTools.js";
92
92
  import { BinaryTaskClassifier } from "./utils/taskClassifier.js";
93
93
  // Tool detection and execution imports
94
94
  // Transformation utilities
95
- import { extractToolNames, optimizeToolForCollection, transformAvailableTools, transformParamsForLogging, transformToolExecutions, transformToolExecutionsForMCP, transformToolsForMCP, transformToolsToDescriptions, transformToolsToExpectedFormat, } from "./utils/transformationUtils.js";
95
+ import { extractToolNames, optimizeToolForCollection, transformAvailableTools, transformParamsForLogging, transformToolExecutionsForMCP, transformToolsForMCP, transformToolsToDescriptions, transformToolsToExpectedFormat, } from "./utils/transformationUtils.js";
96
+ import { toToolExecutionRecords } from "./core/toolExecutionRecorder.js";
96
97
  import { isNonNullObject } from "./utils/typeUtils.js";
97
98
  import { getWorkflow } from "./workflow/core/workflowRegistry.js";
98
99
  import { runWorkflow } from "./workflow/core/workflowRunner.js";
@@ -755,6 +756,11 @@ export class NeuroLink {
755
756
  * This context will be merged with any runtime context passed by the AI model
756
757
  */
757
758
  toolExecutionContext;
759
+ /**
760
+ * Set when registerAgentTool() has registered at least one delegation
761
+ * tool — gates the per-turn delegation scope in generate().
762
+ */
763
+ hasAgentTools = false;
758
764
  /**
759
765
  * Creates a new NeuroLink instance for AI text generation with MCP tool integration.
760
766
  *
@@ -3142,6 +3148,18 @@ Current user's request: ${currentInput}`;
3142
3148
  * @since 1.0.0
3143
3149
  */
3144
3150
  async generate(optionsOrPrompt) {
3151
+ // Host-loop delegation (registerAgentTool): enter a per-turn scope so
3152
+ // delegation caps count against THIS top-level generate, and withhold
3153
+ // depth-limited agent tools from the request. beginDelegationTurn
3154
+ // returns null when a scope is already active, so the re-entrant call
3155
+ // below proceeds through the normal body sharing the turn's counters.
3156
+ if (this.hasAgentTools) {
3157
+ const { beginDelegationTurn } = await import("./agent/agentToolRegistrar.js");
3158
+ const scope = beginDelegationTurn(this, optionsOrPrompt);
3159
+ if (scope) {
3160
+ return scope.run(() => this.generate(scope.options));
3161
+ }
3162
+ }
3145
3163
  // Defensive call-isolation clone — mirrors stream(): downstream
3146
3164
  // generate-prep (memory retrieval, orchestration, RAG/MCP tool
3147
3165
  // injection) mutates nested branches on the caller-supplied options
@@ -4112,7 +4130,7 @@ Current user's request: ${currentInput}`;
4112
4130
  : undefined,
4113
4131
  responseTime: textResult.responseTime,
4114
4132
  toolsUsed: textResult.toolsUsed,
4115
- toolExecutions: transformToolExecutions(textResult.toolExecutions),
4133
+ toolExecutions: toToolExecutionRecords(textResult.toolExecutions),
4116
4134
  enhancedWithTools: textResult.enhancedWithTools,
4117
4135
  availableTools: transformAvailableTools(textResult.availableTools),
4118
4136
  analytics: textResult.analytics,
@@ -5720,21 +5738,16 @@ Current user's request: ${currentInput}`;
5720
5738
  rawFinishReason: poolResult.rawFinishReason,
5721
5739
  stepsUsed: poolResult.stepsUsed,
5722
5740
  toolsUsed: poolResult.toolsUsed || [],
5723
- toolExecutions: poolResult.toolExecutions?.map((te) => {
5724
- const t = te;
5725
- return {
5726
- ...te,
5727
- toolName: te.name,
5728
- executionTime: typeof t.executionTime === "number"
5729
- ? t.executionTime
5730
- : typeof t.duration === "number"
5731
- ? t.duration
5732
- : 0,
5733
- success: typeof t.success === "boolean"
5734
- ? t.success
5735
- : t.status === "success",
5736
- };
5737
- }),
5741
+ // Lossless pass-through: keep the full ToolExecutionRecord
5742
+ // fields (params/resultText/isError/timing) alongside the
5743
+ // legacy {toolName,executionTime,success} shape this internal
5744
+ // result declares, so the final GenerateResult mapping
5745
+ // reconstructs real records instead of empty ones.
5746
+ toolExecutions: poolResult.toolExecutions?.map((te) => ({
5747
+ ...te,
5748
+ executionTime: te.durationMs,
5749
+ success: !te.isError,
5750
+ })),
5738
5751
  enhancedWithTools: !!poolResult.toolExecutions?.length,
5739
5752
  analytics: poolResult.analytics,
5740
5753
  evaluation: poolResult.evaluation,
@@ -6008,25 +6021,15 @@ Current user's request: ${currentInput}`;
6008
6021
  rawFinishReason: result.rawFinishReason,
6009
6022
  stepsUsed: result.stepsUsed,
6010
6023
  toolsUsed: result.toolsUsed || [],
6011
- // Map toolExecutions from EnhancedGenerateResult shape ({name,input,output})
6012
- // to TextGenerationResult shape ({toolName,executionTime,success}).
6013
- // Preserve original timing/status when present, fall back to safe defaults.
6014
- toolExecutions: result.toolExecutions?.map((te) => {
6015
- const t = te;
6016
- return {
6017
- // Spread original fields first so normalized fields take precedence
6018
- ...te,
6019
- toolName: te.name,
6020
- executionTime: typeof t.executionTime === "number"
6021
- ? t.executionTime
6022
- : typeof t.duration === "number"
6023
- ? t.duration
6024
- : 0,
6025
- success: typeof t.success === "boolean"
6026
- ? t.success
6027
- : t.status === "success",
6028
- };
6029
- }),
6024
+ // Lossless pass-through: keep the full ToolExecutionRecord fields
6025
+ // alongside the legacy {toolName,executionTime,success} shape this
6026
+ // internal result declares, so the final GenerateResult mapping
6027
+ // reconstructs real records instead of empty ones.
6028
+ toolExecutions: result.toolExecutions?.map((te) => ({
6029
+ ...te,
6030
+ executionTime: te.durationMs,
6031
+ success: !te.isError,
6032
+ })),
6030
6033
  enhancedWithTools: !!result.toolExecutions?.length,
6031
6034
  analytics: result.analytics,
6032
6035
  evaluation: result.evaluation,
@@ -6277,6 +6280,19 @@ Current user's request: ${currentInput}`;
6277
6280
  * @throws {Error} When conversation memory operations fail (if enabled)
6278
6281
  */
6279
6282
  async stream(options) {
6283
+ // Host-loop delegation (registerAgentTool): the same per-turn scope as
6284
+ // generate() — delegation caps count against THIS streamed turn and
6285
+ // depth-limited agent tools are withheld from the request. The provider
6286
+ // stream loops start inside this call, so the ALS scope propagates into
6287
+ // their tool executions. beginDelegationTurn returns null when a scope
6288
+ // is already active (the re-entrant call below shares the counters).
6289
+ if (this.hasAgentTools) {
6290
+ const { beginDelegationTurn } = await import("./agent/agentToolRegistrar.js");
6291
+ const scope = beginDelegationTurn(this, options);
6292
+ if (scope) {
6293
+ return scope.run(() => this.stream(scope.options));
6294
+ }
6295
+ }
6280
6296
  logger.debug("[NeuroLink] stream() called with options", {
6281
6297
  provider: options.provider,
6282
6298
  model: options.model,
@@ -12162,6 +12178,150 @@ Current user's request: ${currentInput}`;
12162
12178
  });
12163
12179
  return new AgentNetwork(config, this);
12164
12180
  }
12181
+ /**
12182
+ * Create a worker-mode NeuroLink instance for sub-agent execution.
12183
+ *
12184
+ * Worker mode is the framework-provided version of the config block every
12185
+ * consumer used to copy by hand: conversation memory OFF, orchestration
12186
+ * OFF, observability inherited from this instance with
12187
+ * `autoDetectExternalProvider: true` + `skipLangfuseSpanProcessor: true`
12188
+ * (worker spans join the host's tracer without duplicate Langfuse
12189
+ * exports), credentials inherited, the host's tool registry shared (so
12190
+ * worker tool calls reuse the host's connections), and an internal log
12191
+ * bridge attached with a caller-supplied tag.
12192
+ *
12193
+ * Dispose the worker (`worker.dispose()`) when done — `runIsolatedAgent`
12194
+ * does this automatically in a `finally`.
12195
+ *
12196
+ * @param options - Worker options (log tag/sink, registry sharing, config)
12197
+ * @returns A new worker-mode NeuroLink instance
12198
+ * @see {@link WorkerInstanceOptions}
12199
+ */
12200
+ createWorkerInstance(options) {
12201
+ const tag = options?.logTag ?? "worker";
12202
+ const hostEmitter = this.emitter;
12203
+ const configOverrides = (options?.config ?? {});
12204
+ const workerConfig = {
12205
+ ...(this.credentials ? { credentials: this.credentials } : {}),
12206
+ ...configOverrides,
12207
+ // Worker-mode fields always win over the config merge.
12208
+ conversationMemory: { enabled: false },
12209
+ enableOrchestration: false,
12210
+ observability: {
12211
+ ...(this.observabilityConfig ?? {}),
12212
+ langfuse: {
12213
+ ...(this.observabilityConfig?.langfuse ?? {}),
12214
+ autoDetectExternalProvider: true,
12215
+ skipLangfuseSpanProcessor: true,
12216
+ },
12217
+ },
12218
+ ...(options?.shareToolRegistry !== false && {
12219
+ toolRegistry: this.toolRegistry,
12220
+ }),
12221
+ };
12222
+ const worker = new NeuroLink(workerConfig);
12223
+ // Constructing an instance rebinds the process-global logger sink to the
12224
+ // new instance's emitter — restore the host as the active sink so host
12225
+ // log bridges keep flowing while workers come and go.
12226
+ logger.setEventEmitter(hostEmitter);
12227
+ if (options?.onLog) {
12228
+ const onLog = options.onLog;
12229
+ const forward = (raw) => {
12230
+ try {
12231
+ const entry = (raw ?? {});
12232
+ onLog({
12233
+ tag,
12234
+ level: String(entry.level ?? "info"),
12235
+ message: String(entry.message ?? ""),
12236
+ timestamp: typeof entry.timestamp === "number"
12237
+ ? entry.timestamp
12238
+ : Date.now(),
12239
+ data: entry.data,
12240
+ });
12241
+ }
12242
+ catch {
12243
+ // Log-bridge listener errors never disrupt the worker.
12244
+ }
12245
+ };
12246
+ hostEmitter.on("log-event", forward);
12247
+ const originalDispose = worker.dispose.bind(worker);
12248
+ worker.dispose = async () => {
12249
+ hostEmitter.off("log-event", forward);
12250
+ await originalDispose();
12251
+ };
12252
+ }
12253
+ logger.debug("[NeuroLink] Created worker instance", {
12254
+ tag,
12255
+ sharedToolRegistry: options?.shareToolRegistry !== false,
12256
+ });
12257
+ return worker;
12258
+ }
12259
+ /**
12260
+ * Run an isolated sub-agent: a worker instance (see
12261
+ * {@link createWorkerInstance}) executes a tool-using research pass under
12262
+ * the turn budget (wrap-up nudge, stall watchdog, honest `stopReason`),
12263
+ * then an extraction pass ALWAYS runs tools-off on its own timeout with a
12264
+ * structured-recovery ladder and corrective re-asks. A non-empty execution
12265
+ * record never produces an empty result (mechanical digest fallback), a
12266
+ * parent `abortSignal` stops everything cleanly, and `options.leg` enables
12267
+ * leashed mode with TTL'd resume handles ({@link continueAgent} /
12268
+ * {@link stopAgent}).
12269
+ *
12270
+ * @param definition - Agent definition (+ optional structured extraction)
12271
+ * @param input - Task input: string or structured object
12272
+ * @param options - Run options (abort, overrides, tool context, events, leg)
12273
+ * @returns The run outcome
12274
+ * @see {@link IsolatedAgentDefinition}
12275
+ * @see {@link AgentRunOptions}
12276
+ * @see {@link AgentRunOutcome}
12277
+ */
12278
+ async runIsolatedAgent(definition, input, options) {
12279
+ const { runIsolatedAgent } = await import("./agent/isolatedAgentRunner.js");
12280
+ return runIsolatedAgent(this, definition, input, options);
12281
+ }
12282
+ /**
12283
+ * Resume a leashed isolated-agent run by handle. `guidance`, when given,
12284
+ * is appended as a user turn before the next leg — the supervisor's
12285
+ * re-steering channel. An expired handle returns its tombstoned final
12286
+ * outcome exactly once.
12287
+ *
12288
+ * @param handle - Handle from an `in_progress` {@link AgentRunOutcome}
12289
+ * @param guidance - Optional supervisor guidance for the next leg
12290
+ * @returns The next leg's outcome (or the final outcome)
12291
+ */
12292
+ async continueAgent(handle, guidance) {
12293
+ const { continueIsolatedAgent } = await import("./agent/isolatedAgentRunner.js");
12294
+ return continueIsolatedAgent(this, handle, guidance);
12295
+ }
12296
+ /**
12297
+ * Stop a leashed isolated-agent run: dispose its worker and return the
12298
+ * final outcome (mechanical digest over everything gathered so far).
12299
+ *
12300
+ * @param handle - Handle from an `in_progress` {@link AgentRunOutcome}
12301
+ * @returns The final outcome
12302
+ */
12303
+ async stopAgent(handle) {
12304
+ const { stopIsolatedAgent } = await import("./agent/isolatedAgentRunner.js");
12305
+ return stopIsolatedAgent(this, handle);
12306
+ }
12307
+ /**
12308
+ * Register an isolated agent as a delegation tool on THIS instance, so
12309
+ * its existing generate() loop can delegate — no second router generate.
12310
+ * Framework policy (per-turn caps, depth withholding, a process-wide
12311
+ * concurrency pool with queue timeout) is enforced in the loop itself,
12312
+ * and every refusal carries its recovery instruction in the error text.
12313
+ *
12314
+ * @param definition - Agent definition (+ optional structured extraction)
12315
+ * @param options - Registration options (name, caps, depth, pool, leg)
12316
+ * @returns The registered tool name
12317
+ * @see {@link AgentToolRegistrationOptions}
12318
+ */
12319
+ async registerAgentTool(definition, options) {
12320
+ const { registerAgentTool } = await import("./agent/agentToolRegistrar.js");
12321
+ const registered = registerAgentTool(this, definition, options);
12322
+ this.hasAgentTools = true;
12323
+ return registered;
12324
+ }
12165
12325
  /**
12166
12326
  * Execute an agent network with the given input.
12167
12327
  *
@@ -12293,7 +12453,9 @@ Current user's request: ${currentInput}`;
12293
12453
  try {
12294
12454
  logger.debug("[NeuroLink] Removing all event listeners...");
12295
12455
  this.emitter.removeAllListeners();
12296
- logger.clearEventEmitter();
12456
+ // Clear only if this instance's emitter is the active log sink —
12457
+ // disposing a worker instance must not yank the host's bridge.
12458
+ logger.clearEventEmitter(this.emitter);
12297
12459
  logger.debug("[NeuroLink] Event listeners removed successfully");
12298
12460
  }
12299
12461
  catch (error) {
@@ -10,6 +10,7 @@ import { AuthenticationError, ProviderError, RateLimitError, } from "../types/in
10
10
  import { isAbortError, withTimeout } from "../utils/errorHandling.js";
11
11
  import { emitToolEndFromStepFinish } from "../utils/toolEndEmitter.js";
12
12
  import { logger } from "../utils/logger.js";
13
+ import { resolveSamplingParams } from "../models/modelRegistry.js";
13
14
  import { calculateCost } from "../utils/pricing.js";
14
15
  import { buildMultimodalMessagesArray } from "../utils/messageBuilder.js";
15
16
  import { buildMultimodalOptions } from "../utils/multimodalOptionsBuilder.js";
@@ -322,6 +323,10 @@ export class AmazonBedrockProvider extends BaseProvider {
322
323
  const aiTools = await this.getAllTools();
323
324
  const allTools = this.convertAISDKToolsToToolDefinitions(aiTools);
324
325
  const toolConfig = this.formatToolsForBedrock(allTools);
326
+ // Registry-driven strip: models that reject sampling params
327
+ // (Sonnet 5 / Opus 4.7+ / Fable 5 Claude families on Bedrock)
328
+ // must not receive the legacy 0.7 default temperature.
329
+ const generateSampling = resolveSamplingParams(this.providerName, this.modelName || this.getDefaultModel(), { temperature: options.temperature ?? 0.7 }, "bedrock.converse");
325
330
  const commandInput = {
326
331
  modelId: this.modelName || this.getDefaultModel(),
327
332
  messages: this.convertToAWSMessages(this.conversationHistory),
@@ -333,7 +338,9 @@ export class AmazonBedrockProvider extends BaseProvider {
333
338
  ],
334
339
  inferenceConfig: {
335
340
  maxTokens: options.maxTokens, // No default limit - unlimited unless specified
336
- temperature: options.temperature || 0.7,
341
+ ...(generateSampling.temperature !== undefined && {
342
+ temperature: generateSampling.temperature,
343
+ }),
337
344
  },
338
345
  };
339
346
  if (toolConfig) {
@@ -1324,6 +1331,10 @@ export class AmazonBedrockProvider extends BaseProvider {
1324
1331
  logger.debug(`[AmazonBedrockProvider] Converted Message ${index}: role=${msg.role}, content=${JSON.stringify(msg.content)}`);
1325
1332
  });
1326
1333
  }
1334
+ // Registry-driven strip: models that reject sampling params (Sonnet 5 /
1335
+ // Opus 4.7+ / Fable 5 Claude families on Bedrock) must not receive the
1336
+ // legacy 0.7 default temperature.
1337
+ const streamSampling = resolveSamplingParams(this.providerName, this.modelName || this.getDefaultModel(), { temperature: options.temperature ?? 0.7 }, "bedrock.converseStream");
1327
1338
  const commandInput = {
1328
1339
  modelId: this.modelName || this.getDefaultModel(),
1329
1340
  messages: convertedMessages,
@@ -1335,7 +1346,9 @@ export class AmazonBedrockProvider extends BaseProvider {
1335
1346
  ],
1336
1347
  inferenceConfig: {
1337
1348
  maxTokens: options.maxTokens, // No default limit - unlimited unless specified
1338
- temperature: options.temperature || 0.7,
1349
+ ...(streamSampling.temperature !== undefined && {
1350
+ temperature: streamSampling.temperature,
1351
+ }),
1339
1352
  },
1340
1353
  };
1341
1354
  if (toolConfig) {
@@ -25,7 +25,7 @@ import { buildNoOutputSentinel, stampNoOutputSpan, } from "../utils/noOutputSent
25
25
  import { convertZodToJsonSchema } from "../utils/schemaConversion.js";
26
26
  import { resolveClaudeMaxTokens } from "../utils/tokenLimits.js";
27
27
  import { toAnthropicImageBlock, fileToAnthropicBlock, } from "./anthropicImageBlocks.js";
28
- import { modelDeprecatesTemperature } from "../core/modules/structuredOutputPolicy.js";
28
+ import { resolveSamplingParams } from "../models/modelRegistry.js";
29
29
  import { createChunkQueue, createDeferredAnalytics, stringifyToolInput, } from "./openaiChatCompletionsClient.js";
30
30
  /**
31
31
  * Beta headers for Claude Code integration.
@@ -1139,18 +1139,28 @@ export class AnthropicProvider extends BaseProvider {
1139
1139
  messages: messages,
1140
1140
  });
1141
1141
  const cachedMessages = applyAnthropicHistoryCacheBreakpoints(messages, ANTHROPIC_MAX_CACHE_BREAKPOINTS - cacheMarkersUsed);
1142
+ // Registry-driven strip: Sonnet 5 / Opus 4.7+ / Fable 5 families
1143
+ // reject sampling params (also covers the retry paths — this params
1144
+ // object is what any rebuild spreads).
1145
+ const samplingParams = resolveSamplingParams("anthropic", modelId, {
1146
+ ...(options.temperature !== undefined &&
1147
+ options.temperature !== null
1148
+ ? { temperature: options.temperature }
1149
+ : {}),
1150
+ ...(options.topP !== undefined && options.topP !== null
1151
+ ? { topP: options.topP }
1152
+ : {}),
1153
+ }, "anthropic.doGenerate");
1142
1154
  const params = {
1143
1155
  model: modelId,
1144
1156
  messages: cachedMessages,
1145
1157
  max_tokens: resolveClaudeMaxTokens(modelId, options.maxOutputTokens),
1146
1158
  ...(system ? { system } : {}),
1147
- ...(options.temperature !== undefined &&
1148
- options.temperature !== null &&
1149
- !modelDeprecatesTemperature(modelId)
1150
- ? { temperature: options.temperature }
1159
+ ...(samplingParams.temperature !== undefined
1160
+ ? { temperature: samplingParams.temperature }
1151
1161
  : {}),
1152
- ...(options.topP !== undefined && options.topP !== null
1153
- ? { top_p: options.topP }
1162
+ ...(samplingParams.topP !== undefined
1163
+ ? { top_p: samplingParams.topP }
1154
1164
  : {}),
1155
1165
  ...(options.stopSequences && options.stopSequences.length > 0
1156
1166
  ? { stop_sequences: options.stopSequences }
@@ -1426,16 +1436,18 @@ export class AnthropicProvider extends BaseProvider {
1426
1436
  messages: conversation,
1427
1437
  });
1428
1438
  const cachedConversation = applyAnthropicHistoryCacheBreakpoints(conversation, ANTHROPIC_MAX_CACHE_BREAKPOINTS - cacheMarkersUsed);
1439
+ // Registry-driven strip (Sonnet 5 / Opus 4.7+ / Fable 5 families)
1440
+ const streamSamplingParams = resolveSamplingParams("anthropic", modelId, options.temperature !== undefined && options.temperature !== null
1441
+ ? { temperature: options.temperature }
1442
+ : {}, "anthropic.executeStream");
1429
1443
  const params = {
1430
1444
  model: modelId,
1431
1445
  messages: cachedConversation,
1432
1446
  max_tokens: resolveClaudeMaxTokens(modelId, options.maxTokens),
1433
1447
  stream: true,
1434
1448
  ...(payload.system ? { system: payload.system } : {}),
1435
- ...(options.temperature !== undefined &&
1436
- options.temperature !== null &&
1437
- !modelDeprecatesTemperature(modelId)
1438
- ? { temperature: options.temperature }
1449
+ ...(streamSamplingParams.temperature !== undefined
1450
+ ? { temperature: streamSamplingParams.temperature }
1439
1451
  : {}),
1440
1452
  ...(anthropicTools && anthropicTools.length > 0
1441
1453
  ? { tools: anthropicTools }