@jsonstudio/llms 0.4.4 → 0.4.6

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 (160) hide show
  1. package/dist/conversion/codec-registry.js +11 -1
  2. package/dist/conversion/codecs/anthropic-openai-codec.d.ts +13 -0
  3. package/dist/conversion/codecs/anthropic-openai-codec.js +18 -473
  4. package/dist/conversion/codecs/gemini-openai-codec.js +91 -48
  5. package/dist/conversion/codecs/responses-openai-codec.js +9 -2
  6. package/dist/conversion/hub/format-adapters/anthropic-format-adapter.js +3 -0
  7. package/dist/conversion/hub/format-adapters/chat-format-adapter.js +3 -0
  8. package/dist/conversion/hub/format-adapters/gemini-format-adapter.js +3 -0
  9. package/dist/conversion/hub/format-adapters/responses-format-adapter.d.ts +19 -0
  10. package/dist/conversion/hub/format-adapters/responses-format-adapter.js +9 -0
  11. package/dist/conversion/hub/node-support.js +3 -1
  12. package/dist/conversion/hub/pipeline/hub-pipeline.js +37 -32
  13. package/dist/conversion/hub/response/provider-response.js +1 -1
  14. package/dist/conversion/hub/response/response-mappers.js +1 -1
  15. package/dist/conversion/hub/response/response-runtime.js +109 -10
  16. package/dist/conversion/hub/semantic-mappers/anthropic-mapper.js +70 -156
  17. package/dist/conversion/hub/semantic-mappers/chat-mapper.js +63 -52
  18. package/dist/conversion/hub/semantic-mappers/gemini-mapper.js +76 -143
  19. package/dist/conversion/hub/semantic-mappers/responses-mapper.js +40 -160
  20. package/dist/conversion/hub/standardized-bridge.js +3 -0
  21. package/dist/conversion/hub/tool-governance/rules.js +2 -2
  22. package/dist/conversion/index.d.ts +5 -0
  23. package/dist/conversion/index.js +5 -0
  24. package/dist/conversion/pipeline/codecs/v2/anthropic-openai-pipeline.d.ts +12 -0
  25. package/dist/conversion/pipeline/codecs/v2/anthropic-openai-pipeline.js +100 -0
  26. package/dist/conversion/pipeline/codecs/v2/openai-openai-pipeline.d.ts +15 -0
  27. package/dist/conversion/pipeline/codecs/v2/openai-openai-pipeline.js +174 -0
  28. package/dist/conversion/pipeline/codecs/v2/responses-openai-pipeline.d.ts +14 -0
  29. package/dist/conversion/pipeline/codecs/v2/responses-openai-pipeline.js +166 -0
  30. package/dist/conversion/pipeline/codecs/v2/shared/openai-chat-helpers.d.ts +13 -0
  31. package/dist/conversion/pipeline/codecs/v2/shared/openai-chat-helpers.js +66 -0
  32. package/dist/conversion/pipeline/hooks/adapter-context.d.ts +7 -0
  33. package/dist/conversion/pipeline/hooks/adapter-context.js +18 -0
  34. package/dist/conversion/pipeline/hooks/protocol-hooks.d.ts +67 -0
  35. package/dist/conversion/pipeline/hooks/protocol-hooks.js +1 -0
  36. package/dist/conversion/pipeline/index.d.ts +35 -0
  37. package/dist/conversion/pipeline/index.js +103 -0
  38. package/dist/conversion/pipeline/meta/meta-bag.d.ts +20 -0
  39. package/dist/conversion/pipeline/meta/meta-bag.js +81 -0
  40. package/dist/conversion/pipeline/schema/canonical-chat.d.ts +18 -0
  41. package/dist/conversion/pipeline/schema/canonical-chat.js +1 -0
  42. package/dist/conversion/pipeline/schema/index.d.ts +1 -0
  43. package/dist/conversion/pipeline/schema/index.js +1 -0
  44. package/dist/conversion/responses/responses-openai-bridge.d.ts +48 -0
  45. package/dist/conversion/responses/responses-openai-bridge.js +157 -1146
  46. package/dist/conversion/shared/anthropic-message-utils.d.ts +12 -0
  47. package/dist/conversion/shared/anthropic-message-utils.js +587 -0
  48. package/dist/conversion/shared/bridge-actions.d.ts +39 -0
  49. package/dist/conversion/shared/bridge-actions.js +709 -0
  50. package/dist/conversion/shared/bridge-conversation-store.d.ts +41 -0
  51. package/dist/conversion/shared/bridge-conversation-store.js +279 -0
  52. package/dist/conversion/shared/bridge-id-utils.d.ts +7 -0
  53. package/dist/conversion/shared/bridge-id-utils.js +42 -0
  54. package/dist/conversion/shared/bridge-instructions.d.ts +1 -0
  55. package/dist/conversion/shared/bridge-instructions.js +113 -0
  56. package/dist/conversion/shared/bridge-message-types.d.ts +39 -0
  57. package/dist/conversion/shared/bridge-message-types.js +1 -0
  58. package/dist/conversion/shared/bridge-message-utils.d.ts +22 -0
  59. package/dist/conversion/shared/bridge-message-utils.js +473 -0
  60. package/dist/conversion/shared/bridge-metadata.d.ts +1 -0
  61. package/dist/conversion/shared/bridge-metadata.js +1 -0
  62. package/dist/conversion/shared/bridge-policies.d.ts +18 -0
  63. package/dist/conversion/shared/bridge-policies.js +276 -0
  64. package/dist/conversion/shared/bridge-request-adapter.d.ts +28 -0
  65. package/dist/conversion/shared/bridge-request-adapter.js +430 -0
  66. package/dist/conversion/shared/chat-output-normalizer.d.ts +4 -0
  67. package/dist/conversion/shared/chat-output-normalizer.js +56 -0
  68. package/dist/conversion/shared/chat-request-filters.js +24 -1
  69. package/dist/conversion/shared/gemini-tool-utils.d.ts +5 -0
  70. package/dist/conversion/shared/gemini-tool-utils.js +130 -0
  71. package/dist/conversion/shared/metadata-passthrough.d.ts +11 -0
  72. package/dist/conversion/shared/metadata-passthrough.js +57 -0
  73. package/dist/conversion/shared/output-content-normalizer.d.ts +12 -0
  74. package/dist/conversion/shared/output-content-normalizer.js +119 -0
  75. package/dist/conversion/shared/reasoning-normalizer.d.ts +21 -0
  76. package/dist/conversion/shared/reasoning-normalizer.js +368 -0
  77. package/dist/conversion/shared/reasoning-tool-normalizer.d.ts +12 -0
  78. package/dist/conversion/shared/reasoning-tool-normalizer.js +132 -0
  79. package/dist/conversion/shared/reasoning-tool-parser.d.ts +10 -0
  80. package/dist/conversion/shared/reasoning-tool-parser.js +95 -0
  81. package/dist/conversion/shared/reasoning-utils.d.ts +2 -0
  82. package/dist/conversion/shared/reasoning-utils.js +42 -0
  83. package/dist/conversion/shared/responses-conversation-store.js +5 -11
  84. package/dist/conversion/shared/responses-message-utils.d.ts +15 -0
  85. package/dist/conversion/shared/responses-message-utils.js +206 -0
  86. package/dist/conversion/shared/responses-output-builder.d.ts +15 -0
  87. package/dist/conversion/shared/responses-output-builder.js +179 -0
  88. package/dist/conversion/shared/responses-output-utils.d.ts +7 -0
  89. package/dist/conversion/shared/responses-output-utils.js +108 -0
  90. package/dist/conversion/shared/responses-request-adapter.d.ts +28 -0
  91. package/dist/conversion/shared/responses-request-adapter.js +9 -40
  92. package/dist/conversion/shared/responses-response-utils.d.ts +3 -0
  93. package/dist/conversion/shared/responses-response-utils.js +209 -0
  94. package/dist/conversion/shared/responses-tool-utils.d.ts +12 -0
  95. package/dist/conversion/shared/responses-tool-utils.js +90 -0
  96. package/dist/conversion/shared/responses-types.d.ts +33 -0
  97. package/dist/conversion/shared/responses-types.js +1 -0
  98. package/dist/conversion/shared/tool-call-utils.d.ts +11 -0
  99. package/dist/conversion/shared/tool-call-utils.js +56 -0
  100. package/dist/conversion/shared/tool-governor.js +5 -0
  101. package/dist/conversion/shared/tool-mapping.d.ts +19 -0
  102. package/dist/conversion/shared/tool-mapping.js +124 -0
  103. package/dist/conversion/shared/tool-normalizers.d.ts +4 -0
  104. package/dist/conversion/shared/tool-normalizers.js +84 -0
  105. package/dist/router/virtual-router/bootstrap.js +18 -3
  106. package/dist/router/virtual-router/provider-registry.js +4 -2
  107. package/dist/router/virtual-router/types.d.ts +212 -0
  108. package/dist/sse/index.d.ts +38 -2
  109. package/dist/sse/index.js +27 -0
  110. package/dist/sse/json-to-sse/anthropic-json-to-sse-converter.d.ts +14 -0
  111. package/dist/sse/json-to-sse/anthropic-json-to-sse-converter.js +106 -73
  112. package/dist/sse/json-to-sse/chat-json-to-sse-converter.js +6 -2
  113. package/dist/sse/json-to-sse/gemini-json-to-sse-converter.d.ts +14 -0
  114. package/dist/sse/json-to-sse/gemini-json-to-sse-converter.js +99 -0
  115. package/dist/sse/json-to-sse/index.d.ts +7 -0
  116. package/dist/sse/json-to-sse/index.js +2 -0
  117. package/dist/sse/json-to-sse/sequencers/anthropic-sequencer.d.ts +13 -0
  118. package/dist/sse/json-to-sse/sequencers/anthropic-sequencer.js +150 -0
  119. package/dist/sse/json-to-sse/sequencers/chat-sequencer.d.ts +39 -0
  120. package/dist/sse/json-to-sse/sequencers/chat-sequencer.js +49 -3
  121. package/dist/sse/json-to-sse/sequencers/gemini-sequencer.d.ts +10 -0
  122. package/dist/sse/json-to-sse/sequencers/gemini-sequencer.js +95 -0
  123. package/dist/sse/json-to-sse/sequencers/responses-sequencer.js +31 -5
  124. package/dist/sse/registry/sse-codec-registry.d.ts +32 -0
  125. package/dist/sse/registry/sse-codec-registry.js +30 -1
  126. package/dist/sse/shared/reasoning-dispatcher.d.ts +10 -0
  127. package/dist/sse/shared/reasoning-dispatcher.js +25 -0
  128. package/dist/sse/shared/responses-output-normalizer.d.ts +12 -0
  129. package/dist/sse/shared/responses-output-normalizer.js +45 -0
  130. package/dist/sse/shared/serializers/anthropic-event-serializer.d.ts +2 -0
  131. package/dist/sse/shared/serializers/anthropic-event-serializer.js +9 -0
  132. package/dist/sse/shared/serializers/gemini-event-serializer.d.ts +2 -0
  133. package/dist/sse/shared/serializers/gemini-event-serializer.js +5 -0
  134. package/dist/sse/shared/serializers/index.d.ts +41 -0
  135. package/dist/sse/shared/serializers/index.js +2 -0
  136. package/dist/sse/shared/writer.d.ts +127 -0
  137. package/dist/sse/shared/writer.js +37 -1
  138. package/dist/sse/sse-to-json/anthropic-sse-to-json-converter.d.ts +11 -0
  139. package/dist/sse/sse-to-json/anthropic-sse-to-json-converter.js +92 -127
  140. package/dist/sse/sse-to-json/builders/anthropic-response-builder.d.ts +16 -0
  141. package/dist/sse/sse-to-json/builders/anthropic-response-builder.js +151 -0
  142. package/dist/sse/sse-to-json/builders/response-builder.d.ts +165 -0
  143. package/dist/sse/sse-to-json/builders/response-builder.js +27 -6
  144. package/dist/sse/sse-to-json/chat-sse-to-json-converter.d.ts +114 -0
  145. package/dist/sse/sse-to-json/chat-sse-to-json-converter.js +79 -3
  146. package/dist/sse/sse-to-json/gemini-sse-to-json-converter.d.ts +13 -0
  147. package/dist/sse/sse-to-json/gemini-sse-to-json-converter.js +160 -0
  148. package/dist/sse/sse-to-json/index.d.ts +7 -0
  149. package/dist/sse/sse-to-json/index.js +2 -0
  150. package/dist/sse/sse-to-json/parsers/sse-parser.js +53 -1
  151. package/dist/sse/types/anthropic-types.d.ts +170 -0
  152. package/dist/sse/types/anthropic-types.js +8 -5
  153. package/dist/sse/types/chat-types.d.ts +10 -0
  154. package/dist/sse/types/chat-types.js +2 -1
  155. package/dist/sse/types/core-interfaces.d.ts +1 -1
  156. package/dist/sse/types/gemini-types.d.ts +116 -0
  157. package/dist/sse/types/gemini-types.js +5 -0
  158. package/dist/sse/types/index.d.ts +5 -2
  159. package/dist/sse/types/index.js +2 -0
  160. package/package.json +1 -1
