@juspay/neurolink 12.47.14 → 12.48.1

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 +2 -2
  2. package/dist/auth/middleware/AuthMiddleware.d.ts +1 -1
  3. package/dist/browser/neurolink.min.js +578 -565
  4. package/dist/cli/factories/commandFactory.d.ts +7 -0
  5. package/dist/cli/factories/commandFactory.js +26 -0
  6. package/dist/cli/loop/optionsSchema.js +9 -0
  7. package/dist/context/budgetChecker.js +54 -2
  8. package/dist/context/contextDecision.js +2 -0
  9. package/dist/context/toolPairRepair.d.ts +12 -0
  10. package/dist/context/toolPairRepair.js +29 -3
  11. package/dist/core/baseProvider.d.ts +92 -3
  12. package/dist/core/baseProvider.js +254 -10
  13. package/dist/core/conversationMemoryManager.js +17 -2
  14. package/dist/core/loopEngine.js +1 -1
  15. package/dist/core/modules/MessageBuilder.js +11 -3
  16. package/dist/core/modules/TelemetryHandler.d.ts +2 -2
  17. package/dist/core/modules/ToolsManager.d.ts +35 -0
  18. package/dist/core/modules/ToolsManager.js +159 -55
  19. package/dist/core/nativeGenerateLoop.d.ts +58 -1
  20. package/dist/core/nativeGenerateLoop.js +277 -3
  21. package/dist/core/redisConversationMemoryManager.js +10 -3
  22. package/dist/core/resolveRequestKind.d.ts +24 -4
  23. package/dist/core/resolveRequestKind.js +31 -4
  24. package/dist/core/toolExecutionRecorder.d.ts +6 -0
  25. package/dist/core/toolExecutionRecorder.js +24 -2
  26. package/dist/core/toolRoutingDecision.js +1 -0
  27. package/dist/factories/mediaHandlerCatalog.js +5 -0
  28. package/dist/factories/providerDescriptors.js +15 -0
  29. package/dist/files/fileTools.d.ts +1 -1
  30. package/dist/index.d.ts +5 -2
  31. package/dist/index.js +9 -2
  32. package/dist/memory/condensationPrompt.d.ts +8 -0
  33. package/dist/memory/condensationPrompt.js +29 -0
  34. package/dist/memory/hippocampusInitializer.d.ts +14 -2
  35. package/dist/memory/hippocampusInitializer.js +64 -3
  36. package/dist/neurolink.d.ts +252 -3
  37. package/dist/neurolink.js +898 -43
  38. package/dist/providers/amazonBedrock/client.js +6 -0
  39. package/dist/providers/amazonSagemaker.js +14 -1
  40. package/dist/providers/anthropic/client.js +148 -19
  41. package/dist/providers/anthropic/loopAdapter.js +2 -2
  42. package/dist/providers/googleAiStudio/client.js +2 -1
  43. package/dist/providers/openaiChatCompletionsBase.js +103 -23
  44. package/dist/providers/systemOneDecision.d.ts +6 -0
  45. package/dist/providers/systemOneDecision.js +14 -63
  46. package/dist/providers/typesafe.d.ts +6 -0
  47. package/dist/providers/typesafe.js +6 -0
  48. package/dist/rag/retrieval/searchDecision.js +1 -0
  49. package/dist/routing/classifierRouter.js +6 -5
  50. package/dist/routing/classifierStrategies.js +8 -1
  51. package/dist/types/classifierRouter.d.ts +16 -3
  52. package/dist/types/common.d.ts +2 -0
  53. package/dist/types/config.d.ts +9 -0
  54. package/dist/types/context.d.ts +8 -1
  55. package/dist/types/conversation.d.ts +87 -0
  56. package/dist/types/decision.d.ts +210 -1
  57. package/dist/types/decision.js +9 -1
  58. package/dist/types/dispatch.d.ts +11 -4
  59. package/dist/types/generate.d.ts +118 -54
  60. package/dist/types/loopEngine.d.ts +7 -2
  61. package/dist/types/memory.d.ts +177 -9
  62. package/dist/types/openaiCompatible.d.ts +11 -3
  63. package/dist/types/stream.d.ts +13 -16
  64. package/dist/types/stt.d.ts +58 -1
  65. package/dist/types/tools.d.ts +14 -0
  66. package/dist/types/tools.js +8 -0
  67. package/dist/types/tts.d.ts +108 -0
  68. package/dist/types/voice.d.ts +41 -4
  69. package/dist/utils/decisionAnswers.d.ts +31 -1
  70. package/dist/utils/decisionAnswers.js +114 -0
  71. package/dist/utils/decisionLimits.d.ts +39 -0
  72. package/dist/utils/decisionLimits.js +116 -0
  73. package/dist/utils/logger.d.ts +7 -0
  74. package/dist/utils/logger.js +9 -0
  75. package/dist/utils/messageBuilder.d.ts +45 -1
  76. package/dist/utils/messageBuilder.js +393 -16
  77. package/dist/utils/multimodalOptionsBuilder.d.ts +1 -0
  78. package/dist/utils/multimodalOptionsBuilder.js +1 -0
  79. package/dist/utils/speechText.d.ts +41 -0
  80. package/dist/utils/speechText.js +364 -0
  81. package/dist/utils/toolResultStatus.d.ts +26 -0
  82. package/dist/utils/toolResultStatus.js +73 -0
  83. package/dist/utils/ttsProcessor.d.ts +29 -0
  84. package/dist/utils/ttsProcessor.js +84 -2
  85. package/dist/voice/index.d.ts +1 -0
  86. package/dist/voice/index.js +3 -0
  87. package/dist/voice/providers/ElevenLabsSTT.d.ts +42 -0
  88. package/dist/voice/providers/ElevenLabsSTT.js +351 -0
  89. package/dist/voice/providers/ElevenLabsTTS.d.ts +81 -1
  90. package/dist/voice/providers/ElevenLabsTTS.js +415 -74
  91. package/docs-site/static/search-index.json +48 -33
  92. package/package.json +5 -2
  93. package/dist/utils/toolEndEmitter.d.ts +0 -25
  94. package/dist/utils/toolEndEmitter.js +0 -64
