@juspay/neurolink 12.47.13 → 12.48.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 +3 -3
  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 +29 -14
  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,9 @@
19
19
  * re-export) keep their original public names — only their definitions
20
20
  * move from `import("@juspay/hippocampus").Foo` to local structural form.
21
21
  */
22
+ import type { TokenUsage } from "./analytics.js";
23
+ import type { AdditionalMemoryUser } from "./generate.js";
24
+ import type { NeurolinkCredentials } from "./providers.js";
22
25
  export type MemorySqliteStorageConfig = {
23
26
  type: "sqlite";
24
27
  /** Path to SQLite file. Default: ./data/hippocampus.sqlite */
@@ -56,17 +59,159 @@ export type HippocampusAddOptions = {
56
59
  prompt?: string;
57
60
  maxWords?: number;
58
61
  };
62
+ /**
63
+ * The exact request shape Hippocampus's condenser sends to `generate()`
64
+ * (`@juspay/hippocampus` `src/client.ts`, the `neurolink.generate({...})`
65
+ * call inside `add()`). A full `NeuroLink` instance satisfies this
66
+ * structurally; so does any host object that can answer a plain text prompt.
67
+ */
68
+ export type HippocampusCondenserGenerateOptions = {
69
+ input: {
70
+ text: string;
71
+ };
72
+ provider?: string;
73
+ model?: string;
74
+ temperature?: number;
75
+ disableTools?: boolean;
76
+ };
77
+ /** The only field Hippocampus reads back from the condensation call. */
78
+ export type HippocampusCondenserResult = {
79
+ content?: string;
80
+ };
81
+ /**
82
+ * Minimal `generate()`-bearing object Hippocampus can use for condensation
83
+ * instead of constructing its own bare `new NeuroLink()`.
84
+ */
85
+ export type HippocampusNeurolinkLike = {
86
+ generate: (options: HippocampusCondenserGenerateOptions) => Promise<HippocampusCondenserResult>;
87
+ };
88
+ /**
89
+ * Condenser settings forwarded to Hippocampus.
90
+ *
91
+ * `provider` / `model` / `temperature` select the condensation model.
92
+ * `credentials` and `instance` are read by `@juspay/hippocampus` ≥0.2.0;
93
+ * the peer floor is 0.2.1, which also substitutes the condensation
94
+ * placeholders literally (user content containing `$&` no longer corrupts
95
+ * the prompt). NeuroLink always fills `instance` itself — a dedicated
96
+ * child instance that carries this instance's `credentials` — unless the
97
+ * host supplies one, so condensation stops depending on provider keys being
98
+ * present in `process.env`.
99
+ */
100
+ export type HippocampusNeurolinkConfig = {
101
+ provider?: string;
102
+ model?: string;
103
+ temperature?: number;
104
+ /**
105
+ * Credentials for the condenser's own NeuroLink instance. Defaults to the
106
+ * parent `new NeuroLink({ credentials })` value.
107
+ */
108
+ credentials?: NeurolinkCredentials;
109
+ /**
110
+ * Host-managed condenser. When set, NeuroLink does not build a child
111
+ * instance and passes this object through untouched.
112
+ */
113
+ instance?: HippocampusNeurolinkLike;
114
+ };
59
115
  /** Constructor config accepted by the Hippocampus class. */
60
116
  export type HippocampusConfig = {
61
117
  storage?: HippocampusStorageConfig;
62
118
  prompt?: string;
63
- neurolink?: {
64
- provider?: string;
65
- model?: string;
66
- temperature?: number;
67
- };
119
+ neurolink?: HippocampusNeurolinkConfig;
68
120
  maxWords?: number;
69
121
  };
122
+ /**
123
+ * One completed turn as seen by the memory write hooks. `usage`,
124
+ * `finishReason` and `toolsUsed` are best-effort: the generate path has the
125
+ * full result, the stream path only knows the provider, the accumulated
126
+ * text and this request's own stream result's tool names.
127
+ */
128
+ export type MemoryTurn = {
129
+ /** The caller's original prompt, before memory context was prepended. */
130
+ prompt: string;
131
+ /** The assistant's final text, trimmed. */
132
+ response: string;
133
+ /** Primary memory owner — `context.userId`. */
134
+ userId: string;
135
+ /** `context.sessionId`, when the call carried one. */
136
+ sessionId?: string;
137
+ provider?: string;
138
+ model?: string;
139
+ /** Names of tools that ran during the turn. */
140
+ toolsUsed?: string[];
141
+ /** Token usage — generate path only. */
142
+ usage?: TokenUsage;
143
+ /** Provider finish reason — generate path only. */
144
+ finishReason?: string;
145
+ };
146
+ /**
147
+ * Gate that decides whether a turn is written to memory at all. Runs inside
148
+ * the deferred background write, never on the response path. Returning
149
+ * `false` skips every `add()` for the turn (primary owner and
150
+ * `additionalUsers`). A thrown error is logged and treated as `false`.
151
+ */
152
+ export type MemoryShouldWriteHook = (turn: MemoryTurn) => boolean | Promise<boolean>;
153
+ /**
154
+ * Transform (or veto) the exact text handed to `Hippocampus.add()`. Receives
155
+ * the default `"User: …\nAssistant: …"` rendering; return a replacement
156
+ * string, or `null` to skip the write. A thrown error is logged and the
157
+ * write is skipped — a missed memory is recoverable, a polluted condensed
158
+ * summary is not.
159
+ */
160
+ export type MemoryBeforeStoreHook = (content: string, turn: MemoryTurn) => string | null | Promise<string | null>;
161
+ /**
162
+ * Write-side hooks accepted both per call (`generate({ memory })` /
163
+ * `stream({ memory })`) and on the instance
164
+ * (`conversationMemory.memory`). When both are set for the same hook, the
165
+ * per-call one wins for that call.
166
+ */
167
+ export type MemoryWriteHooks = {
168
+ shouldWrite?: MemoryShouldWriteHook;
169
+ onBeforeStore?: MemoryBeforeStoreHook;
170
+ };
171
+ /**
172
+ * Per-call `memory` block shared by `GenerateOptions` and `StreamOptions`.
173
+ *
174
+ * Overrides the instance-level memory behaviour for one call. All flags
175
+ * default to `true` when the instance memory SDK is enabled; when it is
176
+ * disabled this block has no effect.
177
+ */
178
+ export type MemoryCallOptions = MemoryWriteHooks & {
179
+ /** Master toggle for this call. When false, both read and write are skipped. Defaults to true. */
180
+ enabled?: boolean;
181
+ /** Whether to read condensed memory and prepend to prompt. Defaults to true. */
182
+ read?: boolean;
183
+ /** Whether to write (add/condense) the conversation into memory after completion. Defaults to true. */
184
+ write?: boolean;
185
+ /**
186
+ * Additional users whose memory should be retrieved/stored alongside the primary user.
187
+ * Each entry can override the condensation prompt and maxWords for that user.
188
+ * Primary user is still determined by context.userId.
189
+ */
190
+ additionalUsers?: AdditionalMemoryUser[];
191
+ /**
192
+ * Condensation prompt for the primary owner on this call. Precedence:
193
+ * per-call > instance `conversationMemory.memory.prompt` > Hippocampus
194
+ * default. Must contain `{{OLD_MEMORY}}` and `{{NEW_CONTENT}}`; a template
195
+ * missing either is logged and the instance prompt is used instead. An
196
+ * empty or whitespace-only string counts as unset.
197
+ */
198
+ prompt?: string;
199
+ /** Max words for the primary owner's condensed memory on this call. */
200
+ maxWords?: number;
201
+ };
202
+ /** Placeholder names a condensation prompt template can carry. */
203
+ export type CondensationPlaceholder = "OLD_MEMORY" | "NEW_CONTENT" | "MAX_WORDS";
204
+ /**
205
+ * Result of `validateCondensationPrompt()`. `missing` lists every absent
206
+ * placeholder; `fatal` is the subset whose absence breaks memory
207
+ * (`OLD_MEMORY`: each turn overwrites the summary; `NEW_CONTENT`: memory
208
+ * never grows). A missing `MAX_WORDS` only loses the word cap.
209
+ */
210
+ export type CondensationPromptValidation = {
211
+ valid: boolean;
212
+ missing: CondensationPlaceholder[];
213
+ fatal: CondensationPlaceholder[];
214
+ };
70
215
  /**
71
216
  * Subset of the @juspay/hippocampus client surface that NeuroLink core
72
217
  * actually calls. Defining this locally lets the initializer / SDK code
@@ -78,13 +223,36 @@ export type HippocampusLike = {
78
223
  delete: (ownerId: string) => Promise<void>;
79
224
  close: () => Promise<void>;
80
225
  };
226
+ /** Builds a Hippocampus-compatible client from the config NeuroLink assembled. */
227
+ export type HippocampusClientFactory = (config: HippocampusConfig) => HippocampusLike;
81
228
  /**
82
- * Consumer-facing memory config. Same shape as before — the `enabled`
83
- * flag toggles activation while the rest is passed straight through to
84
- * the Hippocampus constructor when the optional package is installed.
229
+ * Consumer-facing memory config. The `enabled` flag toggles activation; the
230
+ * `HippocampusConfig` part is passed to the Hippocampus constructor when the
231
+ * optional package is installed. The remaining fields are NeuroLink-side and
232
+ * never reach Hippocampus.
85
233
  */
86
- export type HippocampusMemory = HippocampusConfig & {
234
+ export type HippocampusMemory = HippocampusConfig & MemoryWriteHooks & {
87
235
  enabled?: boolean;
236
+ /**
237
+ * Host-managed Hippocampus client (or any object with the same
238
+ * `add/get/delete/close` surface). When set, NeuroLink skips loading and
239
+ * constructing `@juspay/hippocampus` and uses this instance directly.
240
+ * `add()` and `get()` are what core calls and must both be functions;
241
+ * a client missing either disables memory with an `error` log.
242
+ *
243
+ * As a factory it receives the `HippocampusConfig` NeuroLink assembled —
244
+ * including the credentialed condenser under `neurolink.instance` — so a
245
+ * host can construct its own client (a subclass, a different version, a
246
+ * test double) from exactly what `new Hippocampus(config)` would get.
247
+ */
248
+ client?: HippocampusLike | HippocampusClientFactory;
249
+ /**
250
+ * When true, a condensation prompt template (instance `prompt` or
251
+ * `HC_CONDENSATION_PROMPT`) missing `{{OLD_MEMORY}}` or `{{NEW_CONTENT}}`
252
+ * makes the constructor throw instead of the default disable-and-log
253
+ * (at `error`). An empty or whitespace-only template counts as unset.
254
+ */
255
+ strictPrompt?: boolean;
88
256
  };
89
257
  /**
90
258
  * Shape of the dynamically-required `@juspay/hippocampus` module surface
@@ -1,8 +1,9 @@
1
- import type { DeferredUsage, NeuroLinkEvents, TypedEventEmitter } from "./common.js";
1
+ import type { DeferredUsage } from "./common.js";
2
2
  import type { JSONSchema7 } from "./middleware.js";
3
3
  import type { LanguageModelV3, LanguageModelV3CallOptions } from "./middleware.js";
4
4
  import type { StreamOptions } from "./stream.js";
5
5
  import type { Tool } from "./tools.js";
6
+ import type { StepResult } from "./providers.js";
6
7
  export type OpenAICompatChatRole = "system" | "user" | "assistant" | "tool";
7
8
  export type OpenAICompatTextContent = {
8
9
  type: "text";
@@ -223,6 +224,8 @@ export type ToolExecutionSummaryInternal = {
223
224
  error?: string;
224
225
  startTime: Date;
225
226
  endTime: Date;
227
+ /** Zero-based loop step that ran this tool, when the loop tracks one. */
228
+ stepIndex?: number;
226
229
  };
227
230
  export type StreamLoopArgs = {
228
231
  maxSteps: number;
@@ -233,11 +236,16 @@ export type StreamLoopArgs = {
233
236
  options: StreamOptions;
234
237
  conversation: OpenAICompatChatMessage[];
235
238
  openAITools: OpenAICompatChatTool[] | undefined;
236
- openAIToolChoice: OpenAICompatToolChoiceWire | undefined;
239
+ /**
240
+ * The wire `tool_choice` for one step. A function rather than a value
241
+ * because a forced choice applies only to the first `toolChoiceSteps`
242
+ * steps, and a caller's `prepareStep` hook may override it per step;
243
+ * `steps` is the record of the steps completed so far, handed to that hook.
244
+ */
245
+ stepToolChoice: (step: number, steps: StepResult<Record<string, Tool>>[]) => Promise<OpenAICompatToolChoiceWire | undefined>;
237
246
  toolsRecord: Record<string, Tool>;
238
247
  /** Wire → registered tool-name map when sanitization was needed (see buildWireToolNameMaps). */
239
248
  toolNameFromWire?: Map<string, string>;
240
- emitter: TypedEventEmitter<NeuroLinkEvents> | undefined;
241
249
  toolsUsed: string[];
242
250
  toolExecutionSummaries: ToolExecutionSummaryInternal[];
243
251
  pushChunk: (chunk: OpenAICompatStreamChunk) => void;
@@ -9,9 +9,10 @@ import type { TokenUsage } from "./analytics.js";
9
9
  import type { JsonValue, UnknownRecord } from "./common.js";
10
10
  import type { TerminalAgentModeOption, TerminalAgentModeVersion } from "./agentMode.js";
11
11
  import type { Content, ImageWithAltText } from "./content.js";
12
- import type { ChatMessage } from "./conversation.js";
12
+ import type { ChatMessage, ToolReplayMode } from "./conversation.js";
13
13
  import type { StreamNoOutputSentinel } from "./noOutputSentinel.js";
14
- import type { AdditionalMemoryUser, GenerateStopReason, TTSMetadata, ToolExecutionCaptureOptions } from "./generate.js";
14
+ import type { GenerateStopReason, TTSMetadata, ToolExecutionCaptureOptions } from "./generate.js";
15
+ import type { MemoryCallOptions } from "./memory.js";
15
16
  import type { AIModelProviderConfig, NeurolinkCredentials } from "./providers.js";
16
17
  import type { TTSChunk, TTSOptions, TTSResult } from "./tts.js";
17
18
  import type { STTOptions, STTResult } from "./stt.js";
@@ -580,10 +581,18 @@ export type StreamOptions = {
580
581
  /**
581
582
  * Tool choice configuration for streaming generation.
582
583
  * Mirrors generate() so translated/fallback requests can preserve forced tool use.
584
+ * A forced choice (`"required"` or a named tool) is applied only to the
585
+ * first `toolChoiceSteps` steps (default 1); see GenerateOptions.toolChoice.
583
586
  */
584
587
  toolChoice?: ToolChoice<Record<string, Tool>>;
588
+ /** Leading steps a forced `toolChoice` stays in force (default 1). See GenerateOptions.toolChoiceSteps. */
589
+ toolChoiceSteps?: number;
590
+ /** How stored tool steps are replayed into the prompt. See GenerateOptions.replayToolSteps. */
591
+ replayToolSteps?: ToolReplayMode;
585
592
  /**
586
593
  * Optional callback that runs before each stream step in a multi-step generation.
594
+ * Only a returned `toolChoice` is honoured (for that step); `activeTools` is
595
+ * accepted but ignored. See GenerateOptions.prepareStep.
587
596
  */
588
597
  prepareStep?: (options: {
589
598
  steps: StepResult<Record<string, Tool>>[];
@@ -739,21 +748,9 @@ export type StreamOptions = {
739
748
  * Override the global memory SDK behavior for this specific call.
740
749
  * All flags default to `true` when the global memory SDK is enabled.
741
750
  * If the global memory SDK is disabled, these flags have no effect.
751
+ * Shared with `GenerateOptions` — see `MemoryCallOptions`.
742
752
  */
743
- memory?: {
744
- /** Master toggle for this call. When false, both read and write are skipped. Defaults to true. */
745
- enabled?: boolean;
746
- /** Whether to read condensed memory and prepend to prompt. Defaults to true. */
747
- read?: boolean;
748
- /** Whether to write (add/condense) the conversation into memory after completion. Defaults to true. */
749
- write?: boolean;
750
- /**
751
- * Additional users whose memory should be retrieved/stored alongside the primary user.
752
- * Each entry can override the condensation prompt and maxWords for that user.
753
- * Primary user is still determined by context.userId.
754
- */
755
- additionalUsers?: AdditionalMemoryUser[];
756
- };
753
+ memory?: MemoryCallOptions;
757
754
  /** PII detection — scans and optionally redacts PII from input before the LLM call. */
758
755
  piiDetection?: {
759
756
  enabled?: boolean;
@@ -51,10 +51,25 @@ export type STTOptions = {
51
51
  /**
52
52
  * STT result from transcription
53
53
  */
54
+ /**
55
+ * Where a provider's `STTResult.confidence` came from, when it has to say —
56
+ * Scribe reports a per-word `logprob` on some responses and only a
57
+ * language-detection probability on others, and a caller reading the number
58
+ * should know which it is getting. `"none"` means the provider reported no
59
+ * transcript-level signal at all and the field is 0.
60
+ */
61
+ export type STTConfidenceSource = "word_logprobs" | "language_probability" | "none";
54
62
  export type STTResult = {
55
63
  /** Full transcribed text */
56
64
  text: string;
57
- /** Confidence score (0-1) */
65
+ /**
66
+ * Transcript confidence (0-1) — Deepgram's alternative confidence,
67
+ * Whisper's `exp(avg_logprob)`, Scribe's mean per-word `exp(logprob)`.
68
+ * Always present: a strict-TS consumer reads it without a guard. A provider
69
+ * with no transcript-level signal reports 0 and, where it can, says so in
70
+ * `metadata.confidenceSource` rather than passing off a different number
71
+ * (a language-detection probability, say) as this one silently.
72
+ */
58
73
  confidence: number;
59
74
  /** Detected language code */
60
75
  language?: string;
@@ -74,6 +89,8 @@ export type STTResult = {
74
89
  provider?: string;
75
90
  /** Model used */
76
91
  model?: string;
92
+ /** What `confidence` was derived from, for providers that must choose. */
93
+ confidenceSource?: STTConfidenceSource;
77
94
  /** Additional provider-specific metadata */
78
95
  [key: string]: unknown;
79
96
  };
@@ -435,6 +452,46 @@ export type ElevenLabsVoice = {
435
452
  export type ElevenLabsVoicesResponse = {
436
453
  voices: ElevenLabsVoice[];
437
454
  };
455
+ /**
456
+ * Scribe model ids the batch `speech-to-text` endpoint accepts. `scribe_v2`
457
+ * is the current model and the default; `scribe_v2_medical` is its clinical
458
+ * fine-tune; `scribe_v1` (and the experimental variant) are deprecated. The
459
+ * `(string & {})` member keeps the union open so a newly released id can be
460
+ * used without a cast.
461
+ */
462
+ export type ElevenLabsSTTModel = "scribe_v2" | "scribe_v2_medical" | "scribe_v1" | "scribe_v1_experimental" | (string & {});
463
+ export type ElevenLabsSTTOptions = STTOptions & {
464
+ /** Scribe model id. Default `scribe_v2`. */
465
+ model?: ElevenLabsSTTModel;
466
+ /**
467
+ * Annotate non-speech audio events ("(laughter)", "(music)") in the
468
+ * transcript. ElevenLabs defaults this to true; NeuroLink sends `false` so
469
+ * the transcript reads as plain speech unless asked otherwise.
470
+ * @default false
471
+ */
472
+ tagAudioEvents?: boolean;
473
+ /** Per-request timeout in milliseconds. Default 60_000. */
474
+ timeoutMs?: number;
475
+ /**
476
+ * API base URL including the `/v1` prefix. Default
477
+ * `ELEVENLABS_BASE_URL` or `https://api.elevenlabs.io/v1`.
478
+ */
479
+ baseUrl?: string;
480
+ };
481
+ export type ElevenLabsSTTWord = {
482
+ text: string;
483
+ start?: number;
484
+ end?: number;
485
+ type?: "word" | "spacing" | "audio_event" | string;
486
+ speaker_id?: string;
487
+ logprob?: number;
488
+ };
489
+ export type ElevenLabsSTTResponse = {
490
+ language_code?: string;
491
+ language_probability?: number;
492
+ text: string;
493
+ words?: ElevenLabsSTTWord[];
494
+ };
438
495
  export type AzureVoiceInfo = {
439
496
  Name: string;
440
497
  DisplayName: string;
@@ -358,6 +358,8 @@ export type ToolEventPayload = {
358
358
  timestamp?: number;
359
359
  duration?: number;
360
360
  executionId?: string;
361
+ /** The model's tool-call id when the loop supplied one, for pairing start/end. */
362
+ toolCallId?: string;
361
363
  };
362
364
  /**
363
365
  * Tool execution summary for completed executions
@@ -570,3 +572,15 @@ export type BashToolResult = {
570
572
  stderr: string;
571
573
  error?: string;
572
574
  };
575
+ /**
576
+ * Own property stamped on a tool object whose `execute` already emits one
577
+ * `tool:start` / `tool:end` pair per execution. It lives on the OBJECT, not
578
+ * the function: later layers (the execution recorder, discovery) replace
579
+ * `execute` via `{ ...tool, execute }`, which drops a function-identity check
580
+ * but carries a symbol-keyed own property along.
581
+ */
582
+ export declare const TOOL_EVENTS_WRAPPED: unique symbol;
583
+ /** A `Tool` that may carry the {@link TOOL_EVENTS_WRAPPED} marker. */
584
+ export type EventWrappedTool = Tool & {
585
+ [TOOL_EVENTS_WRAPPED]?: true;
586
+ };
@@ -22,3 +22,11 @@ export function isToolDefinition(value) {
22
22
  typeof value.description === "string" &&
23
23
  typeof value.execute === "function");
24
24
  }
25
+ /**
26
+ * Own property stamped on a tool object whose `execute` already emits one
27
+ * `tool:start` / `tool:end` pair per execution. It lives on the OBJECT, not
28
+ * the function: later layers (the execution recorder, discovery) replace
29
+ * `execute` via `{ ...tool, execute }`, which drops a function-identity check
30
+ * but carries a symbol-keyed own property along.
31
+ */
32
+ export const TOOL_EVENTS_WRAPPED = Symbol.for("neurolink.toolEventsWrapped");
@@ -25,12 +25,80 @@ export type TTSQuality = "standard" | "hd";
25
25
  * still surfacing the built-in choices in editor autocomplete.
26
26
  */
27
27
  export type TTSProviderName = "google-ai" | "vertex" | "openai-tts" | "elevenlabs" | "elevenlabs-tts" | "azure-tts" | "fish-audio" | "sixtydb" | "cartesia" | (string & {});
28
+ /**
29
+ * What `generate({ tts })` synthesizes.
30
+ *
31
+ * - `"direct"` — the input text / prompt itself, with no LLM call. This is
32
+ * the default, and what every documented "speak this text" caller relies
33
+ * on.
34
+ * - `"response"` — the model's generated reply, after generation completes.
35
+ *
36
+ * `stream()` always synthesizes the streamed response and ignores this.
37
+ */
38
+ export type TTSSynthesisMode = "direct" | "response";
39
+ /**
40
+ * Options for {@link prepareTextForSpeech} — the optional cleanup pass that
41
+ * turns model output (markdown, URLs, emoji) into text a TTS voice can read
42
+ * aloud. Every default below is what `tts: { sanitize: true }` applies.
43
+ */
44
+ export type SpeechSanitizeOptions = {
45
+ /**
46
+ * Strip markdown syntax: headings, emphasis, inline code, list bullets,
47
+ * blockquotes, tables (kept as comma-separated rows), links and images
48
+ * (kept as their text). Fenced code blocks follow `codeBlocks`.
49
+ * @default true
50
+ */
51
+ markdown?: boolean;
52
+ /**
53
+ * How fenced code blocks are spoken. `"drop"` removes them entirely,
54
+ * `"phrase"` replaces each block with `codeBlockPhrase`, `"keep"` strips
55
+ * only the fence lines and reads the contents. Incremental `stream()`
56
+ * synthesis always uses `"keep"`: a segment may hold an unterminated fence,
57
+ * so the block's extent is unknowable per segment.
58
+ * @default "phrase"
59
+ */
60
+ codeBlocks?: "drop" | "phrase" | "keep";
61
+ /**
62
+ * Spoken stand-in for a fenced code block when `codeBlocks` is `"phrase"`.
63
+ * @default "Code block omitted."
64
+ */
65
+ codeBlockPhrase?: string;
66
+ /**
67
+ * How URLs are spoken. `"hostname"` reduces `https://docs.example.com/a/b`
68
+ * to `docs.example.com`; `"remove"` drops the URL altogether.
69
+ * @default "hostname"
70
+ */
71
+ urls?: "hostname" | "remove";
72
+ /**
73
+ * Remove emoji and pictographs (`\p{Extended_Pictographic}`,
74
+ * `\p{Emoji_Presentation}`, plus modifiers and joiners).
75
+ * @default true
76
+ */
77
+ emoji?: boolean;
78
+ };
79
+ /** {@link SpeechSanitizeOptions} with every default applied. */
80
+ export type ResolvedSpeechSanitizeOptions = Required<SpeechSanitizeOptions>;
28
81
  /**
29
82
  * TTS configuration options
30
83
  */
31
84
  export type TTSOptions = {
32
85
  /** Enable TTS output */
33
86
  enabled?: boolean;
87
+ /**
88
+ * What to synthesize: the input text (`"direct"`, the default) or the
89
+ * model's reply (`"response"`). An explicit `mode` wins over the legacy
90
+ * `useAiResponse` flag. Ignored by `stream()`, which always synthesizes the
91
+ * streamed response.
92
+ *
93
+ * @example Speak the model's reply
94
+ * ```typescript
95
+ * const result = await neurolink.generate({
96
+ * input: { text: "Tell me a joke" },
97
+ * tts: { enabled: true, mode: "response" },
98
+ * });
99
+ * ```
100
+ */
101
+ mode?: TTSSynthesisMode;
34
102
  /**
35
103
  * Use the AI-generated response for TTS instead of the input text
36
104
  *
@@ -41,6 +109,8 @@ export type TTSOptions = {
41
109
  * When true: TTS will synthesize the AI-generated response after generation completes
42
110
  *
43
111
  * @default false
112
+ * @deprecated Use `mode: "response"` / `mode: "direct"` instead. Still
113
+ * honoured when `mode` is not set.
44
114
  *
45
115
  * @example Using input text directly (default)
46
116
  * ```typescript
@@ -63,12 +133,37 @@ export type TTSOptions = {
63
133
  * ```
64
134
  */
65
135
  useAiResponse?: boolean;
136
+ /**
137
+ * Clean the text before it reaches the voice: strip markdown, reduce URLs
138
+ * to their hostname, drop emoji. `true` applies the defaults of
139
+ * {@link SpeechSanitizeOptions}; an object tunes them. Off by default so
140
+ * existing callers get byte-identical synthesis input. In `stream()` the
141
+ * pass runs per segment with fenced code kept (fence lines stripped).
142
+ * @default false
143
+ */
144
+ sanitize?: boolean | SpeechSanitizeOptions;
145
+ /**
146
+ * Cancels synthesis. A handler that supports it (ElevenLabs) aborts the
147
+ * request in flight and makes no further retry attempt once this fires.
148
+ * `generate()` derives one from its own synthesis timeout and the caller's
149
+ * `abortSignal`, so a handler's retry loop cannot outlive the call that
150
+ * started it and issue a billable request nobody is waiting for.
151
+ */
152
+ signal?: AbortSignal;
66
153
  /** Voice identifier (e.g., "en-US-Neural2-C") */
67
154
  voice?: string;
68
155
  /** Audio format (default: mp3) */
69
156
  format?: TTSAudioFormat;
70
157
  /** Speaking rate 0.25-4.0 (default: 1.0) */
71
158
  speed?: number;
159
+ /**
160
+ * BCP-47 language tag (e.g. "en-US", "hi") for providers that accept one.
161
+ * Provider-specific: ElevenLabs reduces it to the ISO 639-1 primary subtag
162
+ * (`en-US` → `en`) and forwards it as `language_code` on every model except
163
+ * `eleven_multilingual_v2`, which rejects the field; Cartesia maps it to
164
+ * `language`.
165
+ */
166
+ language?: string;
72
167
  /** Voice pitch adjustment -20.0 to 20.0 semitones (default: 0.0) */
73
168
  pitch?: number;
74
169
  /** Volume gain in dB -96.0 to 16.0 (default: 0.0) */
@@ -88,6 +183,19 @@ export type TTSOptions = {
88
183
  */
89
184
  streamingBufferSize?: number;
90
185
  };
186
+ /**
187
+ * Handler-level wall-clock knobs a provider option type may carry (today:
188
+ * `ElevenLabsTTSOptions`). `generate()`'s outer synthesis timeout is raised to
189
+ * at least `timeoutMs × (1 + retries)` plus backoff slack — with the handler
190
+ * defaults (30 s, one retry) standing in for a knob the caller left unset —
191
+ * so a handler's own retry loop is never cut short from outside.
192
+ */
193
+ export type TTSHandlerTimeBudget = {
194
+ /** Per-attempt request timeout in milliseconds (default 30 000; capped at 2³¹−1). */
195
+ timeoutMs?: number;
196
+ /** Retries after the first attempt (total attempts = 1 + retries). */
197
+ retries?: number;
198
+ };
91
199
  /**
92
200
  * TTS audio result returned from generation
93
201
  */
@@ -13,7 +13,7 @@
13
13
  export * from "./tts.js";
14
14
  export * from "./stt.js";
15
15
  export * from "./realtime.js";
16
- import type { TTSAudioFormat, TTSOptions, TTSResult, TTSVoice } from "./tts.js";
16
+ import type { TTSAudioFormat, TTSHandlerTimeBudget, TTSOptions, TTSResult, TTSVoice } from "./tts.js";
17
17
  import type { TTSHandler } from "./common.js";
18
18
  import type { STTResult, STTHandler } from "./stt.js";
19
19
  import type { RealtimeHandler } from "./realtime.js";
@@ -28,7 +28,7 @@ export type VoiceProviderType = "tts" | "stt" | "realtime";
28
28
  /**
29
29
  * Voice provider name union type
30
30
  */
31
- export type VoiceProviderName = "google-tts" | "elevenlabs" | "openai-tts" | "sixtydb" | "azure-tts" | "sarvam" | "murf" | "playai" | "speechify" | "cartesia" | "deepgram" | "gladia" | "whisper" | "assemblyai" | "google-stt" | "azure-stt" | "openai-realtime" | "gemini-live";
31
+ export type VoiceProviderName = "google-tts" | "elevenlabs" | "openai-tts" | "sixtydb" | "azure-tts" | "sarvam" | "murf" | "playai" | "speechify" | "cartesia" | "deepgram" | "gladia" | "whisper" | "assemblyai" | "google-stt" | "azure-stt" | "elevenlabs-stt" | "openai-realtime" | "gemini-live";
32
32
  /**
33
33
  * Base voice provider configuration
34
34
  */
@@ -264,13 +264,50 @@ export type AzureTTSOptions = TTSOptions & {
264
264
  */
265
265
  allowRawSSML?: boolean;
266
266
  };
267
- export type ElevenLabsModel = "eleven_multilingual_v2" | "eleven_turbo_v2_5" | "eleven_turbo_v2" | "eleven_monolingual_v1";
268
- export type ElevenLabsTTSOptions = TTSOptions & {
267
+ export type ElevenLabsModel = "eleven_multilingual_v2" | "eleven_turbo_v2_5" | "eleven_turbo_v2" | "eleven_flash_v2_5" | "eleven_flash_v2" | "eleven_v3" | "eleven_monolingual_v1";
268
+ /** Opus bitrates (kbps) ElevenLabs accepts on `opus_48000_*`. */
269
+ export type ElevenLabsOpusBitrate = 32 | 64 | 96 | 128 | 192;
270
+ /** MP3 bitrates (kbps) ElevenLabs accepts on `mp3_44100_*`. */
271
+ export type ElevenLabsMp3Bitrate = 32 | 64 | 96 | 128 | 192;
272
+ /**
273
+ * Every `output_format` value the ElevenLabs text-to-speech endpoint accepts.
274
+ * There is no `ogg_*` family — Ogg/Opus is `opus_48000_<kbps>`.
275
+ */
276
+ export type ElevenLabsOutputFormat = "mp3_22050_32" | "mp3_44100_32" | "mp3_44100_64" | "mp3_44100_96" | "mp3_44100_128" | "mp3_44100_192" | "opus_48000_32" | "opus_48000_64" | "opus_48000_96" | "opus_48000_128" | "opus_48000_192" | "pcm_8000" | "pcm_16000" | "pcm_22050" | "pcm_24000" | "pcm_44100" | "pcm_48000" | "ulaw_8000" | "alaw_8000";
277
+ /** The `voice_settings` object on an ElevenLabs synthesis request. */
278
+ export type ElevenLabsVoiceSettings = {
279
+ stability?: number;
280
+ similarity_boost?: number;
281
+ style?: number;
282
+ use_speaker_boost?: boolean;
283
+ /** 0.7–1.2; `TTSOptions.speed` is clamped into this range. */
284
+ speed?: number;
285
+ };
286
+ export type ElevenLabsTTSOptions = TTSOptions & TTSHandlerTimeBudget & {
269
287
  model?: ElevenLabsModel;
270
288
  stability?: number;
271
289
  similarityBoost?: number;
272
290
  style?: number;
273
291
  useSpeakerBoost?: boolean;
292
+ /** Bitrate for `format: "ogg" | "opus"` (→ `opus_48000_<kbps>`). Default 64; an unlisted value falls back to it with a warning. */
293
+ opusBitrate?: ElevenLabsOpusBitrate;
294
+ /** Bitrate for `format: "mp3"` (→ `mp3_44100_<kbps>`). Default 128; an unlisted value falls back to it with a warning. */
295
+ mp3Bitrate?: ElevenLabsMp3Bitrate;
296
+ /**
297
+ * Language sent as `language_code`, an ISO 639-1 code; a BCP-47 tag is
298
+ * reduced to its primary subtag (`en-US` → `en`). Sent on every model
299
+ * except `eleven_multilingual_v2`, which does not accept the field (the
300
+ * API ignores the code on any other model that cannot enforce it).
301
+ * Falls back to the generic `TTSOptions.language`.
302
+ */
303
+ languageCode?: string;
304
+ /** Raw `voice_settings` overrides; explicit fields win over the camelCase ones. */
305
+ voiceSettings?: ElevenLabsVoiceSettings;
306
+ /**
307
+ * API base URL including the `/v1` prefix. Default
308
+ * `ELEVENLABS_BASE_URL` or `https://api.elevenlabs.io/v1`.
309
+ */
310
+ baseUrl?: string;
274
311
  };
275
312
  export type GoogleVoiceType = "Standard" | "WaveNet" | "Neural2" | "Studio" | "Polyglot";
276
313
  export type GoogleTTSOptions = TTSOptions & {