@@ -0,0 +1,709 @@
1
+ import { normalizeMessageReasoningTools } from './reasoning-tool-normalizer.js';
2
+ import { extractReasoningSegments } from './reasoning-utils.js';
3
+ import { extractOutputSegments } from './output-content-normalizer.js';
4
+ import { normalizeChatMessageContent } from './chat-output-normalizer.js';
5
+ import { convertMessagesToBridgeInput } from './bridge-message-utils.js';
6
+ const registry = new Map();
7
+ export function registerBridgeAction(name, action) {
8
+ registry.set(name, action);
9
+ }
10
+ export function createBridgeActionState(seed) {
11
+ const state = {
12
+ messages: Array.isArray(seed?.messages) ? seed.messages : []
13
+ };
14
+ if (seed?.requiredAction)
15
+ state.requiredAction = seed.requiredAction;
16
+ if (seed?.capturedToolResults)
17
+ state.capturedToolResults = seed.capturedToolResults;
18
+ if (seed?.rawRequest)
19
+ state.rawRequest = seed.rawRequest;
20
+ if (seed?.rawResponse)
21
+ state.rawResponse = seed.rawResponse;
22
+ if (seed?.metadata)
23
+ state.metadata = seed.metadata;
24
+ return state;
25
+ }
26
+ export function runBridgeActionPipeline(options) {
27
+ const { stage, actions, protocol, moduleType, requestId, state } = options;
28
+ if (!actions?.length)
29
+ return;
30
+ for (const descriptor of actions) {
31
+ if (!descriptor || typeof descriptor !== 'object')
32
+ continue;
33
+ const action = registry.get(descriptor.name);
34
+ if (!action)
35
+ continue;
36
+ try {
37
+ action({
38
+ stage,
39
+ protocol,
40
+ moduleType,
41
+ requestId,
42
+ descriptor,
43
+ state
44
+ });
45
+ }
46
+ catch {
47
+ // Ignore action failures to preserve core flow; telemetry hooks can be added later.
48
+ }
49
+ }
50
+ }
51
+ function ensureMessagesArray(state) {
52
+ if (!Array.isArray(state.messages)) {
53
+ state.messages = [];
54
+ }
55
+ return state.messages;
56
+ }
57
+ function flattenContentToString(value) {
58
+ if (typeof value === 'string') {
59
+ return value;
60
+ }
61
+ if (Array.isArray(value)) {
62
+ const parts = value
63
+ .map((entry) => flattenContentToString(entry))
64
+ .filter((entry) => typeof entry === 'string' && entry.length > 0);
65
+ if (parts.length) {
66
+ return parts.join('\n');
67
+ }
68
+ return '';
69
+ }
70
+ if (value && typeof value === 'object') {
71
+ const record = value;
72
+ if (typeof record.text === 'string') {
73
+ return record.text;
74
+ }
75
+ if (typeof record.content === 'string') {
76
+ return record.content;
77
+ }
78
+ if (Array.isArray(record.content)) {
79
+ return flattenContentToString(record.content);
80
+ }
81
+ }
82
+ return undefined;
83
+ }
84
+ function ensureMetadataRecord(state) {
85
+ if (!state.metadata || typeof state.metadata !== 'object') {
86
+ state.metadata = {};
87
+ }
88
+ return state.metadata;
89
+ }
90
+ const injectSystemInstructionAction = (ctx) => {
91
+ if (ctx.stage !== 'request_inbound')
92
+ return;
93
+ const raw = ctx.state.rawRequest;
94
+ if (!raw || typeof raw !== 'object')
95
+ return;
96
+ const field = typeof ctx.descriptor.options?.field === 'string' ? ctx.descriptor.options.field : 'instructions';
97
+ const reasoningField = typeof ctx.descriptor.options?.reasoningField === 'string' ? ctx.descriptor.options.reasoningField : undefined;
98
+ const instructions = readInstructionValue(raw, field);
99
+ if (!instructions)
100
+ return;
101
+ const reasoningSegments = readInstructionReasoning(raw, reasoningField);
102
+ const message = buildInstructionMessage('system', instructions, reasoningSegments);
103
+ if (!message)
104
+ return;
105
+ const messages = ensureMessagesArray(ctx.state);
106
+ messages.unshift(message);
107
+ };
108
+ function readInstructionValue(source, field) {
109
+ if (!source || typeof source !== 'object')
110
+ return undefined;
111
+ const raw = source[field];
112
+ if (typeof raw !== 'string')
113
+ return undefined;
114
+ const trimmed = raw.trim();
115
+ return trimmed.length ? trimmed : undefined;
116
+ }
117
+ function readInstructionReasoning(source, field) {
118
+ if (!field || !source || typeof source !== 'object')
119
+ return [];
120
+ const raw = source[field];
121
+ if (!raw)
122
+ return [];
123
+ if (typeof raw === 'string') {
124
+ const trimmed = raw.trim();
125
+ return trimmed.length ? [trimmed] : [];
126
+ }
127
+ if (Array.isArray(raw)) {
128
+ return raw
129
+ .map((entry) => (typeof entry === 'string' ? entry.trim() : ''))
130
+ .filter((entry) => entry.length > 0);
131
+ }
132
+ return [];
133
+ }
134
+ function buildInstructionMessage(role, text, reasoningSegments) {
135
+ const normalized = normalizeChatMessageContent(text);
136
+ const content = typeof normalized.contentText === 'string' ? normalized.contentText : '';
137
+ const merged = [];
138
+ if (typeof normalized.reasoningText === 'string' && normalized.reasoningText.trim().length) {
139
+ merged.push(normalized.reasoningText.trim());
140
+ }
141
+ for (const segment of reasoningSegments) {
142
+ if (typeof segment === 'string' && segment.trim().length) {
143
+ merged.push(segment.trim());
144
+ }
145
+ }
146
+ if (!content.trim().length && !merged.length) {
147
+ return null;
148
+ }
149
+ const message = { role, content };
150
+ if (merged.length) {
151
+ message.reasoning_content = merged.join('\n');
152
+ }
153
+ return message;
154
+ }
155
+ function mapToolResults(state) {
156
+ const toolResults = new Map();
157
+ const append = (id, output, name) => {
158
+ if (typeof id !== 'string' || !id.trim()) {
159
+ return;
160
+ }
161
+ let text = '';
162
+ if (typeof output === 'string') {
163
+ text = output;
164
+ }
165
+ else if (output != null) {
166
+ try {
167
+ text = JSON.stringify(output);
168
+ }
169
+ catch {
170
+ text = String(output);
171
+ }
172
+ }
173
+ const trimmed = text.trim();
174
+ toolResults.set(id, trimmed);
175
+ if (typeof name === 'string' && name.trim().length) {
176
+ toolResults.set(`${id}::name`, name.trim());
177
+ }
178
+ };
179
+ const captured = state.capturedToolResults;
180
+ if (Array.isArray(captured)) {
181
+ for (const entry of captured) {
182
+ append(entry?.tool_call_id ?? entry?.call_id, entry?.output, entry?.name);
183
+ }
184
+ }
185
+ return toolResults;
186
+ }
187
+ function hasToolResponse(messages, startIndex, callId) {
188
+ for (let i = startIndex; i < messages.length; i++) {
189
+ const msg = messages[i];
190
+ if (!msg || typeof msg !== 'object')
191
+ continue;
192
+ const role = String(msg.role || '').toLowerCase();
193
+ if (role !== 'tool')
194
+ continue;
195
+ const id = msg.tool_call_id ||
196
+ msg.call_id ||
197
+ msg.tool_use_id;
198
+ if (typeof id === 'string' && id.trim() === callId) {
199
+ return true;
200
+ }
201
+ }
202
+ return false;
203
+ }
204
+ function normalizeToolName(value) {
205
+ if (typeof value === 'string' && value.trim().length) {
206
+ return value.trim();
207
+ }
208
+ return undefined;
209
+ }
210
+ const ensureToolResponsePlaceholders = (ctx) => {
211
+ const messages = ensureMessagesArray(ctx.state);
212
+ if (!messages.length)
213
+ return;
214
+ const inserts = [];
215
+ const toolResults = mapToolResults(ctx.state);
216
+ for (let i = 0; i < messages.length; i++) {
217
+ const message = messages[i];
218
+ if (!message || typeof message !== 'object')
219
+ continue;
220
+ const role = String(message.role || '').toLowerCase();
221
+ if (role !== 'assistant')
222
+ continue;
223
+ const toolCalls = Array.isArray(message.tool_calls) ? message.tool_calls : undefined;
224
+ if (!toolCalls?.length)
225
+ continue;
226
+ for (const call of toolCalls) {
227
+ if (!call || typeof call !== 'object')
228
+ continue;
229
+ const callId = call.id ||
230
+ call.call_id ||
231
+ call.tool_call_id;
232
+ if (typeof callId !== 'string' || !callId.trim())
233
+ continue;
234
+ if (hasToolResponse(messages, i + 1, callId))
235
+ continue;
236
+ const content = toolResults.get(callId) ?? '';
237
+ const inferredName = toolResults.get(`${callId}::name`) ||
238
+ normalizeToolName(call.name) ||
239
+ normalizeToolName(call?.function?.name);
240
+ const toolMessage = {
241
+ role: 'tool',
242
+ tool_call_id: callId,
243
+ content
244
+ };
245
+ if (inferredName) {
246
+ toolMessage.name = inferredName;
247
+ }
248
+ inserts.push({ index: i + 1, message: toolMessage });
249
+ }
250
+ }
251
+ if (!inserts.length)
252
+ return;
253
+ let offset = 0;
254
+ for (const entry of inserts) {
255
+ const targetIndex = Math.min(messages.length, entry.index + offset);
256
+ messages.splice(targetIndex, 0, entry.message);
257
+ offset++;
258
+ }
259
+ };
260
+ function assignReasoning(message, parts) {
261
+ const trimmed = parts.map((item) => item.trim()).filter(Boolean);
262
+ if (!trimmed.length)
263
+ return;
264
+ const existing = typeof message.reasoning_content === 'string' && message.reasoning_content.trim().length
265
+ ? [message.reasoning_content.trim()]
266
+ : [];
267
+ const merged = [...existing, ...trimmed].filter(Boolean);
268
+ if (merged.length) {
269
+ message.reasoning_content = merged.join('\n').trim();
270
+ }
271
+ }
272
+ function processTextValue(value, collector, dropFromContent) {
273
+ try {
274
+ const sanitized = extractReasoningSegments(value, collector);
275
+ return dropFromContent ? sanitized : value;
276
+ }
277
+ catch {
278
+ return value;
279
+ }
280
+ }
281
+ function stripReasoningFromValue(value, collector, dropFromContent) {
282
+ if (typeof value === 'string') {
283
+ return processTextValue(value, collector, dropFromContent);
284
+ }
285
+ if (Array.isArray(value)) {
286
+ for (let i = 0; i < value.length; i++) {
287
+ value[i] = stripReasoningFromValue(value[i], collector, dropFromContent);
288
+ }
289
+ return value;
290
+ }
291
+ if (value && typeof value === 'object') {
292
+ const record = value;
293
+ if (typeof record.text === 'string') {
294
+ record.text = processTextValue(record.text, collector, dropFromContent);
295
+ }
296
+ if (typeof record.content === 'string') {
297
+ record.content = processTextValue(record.content, collector, dropFromContent);
298
+ }
299
+ else if (Array.isArray(record.content)) {
300
+ record.content = record.content.map((part) => stripReasoningFromValue(part, collector, dropFromContent));
301
+ }
302
+ return value;
303
+ }
304
+ return value;
305
+ }
306
+ const extractReasoningAction = (ctx) => {
307
+ const messages = ensureMessagesArray(ctx.state);
308
+ if (!messages.length)
309
+ return;
310
+ const dropFromContent = ctx.descriptor.options?.dropFromContent !== false;
311
+ const idPrefixBase = typeof ctx.descriptor.options?.idPrefix === 'string' && ctx.descriptor.options.idPrefix.trim().length
312
+ ? ctx.descriptor.options.idPrefix.trim()
313
+ : `${ctx.protocol ?? 'bridge'}_${ctx.stage}`;
314
+ for (let i = 0; i < messages.length; i++) {
315
+ const message = messages[i];
316
+ if (!message || typeof message !== 'object')
317
+ continue;
318
+ const reasoningParts = [];
319
+ if (typeof message.content === 'string') {
320
+ message.content = processTextValue(message.content, reasoningParts, dropFromContent);
321
+ }
322
+ else if (Array.isArray(message.content)) {
323
+ message.content = message.content.map((part) => stripReasoningFromValue(part, reasoningParts, dropFromContent));
324
+ }
325
+ assignReasoning(message, reasoningParts);
326
+ try {
327
+ normalizeMessageReasoningTools(message, {
328
+ idPrefix: `${idPrefixBase}_${i + 1}`
329
+ });
330
+ }
331
+ catch {
332
+ // best-effort normalization; ignore failures
333
+ }
334
+ }
335
+ };
336
+ registerBridgeAction('messages.inject-system-instruction', injectSystemInstructionAction);
337
+ registerBridgeAction('reasoning.extract', extractReasoningAction);
338
+ registerBridgeAction('tools.ensure-response-placeholders', ensureToolResponsePlaceholders);
339
+ const ensureSystemInstructionAction = (ctx) => {
340
+ const messages = ensureMessagesArray(ctx.state);
341
+ if (!messages.length) {
342
+ return;
343
+ }
344
+ if (ctx.stage === 'request_outbound') {
345
+ const originals = [];
346
+ for (const message of messages) {
347
+ if (!message || typeof message !== 'object')
348
+ continue;
349
+ const role = String(message.role || '').toLowerCase();
350
+ if (role !== 'system')
351
+ continue;
352
+ const text = flattenContentToString(message.content);
353
+ if (typeof text === 'string' && text.trim().length) {
354
+ originals.push(text.trim());
355
+ }
356
+ }
357
+ if (!originals.length) {
358
+ return;
359
+ }
360
+ const metadata = ensureMetadataRecord(ctx.state);
361
+ metadata.systemInstruction = originals.join('\n\n');
362
+ metadata.originalSystemMessages = originals.slice();
363
+ return;
364
+ }
365
+ if (ctx.stage === 'request_inbound') {
366
+ const metadata = ctx.state.metadata;
367
+ const systemInstruction = typeof metadata?.systemInstruction === 'string' && metadata.systemInstruction.trim().length
368
+ ? metadata.systemInstruction.trim()
369
+ : undefined;
370
+ if (!systemInstruction) {
371
+ return;
372
+ }
373
+ const hasSystem = messages.some((message) => {
374
+ if (!message || typeof message !== 'object')
375
+ return false;
376
+ return String(message.role || '').toLowerCase() === 'system';
377
+ });
378
+ if (hasSystem) {
379
+ return;
380
+ }
381
+ messages.unshift({
382
+ role: 'system',
383
+ content: systemInstruction
384
+ });
385
+ }
386
+ };
387
+ const normalizeHistoryAction = (ctx) => {
388
+ if (ctx.stage !== 'request_outbound') {
389
+ return;
390
+ }
391
+ const messages = ensureMessagesArray(ctx.state);
392
+ if (!messages.length) {
393
+ return;
394
+ }
395
+ const toolsNode = ctx.state.rawRequest &&
396
+ typeof ctx.state.rawRequest === 'object' &&
397
+ Array.isArray(ctx.state.rawRequest.tools)
398
+ ? ctx.state.rawRequest.tools
399
+ : undefined;
400
+ try {
401
+ const result = convertMessagesToBridgeInput({
402
+ messages,
403
+ tools: toolsNode
404
+ });
405
+ const metadata = ensureMetadataRecord(ctx.state);
406
+ metadata.bridgeHistory = result;
407
+ }
408
+ catch {
409
+ // normalization best-effort
410
+ }
411
+ };
412
+ const ensureOutputFieldsAction = (ctx) => {
413
+ if (ctx.stage !== 'request_outbound') {
414
+ return;
415
+ }
416
+ const messages = ensureMessagesArray(ctx.state);
417
+ if (!messages.length) {
418
+ return;
419
+ }
420
+ const fallback = typeof ctx.descriptor.options?.toolFallback === 'string' && ctx.descriptor.options.toolFallback.trim().length
421
+ ? ctx.descriptor.options.toolFallback.trim()
422
+ : 'Tool call completed (no output).';
423
+ for (const message of messages) {
424
+ if (!message || typeof message !== 'object') {
425
+ continue;
426
+ }
427
+ const role = String(message.role || '').toLowerCase();
428
+ if (role === 'tool') {
429
+ let text = flattenContentToString(message.content);
430
+ if (text === undefined) {
431
+ text = fallback;
432
+ }
433
+ message.content = text;
434
+ continue;
435
+ }
436
+ if (role === 'assistant') {
437
+ const text = flattenContentToString(message.content);
438
+ if (text === undefined) {
439
+ message.content = '';
440
+ }
441
+ else if (typeof message.content !== 'string') {
442
+ message.content = text;
443
+ }
444
+ }
445
+ }
446
+ };
447
+ const ensureToolPlaceholdersAction = (ctx) => {
448
+ ensureToolResponsePlaceholders(ctx);
449
+ };
450
+ const captureToolResultsAction = (ctx) => {
451
+ const source = ctx.stage.startsWith('request') ? ctx.state.rawRequest :
452
+ ctx.stage.startsWith('response') ? ctx.state.rawResponse :
453
+ undefined;
454
+ if ((!Array.isArray(ctx.state.capturedToolResults) || ctx.state.capturedToolResults.length === 0) &&
455
+ source &&
456
+ typeof source === 'object' &&
457
+ Array.isArray(source.tool_outputs)) {
458
+ ctx.state.capturedToolResults = source.tool_outputs.map((entry) => ({
459
+ tool_call_id: typeof entry?.tool_call_id === 'string' ? entry.tool_call_id : undefined,
460
+ call_id: typeof entry?.call_id === 'string' ? entry.call_id : undefined,
461
+ output: entry?.output,
462
+ name: typeof entry?.name === 'string' ? entry.name : undefined
463
+ }));
464
+ }
465
+ if (ctx.stage === 'request_outbound' &&
466
+ Array.isArray(ctx.state.capturedToolResults) &&
467
+ ctx.state.capturedToolResults.length) {
468
+ const metadata = ensureMetadataRecord(ctx.state);
469
+ metadata.capturedToolResults = ctx.state.capturedToolResults.map((entry) => ({ ...entry }));
470
+ }
471
+ };
472
+ const attachReasoningOutputAction = (ctx) => {
473
+ responsesOutputReasoningAction(ctx);
474
+ };
475
+ registerBridgeAction('messages.ensure-system-instruction', ensureSystemInstructionAction);
476
+ registerBridgeAction('messages.normalize-history', normalizeHistoryAction);
477
+ registerBridgeAction('messages.ensure-output-fields', ensureOutputFieldsAction);
478
+ registerBridgeAction('tools.ensure-placeholders', ensureToolPlaceholdersAction);
479
+ registerBridgeAction('tools.capture-results', captureToolResultsAction);
480
+ registerBridgeAction('reasoning.attach-output', attachReasoningOutputAction);
481
+ const responsesOutputReasoningAction = (ctx) => {
482
+ if (ctx.stage !== 'response_inbound')
483
+ return;
484
+ const raw = ctx.state.rawResponse;
485
+ if (!raw || typeof raw !== 'object')
486
+ return;
487
+ const { textParts, reasoningParts } = extractOutputSegments(raw);
488
+ if (!textParts.length && !reasoningParts.length)
489
+ return;
490
+ const messages = ensureMessagesArray(ctx.state);
491
+ if (!messages.length) {
492
+ messages.push({
493
+ role: 'assistant',
494
+ content: textParts.join('\n')
495
+ });
496
+ }
497
+ const target = messages.find((message) => {
498
+ if (!message || typeof message !== 'object')
499
+ return false;
500
+ const role = String(message.role || '').toLowerCase();
501
+ return role === 'assistant';
502
+ }) ?? messages[0];
503
+ if (!target || typeof target !== 'object')
504
+ return;
505
+ if (textParts.length) {
506
+ const combinedText = textParts.join('\n');
507
+ const existing = target.content;
508
+ if (typeof existing !== 'string' || !existing.trim().length) {
509
+ target.content = combinedText;
510
+ }
511
+ }
512
+ if (reasoningParts.length) {
513
+ assignReasoning(target, reasoningParts);
514
+ }
515
+ try {
516
+ normalizeMessageReasoningTools(target, {
517
+ idPrefix: `${ctx.protocol ?? 'responses'}_${ctx.stage}_output`
518
+ });
519
+ }
520
+ catch {
521
+ // best-effort normalization
522
+ }
523
+ };
524
+ registerBridgeAction('responses.output-reasoning', responsesOutputReasoningAction);
525
+ function collectExtraFieldsMap(source, allowed) {
526
+ const extras = {};
527
+ for (const [key, value] of Object.entries(source)) {
528
+ if (allowed && allowed.has(key))
529
+ continue;
530
+ extras[key] = value;
531
+ }
532
+ if (Object.keys(extras).length === 0) {
533
+ return undefined;
534
+ }
535
+ return extras;
536
+ }
537
+ const metadataExtraFieldsAction = (ctx) => {
538
+ const allowedKeysRaw = ctx.descriptor.options?.allowedKeys;
539
+ const allowed = Array.isArray(allowedKeysRaw)
540
+ ? new Set(allowedKeysRaw
541
+ .map((key) => (typeof key === 'string' ? key : undefined))
542
+ .filter((key) => !!key))
543
+ : undefined;
544
+ const target = ctx.stage.startsWith('request') ? ctx.state.rawRequest : ctx.state.rawResponse;
545
+ if (!target || typeof target !== 'object')
546
+ return;
547
+ if (ctx.stage.endsWith('inbound')) {
548
+ const extras = collectExtraFieldsMap(target, allowed);
549
+ if (!extras)
550
+ return;
551
+ const metadata = ensureMetadataRecord(ctx.state);
552
+ const existing = metadata.extraFields && typeof metadata.extraFields === 'object'
553
+ ? metadata.extraFields
554
+ : undefined;
555
+ metadata.extraFields = existing ? { ...existing, ...extras } : extras;
556
+ return;
557
+ }
558
+ const metadata = ctx.state.metadata;
559
+ if (!metadata || typeof metadata !== 'object')
560
+ return;
561
+ const extras = metadata.extraFields;
562
+ if (!extras || typeof extras !== 'object')
563
+ return;
564
+ const source = extras;
565
+ for (const [key, value] of Object.entries(source)) {
566
+ if (target[key] === undefined) {
567
+ target[key] = value;
568
+ }
569
+ }
570
+ };
571
+ const metadataProviderFieldAction = (ctx) => {
572
+ const field = typeof ctx.descriptor.options?.field === 'string' ? ctx.descriptor.options.field : 'metadata';
573
+ const targetKey = typeof ctx.descriptor.options?.target === 'string' ? ctx.descriptor.options.target : 'providerMetadata';
574
+ const payload = ctx.stage.startsWith('request') ? ctx.state.rawRequest :
575
+ ctx.stage.startsWith('response') ? ctx.state.rawResponse :
576
+ undefined;
577
+ if (!payload || typeof payload !== 'object')
578
+ return;
579
+ if (ctx.stage.endsWith('inbound')) {
580
+ const value = payload[field];
581
+ if (!value || typeof value !== 'object')
582
+ return;
583
+ const metadata = ensureMetadataRecord(ctx.state);
584
+ metadata[targetKey] = value;
585
+ return;
586
+ }
587
+ const metadata = ctx.state.metadata;
588
+ if (!metadata || typeof metadata !== 'object')
589
+ return;
590
+ const provider = metadata[targetKey];
591
+ if (!provider || typeof provider !== 'object')
592
+ return;
593
+ if (payload[field] === undefined) {
594
+ payload[field] = provider;
595
+ }
596
+ };
597
+ const metadataProviderSentinelAction = (ctx) => {
598
+ const sentinel = typeof ctx.descriptor.options?.sentinel === 'string'
599
+ ? ctx.descriptor.options.sentinel
600
+ : undefined;
601
+ if (!sentinel)
602
+ return;
603
+ const targetKey = typeof ctx.descriptor.options?.target === 'string'
604
+ ? ctx.descriptor.options.target
605
+ : 'providerMetadata';
606
+ const payload = ctx.stage.startsWith('request') ? ctx.state.rawRequest :
607
+ ctx.stage.startsWith('response') ? ctx.state.rawResponse :
608
+ undefined;
609
+ if (!payload || typeof payload !== 'object')
610
+ return;
611
+ if (ctx.stage.endsWith('inbound')) {
612
+ const rawValue = payload[sentinel];
613
+ if (rawValue === undefined)
614
+ return;
615
+ let provider;
616
+ if (typeof rawValue === 'string') {
617
+ try {
618
+ const parsed = JSON.parse(rawValue);
619
+ if (parsed && typeof parsed === 'object')
620
+ provider = parsed;
621
+ }
622
+ catch {
623
+ provider = undefined;
624
+ }
625
+ }
626
+ else if (rawValue && typeof rawValue === 'object') {
627
+ provider = rawValue;
628
+ }
629
+ delete payload[sentinel];
630
+ if (!provider)
631
+ return;
632
+ const metadata = ensureMetadataRecord(ctx.state);
633
+ metadata[targetKey] = provider;
634
+ return;
635
+ }
636
+ const metadata = ctx.state.metadata;
637
+ if (!metadata || typeof metadata !== 'object') {
638
+ delete payload[sentinel];
639
+ return;
640
+ }
641
+ const provider = metadata[targetKey];
642
+ if (!provider || typeof provider !== 'object') {
643
+ delete payload[sentinel];
644
+ return;
645
+ }
646
+ try {
647
+ payload[sentinel] = JSON.stringify(provider);
648
+ }
649
+ catch {
650
+ payload[sentinel] = provider;
651
+ }
652
+ };
653
+ registerBridgeAction('metadata.provider-field', metadataProviderFieldAction);
654
+ registerBridgeAction('metadata.provider-sentinel', metadataProviderSentinelAction);
655
+ const metadataSystemSentinelAction = (ctx) => {
656
+ const sentinel = typeof ctx.descriptor.options?.sentinel === 'string'
657
+ ? ctx.descriptor.options.sentinel
658
+ : undefined;
659
+ const targetKey = typeof ctx.descriptor.options?.target === 'string'
660
+ ? ctx.descriptor.options.target
661
+ : 'rawSystem';
662
+ const stringifyOutbound = ctx.descriptor.options?.stringify !== false;
663
+ if (!sentinel)
664
+ return;
665
+ const payload = ctx.stage.startsWith('request') ? ctx.state.rawRequest :
666
+ ctx.stage.startsWith('response') ? ctx.state.rawResponse :
667
+ undefined;
668
+ if (!payload || typeof payload !== 'object')
669
+ return;
670
+ if (ctx.stage.endsWith('inbound')) {
671
+ const rawValue = payload[sentinel];
672
+ if (rawValue === undefined)
673
+ return;
674
+ let parsed = rawValue;
675
+ if (typeof rawValue === 'string') {
676
+ try {
677
+ parsed = JSON.parse(rawValue);
678
+ }
679
+ catch {
680
+ parsed = rawValue;
681
+ }
682
+ }
683
+ delete payload[sentinel];
684
+ const metadata = ensureMetadataRecord(ctx.state);
685
+ metadata[targetKey] = parsed;
686
+ return;
687
+ }
688
+ const metadata = ctx.state.metadata;
689
+ if (!metadata || typeof metadata !== 'object') {
690
+ delete payload[sentinel];
691
+ return;
692
+ }
693
+ const value = metadata[targetKey];
694
+ if (value === undefined) {
695
+ delete payload[sentinel];
696
+ return;
697
+ }
698
+ if (stringifyOutbound) {
699
+ try {
700
+ payload[sentinel] = JSON.stringify(value);
701
+ }
702
+ catch {
703
+ payload[sentinel] = value;
704
+ }
705
+ return;
706
+ }
707
+ payload[sentinel] = value;
708
+ };
709
+ registerBridgeAction('metadata.system-sentinel', metadataSystemSentinelAction);