@@ -19,6 +19,13 @@ export declare class CLICommandFactory {
19
19
  * generate / batch / stream paths.
20
20
  */
21
21
  private static normalizeLoopSessionVariables;
22
+ /**
23
+ * The loop's `set toolChoiceSteps` / `set replayToolSteps` values in the
24
+ * shape `generate()` / `stream()` take. Session variables arrive as raw
25
+ * strings or numbers, so each is validated rather than cast: a bad value
26
+ * is dropped here and the SDK default applies.
27
+ */
28
+ private static loopToolStepOptions;
22
29
  static readonly commonOptions: {
23
30
  provider: {
24
31
  choices: string[];
@@ -97,6 +97,28 @@ export class CLICommandFactory {
97
97
  }
98
98
  return normalized;
99
99
  }
100
+ /**
101
+ * The loop's `set toolChoiceSteps` / `set replayToolSteps` values in the
102
+ * shape `generate()` / `stream()` take. Session variables arrive as raw
103
+ * strings or numbers, so each is validated rather than cast: a bad value
104
+ * is dropped here and the SDK default applies.
105
+ */
106
+ static loopToolStepOptions(vars) {
107
+ const out = {};
108
+ const steps = typeof vars.toolChoiceSteps === "string"
109
+ ? vars.toolChoiceSteps.trim() === ""
110
+ ? undefined
111
+ : Number(vars.toolChoiceSteps)
112
+ : vars.toolChoiceSteps;
113
+ if (typeof steps === "number" && Number.isInteger(steps) && steps >= 0) {
114
+ out.toolChoiceSteps = steps;
115
+ }
116
+ const replay = vars.replayToolSteps;
117
+ if (replay === "full" || replay === "marker" || replay === "off") {
118
+ out.replayToolSteps = replay;
119
+ }
120
+ return out;
121
+ }
100
122
  // Common options available on all commands
101
123
  static commonOptions = {
102
124
  // Core generation options
@@ -2753,6 +2775,9 @@ export class CLICommandFactory {
2753
2775
  : undefined,
2754
2776
  disableTools: enhancedOptions.disableTools,
2755
2777
  enabledToolNames: enhancedOptions.enabledToolNames,
2778
+ // Loop `set` variables the schema accepts; without an explicit
2779
+ // entry here they were echoed as set and then dropped.
2780
+ ...CLICommandFactory.loopToolStepOptions(enhancedOptions),
2756
2781
  enableAnalytics: enhancedOptions.enableAnalytics,
2757
2782
  enableEvaluation: enhancedOptions.enableEvaluation,
2758
2783
  evaluationDomain: enhancedOptions.evaluationDomain,
@@ -3005,6 +3030,7 @@ export class CLICommandFactory {
3005
3030
  : undefined,
3006
3031
  disableTools: enhancedOptions.disableTools,
3007
3032
  enabledToolNames: enhancedOptions.enabledToolNames,
3033
+ ...CLICommandFactory.loopToolStepOptions(enhancedOptions),
3008
3034
  enableAnalytics: enhancedOptions.enableAnalytics,
3009
3035
  enableEvaluation: enhancedOptions.enableEvaluation,
3010
3036
  evaluationDomain: enhancedOptions.evaluationDomain,
@@ -81,6 +81,15 @@ export const textGenerationOptionsSchema = {
81
81
  type: "number",
82
82
  description: "Maximum number of tool execution steps.",
83
83
  },
84
+ toolChoiceSteps: {
85
+ type: "number",
86
+ description: "Leading steps a forced toolChoice (required / named tool) stays in force before the model chooses (default 1).",
87
+ },
88
+ replayToolSteps: {
89
+ type: "string",
90
+ description: "How stored tool steps are replayed into the prompt: full, marker (default) or off.",
91
+ allowedValues: ["full", "marker", "off"],
92
+ },
84
93
  enableAnalytics: {
85
94
  type: "boolean",
86
95
  description: "Enable or disable analytics for responses.",
@@ -20,6 +20,58 @@ export const DEFAULT_COMPACTION_THRESHOLD = 0.8;
20
20
  const HISTORY_BUDGET_SAFETY_FACTOR = 0.95;
21
21
  /** Estimated tokens per tool definition */
22
22
  const TOKENS_PER_TOOL_DEFINITION = 200;
23
+ /**
24
+ * Tokens one `[called <tool> → ok]` marker line costs. The tool name is the
25
+ * only variable part and is short; a fixed figure keeps the estimate cheap.
26
+ */
27
+ const TOKENS_PER_TOOL_MARKER = 12;
28
+ /**
29
+ * Estimate the history at the size it will REACH THE MODEL.
30
+ *
31
+ * Tool rows are stored at full size (arguments plus the whole output), but
32
+ * the prompt carries them per `replayToolSteps`: `"marker"` — the default —
33
+ * sends one short line per call and nothing for the result, `"off"` sends
34
+ * nothing, `"full"` sends the stored payload. Counting them at stored size
35
+ * under `"marker"` made the budget check and the summarization trigger see
36
+ * a history several times larger than the prompt, so summarization fired
37
+ * turns before the real prompt was anywhere near the window.
38
+ *
39
+ * Without a mode the rows count at stored size, which is also right for the
40
+ * providers that replay tool rows natively in their own wire shape
41
+ * regardless of the mode (see `replaysToolRowsNatively`).
42
+ */
43
+ function estimateHistoryTokens(messages, provider, mode) {
44
+ if (mode === undefined || mode === "full") {
45
+ return estimateMessagesTokens(messages, provider);
46
+ }
47
+ const regular = messages.filter((m) => m.role !== "tool_call" && m.role !== "tool_result");
48
+ let total = estimateMessagesTokens(regular, provider);
49
+ if (mode === "marker") {
50
+ // One marker per call; a result adds nothing beyond its call's marker.
51
+ // `result.error` rides along on the marker only as "→ error", so it is
52
+ // not counted either.
53
+ total +=
54
+ messages.filter((m) => m.role === "tool_call").length *
55
+ (TOKENS_PER_TOOL_MARKER + TOKENS_PER_MESSAGE);
56
+ }
57
+ return total;
58
+ }
59
+ /**
60
+ * Providers whose native history builders replay stored tool rows in their
61
+ * own wire shape whatever `replayToolSteps` says (Claude-on-Vertex, Gemini
62
+ * on Vertex and AI Studio). Their tool rows always reach the model at full
63
+ * size, so the mode must not shrink the estimate for them.
64
+ */
65
+ const NATIVE_TOOL_REPLAY_PROVIDERS = new Set([
66
+ "vertex",
67
+ "google-ai",
68
+ "google-vertex",
69
+ "googlevertex",
70
+ "google-ai-studio",
71
+ ]);
72
+ function replaysToolRowsNatively(provider) {
73
+ return NATIVE_TOOL_REPLAY_PROVIDERS.has(provider.toLowerCase());
74
+ }
23
75
  /**
24
76
  * Tokens the CONVERSATION HISTORY may occupy, i.e. the model's available input
25
77
  * space minus everything that rides alongside it (system prompt, current
@@ -71,14 +123,14 @@ export function checkContextBudget(params) {
71
123
  }, parentSpanId, traceId);
72
124
  const startTime = Date.now();
73
125
  try {
74
- const { provider, model, maxTokens, systemPrompt, conversationMessages, currentPrompt, toolDefinitions, fileAttachments, compactionThreshold = DEFAULT_COMPACTION_THRESHOLD, } = params;
126
+ const { provider, model, maxTokens, systemPrompt, conversationMessages, currentPrompt, toolDefinitions, fileAttachments, compactionThreshold = DEFAULT_COMPACTION_THRESHOLD, toolReplayMode, } = params;
75
127
  const availableInputTokens = getAvailableInputTokens(provider, model, maxTokens);
76
128
  // Estimate each category
77
129
  const systemPromptTokens = systemPrompt
78
130
  ? estimateTokens(systemPrompt, provider) + TOKENS_PER_MESSAGE
79
131
  : 0;
80
132
  const conversationHistoryTokens = conversationMessages?.length
81
- ? estimateMessagesTokens(conversationMessages, provider)
133
+ ? estimateHistoryTokens(conversationMessages, provider, replaysToolRowsNatively(provider) ? undefined : toolReplayMode)
82
134
  : 0;
83
135
  const currentPromptTokens = currentPrompt
84
136
  ? estimateTokens(currentPrompt, provider) + TOKENS_PER_MESSAGE
@@ -107,6 +107,7 @@ export async function selectIrrelevantMessages(messages, currentRequest, decide,
107
107
  };
108
108
  });
109
109
  const result = await decide({
110
+ site: "contextRelevance",
110
111
  state: {
111
112
  current_request: currentRequest.slice(0, 4000),
112
113
  conversation: eligible.map((entry, position) => ({
@@ -184,6 +185,7 @@ export async function summaryPreservesContext(summary, replacedMessages, decide,
184
185
  return true;
185
186
  }
186
187
  const result = await decide({
188
+ site: "summaryGate",
187
189
  state: {
188
190
  summary: summary.slice(0, 12000),
189
191
  original_messages: replacedMessages.slice(-60).map((message) => ({
@@ -19,6 +19,18 @@
19
19
  * positionally WITHIN the batch (call[i] ↔ result[i]).
20
20
  */
21
21
  import type { ChatMessage, RepairResult } from "../types/index.js";
22
+ /**
23
+ * True for a row this module invented to stand in for a dropped call or
24
+ * result. Replay must not present such a row as a real outcome: the model
25
+ * would be told a call succeeded ("→ ok") or given a fabricated input when
26
+ * the truth is that the record was lost. Recognised by id prefix, with the
27
+ * placeholder text as a fallback for rows that travelled without their id.
28
+ */
29
+ export declare function isRepairPlaceholder(row: {
30
+ id?: string;
31
+ role: string;
32
+ content?: unknown;
33
+ }): boolean;
22
34
  /**
23
35
  * Move a summarize/keep split so it never falls INSIDE a tool batch.
24
36
  *
@@ -20,6 +20,32 @@
20
20
  */
21
21
  import { randomUUID } from "crypto";
22
22
  const MISSING_RESULT_CONTENT = "[Tool result unavailable - conversation was compacted]";
23
+ const REPAIR_RESULT_ID_PREFIX = "repair-result-";
24
+ const REPAIR_CALL_ID_PREFIX = "repair-call-";
25
+ const MISSING_CALL_CONTENT_SUFFIX = " - conversation was compacted]";
26
+ /**
27
+ * True for a row this module invented to stand in for a dropped call or
28
+ * result. Replay must not present such a row as a real outcome: the model
29
+ * would be told a call succeeded ("→ ok") or given a fabricated input when
30
+ * the truth is that the record was lost. Recognised by id prefix, with the
31
+ * placeholder text as a fallback for rows that travelled without their id.
32
+ */
33
+ export function isRepairPlaceholder(row) {
34
+ if (typeof row.id === "string" &&
35
+ (row.id.startsWith(REPAIR_RESULT_ID_PREFIX) ||
36
+ row.id.startsWith(REPAIR_CALL_ID_PREFIX))) {
37
+ return true;
38
+ }
39
+ if (typeof row.content !== "string") {
40
+ return false;
41
+ }
42
+ if (row.role === "tool_result") {
43
+ return row.content === MISSING_RESULT_CONTENT;
44
+ }
45
+ return (row.role === "tool_call" &&
46
+ row.content.startsWith("[Tool call for ") &&
47
+ row.content.endsWith(MISSING_CALL_CONTENT_SUFFIX));
48
+ }
23
49
  /**
24
50
  * Collect the tool batch starting at `start` (which must index a tool-role
25
51
  * message). Consumes the maximal run of calls followed by the maximal run of
@@ -43,7 +69,7 @@ function collectBatch(messages, start) {
43
69
  /** Synthetic `tool_result` standing in for a call whose result was dropped. */
44
70
  function syntheticResult(call) {
45
71
  return {
46
- id: `repair-result-${randomUUID()}`,
72
+ id: `${REPAIR_RESULT_ID_PREFIX}${randomUUID()}`,
47
73
  role: "tool_result",
48
74
  content: MISSING_RESULT_CONTENT,
49
75
  tool: call.tool,
@@ -55,9 +81,9 @@ function syntheticResult(call) {
55
81
  /** Synthetic `tool_call` standing in for a result whose call was dropped. */
56
82
  function syntheticCall(result) {
57
83
  return {
58
- id: `repair-call-${randomUUID()}`,
84
+ id: `${REPAIR_CALL_ID_PREFIX}${randomUUID()}`,
59
85
  role: "tool_call",
60
- content: `[Tool call for ${result.tool || "unknown"} - conversation was compacted]`,
86
+ content: `[Tool call for ${result.tool || "unknown"}${MISSING_CALL_CONTENT_SUFFIX}`,
61
87
  tool: result.tool,
62
88
  ...(result.toolCallId ? { toolCallId: result.toolCallId } : {}),
63
89
  timestamp: result.timestamp,
@@ -1,7 +1,7 @@
1
1
  import type { AIProviderName } from "../constants/enums.js";
2
2
  import type { EvaluationData } from "../index.js";
3
3
  import type { NeuroLink } from "../neurolink.js";
4
- import type { FileToolRootPolicy, UnknownRecord, MiddlewareFactoryOptions, StreamOptions, StreamResult, AIProvider, AnalyticsData, EnhancedGenerateResult, TextGenerationOptions, TextGenerationResult, ValidationSchema, EmbedInput, DecisionRequest, DecisionResult } from "../types/index.js";
4
+ import type { FileToolRootPolicy, UnknownRecord, MiddlewareFactoryOptions, StreamOptions, StreamResult, AIProvider, AnalyticsData, EnhancedGenerateResult, TextGenerationOptions, TextGenerationResult, ValidationSchema, EmbedInput, DecisionRequest, DecisionResult, MemoryToolCallRecord, MemoryToolResultRecord, ToolExecutionSummaryInternal } from "../types/index.js";
5
5
  import { TelemetryHandler } from "./modules/TelemetryHandler.js";
6
6
  import type { LanguageModel, ModelMessage, RawUsageObject, Tool, ToolCallRepairFunction, ToolSet } from "../types/index.js";
7
7
  export declare abstract class BaseProvider implements AIProvider {
@@ -190,6 +190,21 @@ export declare abstract class BaseProvider implements AIProvider {
190
190
  * Build messages array for generation - delegated to MessageBuilder
191
191
  */
192
192
  private buildMessages;
193
+ /**
194
+ * Fold the instance's `conversationMemory.replayToolSteps` into the request
195
+ * when the caller set none, so every path into the message builder — text
196
+ * or multimodal, generate or stream — reads one resolved value. The
197
+ * builder itself defaults to `"marker"` when neither is set.
198
+ *
199
+ * Then degrade `"full"` to `"marker"` for a request that declares no tools:
200
+ * Anthropic rejects a prompt carrying tool_use / tool_result blocks without
201
+ * a `tools` field ("Requests which include tool_use or tool_result blocks
202
+ * must define tools"), so a tools-off call — `disableTools`, or a filter
203
+ * that removed every tool — in a session that already ran one would fail
204
+ * on every later turn. Runs after the provider has merged `options.tools`,
205
+ * which is where every native path calls the builder from.
206
+ */
207
+ protected applyToolReplayDefault(options: StreamOptions | TextGenerationOptions): void;
193
208
  /**
194
209
  * Build messages array for streaming operations - delegated to MessageBuilder
195
210
  * This is a protected helper method that providers can use to build messages
@@ -282,7 +297,60 @@ export declare abstract class BaseProvider implements AIProvider {
282
297
  */
283
298
  gen(optionsOrPrompt: TextGenerationOptions | string, analysisSchema?: ValidationSchema): Promise<EnhancedGenerateResult | null>;
284
299
  private runGenerateInActiveContext;
300
+ /**
301
+ * Mode 1 — synthesize the input text itself, with no LLM call.
302
+ *
303
+ * This is the documented default for `generate({ tts: { enabled: true } })`
304
+ * and what every "speak this text" caller relies on, so a request that
305
+ * looks like it wanted the model's reply (tools, a system prompt, message
306
+ * history, a schema, conversation memory) is warned about, never rejected:
307
+ * those options have no effect here, and the fix is `tts.mode: "response"`.
308
+ */
285
309
  protected handleDirectTTSSynthesis(options: TextGenerationOptions, startTime: number): Promise<EnhancedGenerateResult>;
310
+ /**
311
+ * One warning when a direct-synthesis request carries options that only a
312
+ * model call would honour and the caller never chose a mode. Silence is
313
+ * the wrong answer here — the caller gets `usage` of zero and `content`
314
+ * echoing their prompt, and would otherwise have to guess why.
315
+ *
316
+ * Only options the CALLER supplied count. `conversationMemoryConfig` is
317
+ * attached by NeuroLink to every request on a memory-enabled instance, and
318
+ * on such an instance `conversationMessages` is the stored history
319
+ * NeuroLink injected itself — neither is something the caller set, and a
320
+ * voice bot with memory used to see this warning on every turn after the
321
+ * first for options it never passed.
322
+ */
323
+ private warnIfDirectTTSLooksLikeResponseRequest;
324
+ /**
325
+ * Outer bound on one synthesis call: the provider's request timeout, raised
326
+ * to whatever the handler itself may legitimately spend — `timeoutMs × (1 +
327
+ * retries)` plus retry pauses, with the handler defaults (30 s, one retry)
328
+ * standing in for a knob the caller left unset. Without the raise, a
329
+ * handler's own retry loop could be cut off from outside one attempt in:
330
+ * with the 60 s provider default, a first attempt that hung to its 30 s
331
+ * abort left no room for the documented default retry.
332
+ */
333
+ private resolveTTSSynthesisTimeout;
334
+ /**
335
+ * Apply `tts.sanitize` (off by default — existing callers see byte-identical
336
+ * input). Always runs when configured, regardless of the raw text's
337
+ * length against the provider's cap: sanitizing can shrink text a great
338
+ * deal (the default `codeBlocks: "phrase"` replaces a whole fenced block
339
+ * with one short phrase, `urls: "hostname"` shortens every link), so a
340
+ * reply that is over the cap raw can still fit after sanitizing — a coding
341
+ * answer with a large code block is exactly the reply this option is for.
342
+ * A reply still over the cap afterwards is rejected downstream by
343
+ * `TTSProcessor.synthesize` as `TTS_TEXT_TOO_LONG`, same as today.
344
+ */
345
+ private prepareTTSInput;
346
+ /**
347
+ * One synthesis call under the outer budget. The handler receives a signal
348
+ * that fires when that budget lapses or the caller's own `abortSignal`
349
+ * does, so a handler with a retry loop (ElevenLabs) stops rather than
350
+ * pausing and issuing a further billable request after the caller has
351
+ * already been told the synthesis failed.
352
+ */
353
+ private runTTSSynthesis;
286
354
  private handleVideoFrameGeneration;
287
355
  /**
288
356
  * Close out a turn produced by a provider's own native generate loop.
@@ -298,7 +366,23 @@ export declare abstract class BaseProvider implements AIProvider {
298
366
  * synthesis so telemetry sees the model's own usage, and `enhanceResult`
299
367
  * runs last so analytics and evaluation observe the final content.
300
368
  */
301
- protected finalizeNativeGenerate(result: EnhancedGenerateResult, options: TextGenerationOptions, startTime: number): Promise<EnhancedGenerateResult>;
369
+ protected finalizeNativeGenerate(result: EnhancedGenerateResult, options: TextGenerationOptions, startTime: number, toolExecutionSummaries?: ReadonlyArray<ToolExecutionSummaryInternal>): Promise<EnhancedGenerateResult>;
370
+ /**
371
+ * Persist a generate() turn's tool steps into conversation memory.
372
+ *
373
+ * The stream loops store each step's calls and results as they run;
374
+ * generate() never did, so the same session held tool rows for streamed
375
+ * turns and none for generated ones, and a later turn could not replay
376
+ * what generate() had called. Every native generate path closes out
377
+ * through finalizeNativeGenerate, so this is the one place to do it.
378
+ *
379
+ * Gated on a real session id: the storage helper otherwise invents a
380
+ * `session-<nanoid>` key, and on the in-memory backend that creates a
381
+ * throwaway session per tool-using call — evicting real ones once
382
+ * `maxSessions` is reached. A turn with no session has no history to
383
+ * replay into, so there is nothing to persist.
384
+ */
385
+ private persistGenerateToolSteps;
302
386
  /**
303
387
  * Invoke a caller's `onFinish` for a native turn.
304
388
  *
@@ -555,6 +639,11 @@ export declare abstract class BaseProvider implements AIProvider {
555
639
  * another request.
556
640
  */
557
641
  setFileToolRootPolicy(policy: FileToolRootPolicy | undefined): void;
642
+ /**
643
+ * Emit the start/end pair for a tool call a loop rejected before execute
644
+ * (unknown tool, schema rejection). See ToolsManager.emitRejectedToolCall.
645
+ */
646
+ protected emitRejectedToolCall(toolName: string, error: string, toolCallId?: string): void;
558
647
  /**
559
648
  * Resolve roots for a provider called directly, without NeuroLink. A
560
649
  * policy NeuroLink already bound is left alone unless these options carry
@@ -748,7 +837,7 @@ export declare abstract class BaseProvider implements AIProvider {
748
837
  /**
749
838
  * Check if tool executions should be stored and handle storage
750
839
  */
751
- protected handleToolExecutionStorage(toolCalls: unknown[], toolResults: unknown[], options: TextGenerationOptions | StreamOptions, currentTime: Date): Promise<void>;
840
+ protected handleToolExecutionStorage(toolCalls: MemoryToolCallRecord[], toolResults: MemoryToolResultRecord[], options: TextGenerationOptions | StreamOptions, currentTime: Date): Promise<void>;
752
841
  /**
753
842
  * Utility method to chunk large prompts into smaller pieces
754
843
  * @param prompt The prompt to chunk