@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
@@ -7,7 +7,7 @@
7
7
  *
8
8
  * @module utils/decisionAnswers
9
9
  */
10
- import type { DecisionAnswerMap, DecisionBooleanGate, DecisionChoiceReading, DecisionScoreReading, ProviderDescriptor } from "../types/index.js";
10
+ import type { DecisionAnswerMap, DecisionBooleanGate, DecisionChoiceReading, DecisionQuestion, DecisionResult, DecisionScoreReading, ProviderDescriptor } from "../types/index.js";
11
11
  /**
12
12
  * Read a yes/no answer as a probability.
13
13
  *
@@ -54,3 +54,33 @@ export declare function gateDecisionBoolean(answers: DecisionAnswerMap, id: stri
54
54
  * after any normalisation the wire might apply.
55
55
  */
56
56
  export declare function decisionKey(namespace: string, index: number): string;
57
+ /** Namespace for questions a host adds through `decisionHooks.extendQuestions`. */
58
+ export declare const HOST_DECISION_NAMESPACE = "host";
59
+ /** Whether a wire id is one the funnel gave a host's question (`host__N`). */
60
+ export declare function isHostDecisionKey(id: string): boolean;
61
+ /**
62
+ * A validated copy of a question a host handed back from `extendQuestions`,
63
+ * or undefined when it is malformed or cannot be copied. The wire carries
64
+ * the copy, never the host's object: a getter, a proxy or a later edit on
65
+ * the host's side can then reach neither the request nor the answers keyed
66
+ * on it. A value `structuredClone` refuses (a proxy, a function-valued
67
+ * field, a throwing getter) is treated as malformed.
68
+ */
69
+ export declare function snapshotDecisionQuestion(value: unknown): DecisionQuestion | undefined;
70
+ /**
71
+ * `result` without the answers to a host's namespaced questions — what a
72
+ * consumer is handed when the funnel's host-facing steps failed after the
73
+ * wire call had already returned. A null-prototype map, so an own id such
74
+ * as `__proto__` survives the copy.
75
+ */
76
+ export declare function stripHostAnswers(result: DecisionResult, hostIdByWireId: ReadonlyMap<string, string>): DecisionResult;
77
+ /**
78
+ * Runtime check that a value from outside the type system — a host's
79
+ * `extendQuestions` hook — is a well-formed question, so a malformed one
80
+ * is dropped on its own rather than failing the whole request that
81
+ * NeuroLink's own routing depends on.
82
+ *
83
+ * A `choice` needs at least two options and a `score` at least two levels;
84
+ * fewer carries no information, and the servers refuse it anyway.
85
+ */
86
+ export declare function isDecisionQuestion(value: unknown): value is DecisionQuestion;
@@ -93,3 +93,117 @@ export function gateDecisionBoolean(answers, id, gate) {
93
93
  export function decisionKey(namespace, index) {
94
94
  return `${namespace}__${index}`;
95
95
  }
96
+ /** Namespace for questions a host adds through `decisionHooks.extendQuestions`. */
97
+ export const HOST_DECISION_NAMESPACE = "host";
98
+ /** Whether a wire id is one the funnel gave a host's question (`host__N`). */
99
+ export function isHostDecisionKey(id) {
100
+ return id.startsWith(`${HOST_DECISION_NAMESPACE}__`);
101
+ }
102
+ /**
103
+ * A validated copy of a question a host handed back from `extendQuestions`,
104
+ * or undefined when it is malformed or cannot be copied. The wire carries
105
+ * the copy, never the host's object: a getter, a proxy or a later edit on
106
+ * the host's side can then reach neither the request nor the answers keyed
107
+ * on it. A value `structuredClone` refuses (a proxy, a function-valued
108
+ * field, a throwing getter) is treated as malformed.
109
+ */
110
+ export function snapshotDecisionQuestion(value) {
111
+ let copy;
112
+ try {
113
+ copy = structuredClone(value);
114
+ }
115
+ catch {
116
+ return undefined;
117
+ }
118
+ return isDecisionQuestion(copy) ? copy : undefined;
119
+ }
120
+ /**
121
+ * `result` without the answers to a host's namespaced questions — what a
122
+ * consumer is handed when the funnel's host-facing steps failed after the
123
+ * wire call had already returned. A null-prototype map, so an own id such
124
+ * as `__proto__` survives the copy.
125
+ */
126
+ export function stripHostAnswers(result, hostIdByWireId) {
127
+ if (hostIdByWireId.size === 0) {
128
+ return result;
129
+ }
130
+ const answers = Object.create(null);
131
+ for (const [id, answer] of Object.entries(result.answers)) {
132
+ if (!hostIdByWireId.has(id)) {
133
+ answers[id] = answer;
134
+ }
135
+ }
136
+ return { ...result, answers };
137
+ }
138
+ const isRecordValue = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
139
+ /**
140
+ * Whether `value` is something a decision model accepts as content: a
141
+ * string, number or boolean, or an array or plain object of the same.
142
+ * `null` and `undefined` are not — a question with no instructions is
143
+ * refused by every provider.
144
+ */
145
+ function isDecisionInput(value) {
146
+ switch (typeof value) {
147
+ case "string":
148
+ case "boolean":
149
+ return true;
150
+ case "number":
151
+ // NaN and ±Infinity serialise to null and would make the provider
152
+ // reject the whole request, not just this question.
153
+ return Number.isFinite(value);
154
+ case "object":
155
+ if (value === null) {
156
+ return false;
157
+ }
158
+ return Array.isArray(value)
159
+ ? value.every(isDecisionInput)
160
+ : Object.values(value).every(isDecisionInput);
161
+ default:
162
+ return false;
163
+ }
164
+ }
165
+ /**
166
+ * Runtime check that a value from outside the type system — a host's
167
+ * `extendQuestions` hook — is a well-formed question, so a malformed one
168
+ * is dropped on its own rather than failing the whole request that
169
+ * NeuroLink's own routing depends on.
170
+ *
171
+ * A `choice` needs at least two options and a `score` at least two levels;
172
+ * fewer carries no information, and the servers refuse it anyway.
173
+ */
174
+ export function isDecisionQuestion(value) {
175
+ try {
176
+ return isWellFormedDecisionQuestion(value);
177
+ }
178
+ catch {
179
+ // Cyclic or otherwise exotic host input: the walk below can overflow the
180
+ // stack. That drops this question alone rather than failing the request.
181
+ return false;
182
+ }
183
+ }
184
+ function isWellFormedDecisionQuestion(value) {
185
+ if (!isRecordValue(value) || !isDecisionInput(value.instructions)) {
186
+ return false;
187
+ }
188
+ switch (value.type) {
189
+ case "boolean": {
190
+ if (value.criteria === undefined) {
191
+ return true;
192
+ }
193
+ if (!isRecordValue(value.criteria)) {
194
+ return false;
195
+ }
196
+ return Object.values(value.criteria).every((side) => side === null || side === undefined || isDecisionInput(side));
197
+ }
198
+ case "choice":
199
+ return (isRecordValue(value.criteria) &&
200
+ Object.keys(value.criteria).length >= 2 &&
201
+ Object.values(value.criteria).every((rubric) => rubric === null || isDecisionInput(rubric)));
202
+ case "score":
203
+ return (Array.isArray(value.criteria) &&
204
+ value.criteria.length >= 2 &&
205
+ value.criteria.every((level) => level === null || isDecisionInput(level)));
206
+ default:
207
+ return false;
208
+ }
209
+ }
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Decision limits — the one place a provider's `decisionLimits` are read and
3
+ * a state's size is estimated against them.
4
+ *
5
+ * `SystemOneDecisionProvider` refuses an over-limit request through these
6
+ * functions, and `NeuroLink.decisionLimits()` / `estimateDecisionStateTokens`
7
+ * expose the same functions to hosts, so what a host measures before the call
8
+ * is exactly what the pre-flight check measures inside it. Two estimators that
9
+ * drift apart would let a host size a state as fitting and then watch it be
10
+ * refused.
11
+ *
12
+ * @module utils/decisionLimits
13
+ */
14
+ import type { DecisionLimits, DecisionLimitsReading, DecisionState, ProviderDescriptor } from "../types/index.js";
15
+ /**
16
+ * Estimate how many tokens a decision model will read from `state`.
17
+ *
18
+ * `estimateTokens` assumes ~4 characters per token. That holds for English
19
+ * and is several times too generous for other scripts on an English
20
+ * tokenizer, so when the limits declare a `nonAsciiTokensPerChar` rate,
21
+ * non-ASCII characters are counted at it instead. Digits, ASCII punctuation
22
+ * and astral characters (emoji) are each counted separately only when the
23
+ * limits declare a rate for that class — a tokenizer that reads each digit,
24
+ * and most punctuation, on its own makes a number-heavy or JSON-heavy state
25
+ * several times longer than the ~4-characters-per-token estimate suggests. A
26
+ * non-string state is serialized first, as it is on the wire.
27
+ *
28
+ * This is the estimator the pre-flight refusal uses: a state this reports at
29
+ * or under `maxStateTokens` is sent, one over it is refused before any
30
+ * network call.
31
+ */
32
+ export declare function estimateDecisionStateTokens(state: DecisionState, limits?: Pick<DecisionLimits, "nonAsciiTokensPerChar" | "digitTokensPerChar" | "symbolTokensPerChar" | "astralTokensPerChar">): number;
33
+ /**
34
+ * Flatten a descriptor's `decisionLimits` for one model: the per-model entry
35
+ * overrides the base, field by field, and `advisory` becomes
36
+ * `enforcedLocally: false`. Returns null for a descriptor that declares no
37
+ * limits.
38
+ */
39
+ export declare function resolveDecisionLimitsReading(descriptor: Pick<ProviderDescriptor, "name" | "decisionLimits">, model: string): DecisionLimitsReading | null;
@@ -0,0 +1,116 @@
1
+ /**
2
+ * Decision limits — the one place a provider's `decisionLimits` are read and
3
+ * a state's size is estimated against them.
4
+ *
5
+ * `SystemOneDecisionProvider` refuses an over-limit request through these
6
+ * functions, and `NeuroLink.decisionLimits()` / `estimateDecisionStateTokens`
7
+ * expose the same functions to hosts, so what a host measures before the call
8
+ * is exactly what the pre-flight check measures inside it. Two estimators that
9
+ * drift apart would let a host size a state as fitting and then watch it be
10
+ * refused.
11
+ *
12
+ * @module utils/decisionLimits
13
+ */
14
+ import { CHARS_PER_TOKEN, estimateTokens, serializeForEstimate, } from "./tokenEstimation.js";
15
+ /**
16
+ * Estimate how many tokens a decision model will read from `state`.
17
+ *
18
+ * `estimateTokens` assumes ~4 characters per token. That holds for English
19
+ * and is several times too generous for other scripts on an English
20
+ * tokenizer, so when the limits declare a `nonAsciiTokensPerChar` rate,
21
+ * non-ASCII characters are counted at it instead. Digits, ASCII punctuation
22
+ * and astral characters (emoji) are each counted separately only when the
23
+ * limits declare a rate for that class — a tokenizer that reads each digit,
24
+ * and most punctuation, on its own makes a number-heavy or JSON-heavy state
25
+ * several times longer than the ~4-characters-per-token estimate suggests. A
26
+ * non-string state is serialized first, as it is on the wire.
27
+ *
28
+ * This is the estimator the pre-flight refusal uses: a state this reports at
29
+ * or under `maxStateTokens` is sent, one over it is refused before any
30
+ * network call.
31
+ */
32
+ export function estimateDecisionStateTokens(state, limits) {
33
+ const text = serializeForEstimate(state);
34
+ const nonAsciiTokensPerChar = limits?.nonAsciiTokensPerChar;
35
+ const digit = limits?.digitTokensPerChar;
36
+ const symbol = limits?.symbolTokensPerChar;
37
+ const astral = limits?.astralTokensPerChar;
38
+ if (nonAsciiTokensPerChar === undefined &&
39
+ digit === undefined &&
40
+ symbol === undefined &&
41
+ astral === undefined) {
42
+ return estimateTokens(text);
43
+ }
44
+ // Each class of character is counted once, at its own rate. A class with no
45
+ // declared rate stays in the ordinary four-characters-a-token estimate (or,
46
+ // for non-ASCII, at that estimate's per-character rate), so a provider that
47
+ // declares none of the extra rates is estimated exactly as before.
48
+ const isDigit = (c) => c >= "0" && c <= "9";
49
+ const isSymbol = (c) => /[!-/:-@[-`{-~]/.test(c);
50
+ let digits = 0;
51
+ let symbols = 0;
52
+ let astrals = 0;
53
+ let nonAscii = 0;
54
+ const rest = [];
55
+ for (const c of text) {
56
+ const code = c.codePointAt(0) ?? 0;
57
+ if (code > 0xffff && astral !== undefined) {
58
+ astrals += 1;
59
+ }
60
+ else if (code > 0x7f) {
61
+ nonAscii += 1;
62
+ }
63
+ else if (digit !== undefined && isDigit(c)) {
64
+ digits += 1;
65
+ }
66
+ else if (symbol !== undefined && isSymbol(c)) {
67
+ symbols += 1;
68
+ }
69
+ else {
70
+ rest.push(c);
71
+ }
72
+ }
73
+ return (estimateTokens(rest.join("")) +
74
+ Math.ceil(digits * (digit ?? 0)) +
75
+ Math.ceil(symbols * (symbol ?? 0)) +
76
+ Math.ceil(astrals * (astral ?? 0)) +
77
+ Math.ceil(nonAscii * (nonAsciiTokensPerChar ?? 1 / CHARS_PER_TOKEN)));
78
+ }
79
+ /**
80
+ * Flatten a descriptor's `decisionLimits` for one model: the per-model entry
81
+ * overrides the base, field by field, and `advisory` becomes
82
+ * `enforcedLocally: false`. Returns null for a descriptor that declares no
83
+ * limits.
84
+ */
85
+ export function resolveDecisionLimitsReading(descriptor, model) {
86
+ const limits = descriptor.decisionLimits;
87
+ if (!limits) {
88
+ return null;
89
+ }
90
+ const modelLimits = limits.models?.[model];
91
+ const nonAsciiTokensPerChar = modelLimits?.nonAsciiTokensPerChar ?? limits.nonAsciiTokensPerChar;
92
+ return {
93
+ provider: descriptor.name,
94
+ model,
95
+ maxStateTokens: modelLimits?.maxStateTokens ?? limits.maxStateTokens,
96
+ // Absent for a provider that caps by tokens or bytes rather than by
97
+ // question count (TypeSafe, XOR).
98
+ ...(limits.maxQuestions !== undefined
99
+ ? { maxQuestions: limits.maxQuestions }
100
+ : {}),
101
+ ...(nonAsciiTokensPerChar !== undefined ? { nonAsciiTokensPerChar } : {}),
102
+ // Not model-overridable — `models` only carries `maxStateTokens` and
103
+ // `nonAsciiTokensPerChar` — so these three come from the base limits only.
104
+ ...(limits.digitTokensPerChar !== undefined
105
+ ? { digitTokensPerChar: limits.digitTokensPerChar }
106
+ : {}),
107
+ ...(limits.symbolTokensPerChar !== undefined
108
+ ? { symbolTokensPerChar: limits.symbolTokensPerChar }
109
+ : {}),
110
+ ...(limits.astralTokensPerChar !== undefined
111
+ ? { astralTokensPerChar: limits.astralTokensPerChar }
112
+ : {}),
113
+ ...(limits.media !== undefined ? { media: limits.media } : {}),
114
+ enforcedLocally: limits.advisory !== true,
115
+ };
116
+ }
@@ -30,6 +30,12 @@ declare class NeuroLinkLogger {
30
30
  * @param emitter - The event emitter instance
31
31
  */
32
32
  setEventEmitter(emitter: LogEventEmitter): void;
33
+ /**
34
+ * The process-wide sink currently receiving log events, or undefined when
35
+ * none is set. Lets code that must construct an instance without changing
36
+ * the sink capture it beforehand and put it back afterwards.
37
+ */
38
+ getEventEmitter(): LogEventEmitter | undefined;
33
39
  /**
34
40
  * Clears the event emitter reference.
35
41
  * Should be called when a NeuroLink instance is disposed to prevent memory
@@ -279,6 +285,7 @@ export declare const logger: {
279
285
  getLogs: (level?: LogLevel) => LogEntry[];
280
286
  clearLogs: () => void;
281
287
  setEventEmitter: (emitter: LogEventEmitter) => void;
288
+ getEventEmitter: () => LogEventEmitter | undefined;
282
289
  clearEventEmitter: (ifEmitter?: LogEventEmitter) => void;
283
290
  runInInstanceScope: <T>(instanceId: string, fn: () => T) => T;
284
291
  getInstanceScope: () => string | undefined;
@@ -113,6 +113,14 @@ class NeuroLinkLogger {
113
113
  setEventEmitter(emitter) {
114
114
  this.eventEmitter = emitter;
115
115
  }
116
+ /**
117
+ * The process-wide sink currently receiving log events, or undefined when
118
+ * none is set. Lets code that must construct an instance without changing
119
+ * the sink capture it beforehand and put it back afterwards.
120
+ */
121
+ getEventEmitter() {
122
+ return this.eventEmitter;
123
+ }
116
124
  /**
117
125
  * Clears the event emitter reference.
118
126
  * Should be called when a NeuroLink instance is disposed to prevent memory
@@ -634,6 +642,7 @@ export const logger = {
634
642
  getLogs: (level) => neuroLinkLogger.getLogs(level),
635
643
  clearLogs: () => neuroLinkLogger.clearLogs(),
636
644
  setEventEmitter: (emitter) => neuroLinkLogger.setEventEmitter(emitter),
645
+ getEventEmitter: () => neuroLinkLogger.getEventEmitter(),
637
646
  clearEventEmitter: (ifEmitter) => neuroLinkLogger.clearEventEmitter(ifEmitter),
638
647
  // Per-instance routing (see NeuroLinkLogger.runInInstanceScope)
639
648
  runInInstanceScope: (instanceId, fn) => neuroLinkLogger.runInInstanceScope(instanceId, fn),
@@ -1,10 +1,47 @@
1
1
  import type { GenerateOptions, MultimodalChatMessage, StreamOptions, TextGenerationOptions, VisionImageOutputFormat } from "../types/index.js";
2
- import type { ModelMessage } from "../types/index.js";
2
+ import type { ModelMessage, ToolHistoryRow, ToolReplayMode } from "../types/index.js";
3
3
  /**
4
4
  * Type-safe conversion from MultimodalChatMessage[] to ModelMessage[]
5
5
  * Filters out invalid content and ensures strict ModelMessage contract compliance
6
6
  */
7
7
  export declare function convertToModelMessages(messages: MultimodalChatMessage[]): ModelMessage[];
8
+ /**
9
+ * Resolve the replay mode for a request: the per-request option, else the
10
+ * default. The instance-level `conversationMemory.replayToolSteps` is folded
11
+ * into the option by BaseProvider before the builder runs.
12
+ */
13
+ export declare const resolveToolReplayMode: (options: {
14
+ replayToolSteps?: ToolReplayMode;
15
+ } | undefined) => ToolReplayMode;
16
+ /**
17
+ * Replay a session's tool rows into the prompt while the caller converts the
18
+ * regular rows its own way.
19
+ *
20
+ * Usage: normalise the history with `rows()`, then for each row call
21
+ * `consume(row)`; a `true` return means the row was a tool row and is now
22
+ * held. Before converting a non-tool row (and once after the last row) call
23
+ * `flush(messages, nextRole)`, which appends whatever the held tool rows
24
+ * amount to under `mode`:
25
+ *
26
+ * - `"full"`: paired tool-call / tool-result turns, one pair per step.
27
+ * - `"marker"`: markers are folded into the assistant text — returned for
28
+ * the caller to prepend when the next row is the assistant that answered
29
+ * after those tools; otherwise appended to the assistant turn just
30
+ * emitted, or emitted as a marker-only assistant turn when there is none.
31
+ * - `"off"`: dropped.
32
+ *
33
+ * The caller owns the conversion of regular rows so that the text and the
34
+ * multimodal builders keep their own (different) handling of them.
35
+ */
36
+ export declare const createToolStepReplay: (mode: ToolReplayMode) => {
37
+ rows: <TRow extends ToolHistoryRow>(rows: ReadonlyArray<TRow>) => TRow[];
38
+ consume: (row: ToolHistoryRow) => boolean;
39
+ /**
40
+ * Append the held steps to `messages`. Returns marker text the caller must
41
+ * prepend to the assistant row it is about to convert, or undefined.
42
+ */
43
+ flush: (messages: Array<ModelMessage | MultimodalChatMessage>, nextRole: string | undefined) => string | undefined;
44
+ };
8
45
  /**
9
46
  * Build a properly formatted message array for AI providers
10
47
  * Combines system prompt, conversation history, and current user prompt
@@ -43,6 +80,13 @@ export declare function processUnifiedFilesArray(options: GenerateOptions, maxSi
43
80
  * Detects when images are present and routes through provider adapter
44
81
  */
45
82
  export declare function buildMultimodalMessagesArray(options: GenerateOptions, provider: string, model: string): Promise<MultimodalChatMessage[]>;
83
+ /**
84
+ * The multimodal builder proper. Returns model messages so a session's tool
85
+ * steps can be replayed as real tool turns (`replayToolSteps: "full"`) on a
86
+ * turn that also carries files or images; `buildMultimodalMessagesArray`
87
+ * wraps this for callers on the older `MultimodalChatMessage[]` shape.
88
+ */
89
+ export declare function buildMultimodalModelMessages(options: GenerateOptions, provider: string, model: string, replayMode?: ToolReplayMode): Promise<Array<MultimodalChatMessage | ModelMessage>>;
46
90
  /**
47
91
  * Transcode any image in `input.images` that no vision provider accepts,
48
92
  * rewriting the entry in place as a PNG data URI.