@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.
- package/dist/conversion/codec-registry.js +11 -1
- package/dist/conversion/codecs/anthropic-openai-codec.d.ts +13 -0
- package/dist/conversion/codecs/anthropic-openai-codec.js +18 -473
- package/dist/conversion/codecs/gemini-openai-codec.js +91 -48
- package/dist/conversion/codecs/responses-openai-codec.js +9 -2
- package/dist/conversion/hub/format-adapters/anthropic-format-adapter.js +3 -0
- package/dist/conversion/hub/format-adapters/chat-format-adapter.js +3 -0
- package/dist/conversion/hub/format-adapters/gemini-format-adapter.js +3 -0
- package/dist/conversion/hub/format-adapters/responses-format-adapter.d.ts +19 -0
- package/dist/conversion/hub/format-adapters/responses-format-adapter.js +9 -0
- package/dist/conversion/hub/node-support.js +3 -1
- package/dist/conversion/hub/pipeline/hub-pipeline.js +37 -32
- package/dist/conversion/hub/response/provider-response.js +1 -1
- package/dist/conversion/hub/response/response-mappers.js +1 -1
- package/dist/conversion/hub/response/response-runtime.js +109 -10
- package/dist/conversion/hub/semantic-mappers/anthropic-mapper.js +70 -156
- package/dist/conversion/hub/semantic-mappers/chat-mapper.js +63 -52
- package/dist/conversion/hub/semantic-mappers/gemini-mapper.js +76 -143
- package/dist/conversion/hub/semantic-mappers/responses-mapper.js +40 -160
- package/dist/conversion/hub/standardized-bridge.js +3 -0
- package/dist/conversion/hub/tool-governance/rules.js +2 -2
- package/dist/conversion/index.d.ts +5 -0
- package/dist/conversion/index.js +5 -0
- package/dist/conversion/pipeline/codecs/v2/anthropic-openai-pipeline.d.ts +12 -0
- package/dist/conversion/pipeline/codecs/v2/anthropic-openai-pipeline.js +100 -0
- package/dist/conversion/pipeline/codecs/v2/openai-openai-pipeline.d.ts +15 -0
- package/dist/conversion/pipeline/codecs/v2/openai-openai-pipeline.js +174 -0
- package/dist/conversion/pipeline/codecs/v2/responses-openai-pipeline.d.ts +14 -0
- package/dist/conversion/pipeline/codecs/v2/responses-openai-pipeline.js +166 -0
- package/dist/conversion/pipeline/codecs/v2/shared/openai-chat-helpers.d.ts +13 -0
- package/dist/conversion/pipeline/codecs/v2/shared/openai-chat-helpers.js +66 -0
- package/dist/conversion/pipeline/hooks/adapter-context.d.ts +7 -0
- package/dist/conversion/pipeline/hooks/adapter-context.js +18 -0
- package/dist/conversion/pipeline/hooks/protocol-hooks.d.ts +67 -0
- package/dist/conversion/pipeline/hooks/protocol-hooks.js +1 -0
- package/dist/conversion/pipeline/index.d.ts +35 -0
- package/dist/conversion/pipeline/index.js +103 -0
- package/dist/conversion/pipeline/meta/meta-bag.d.ts +20 -0
- package/dist/conversion/pipeline/meta/meta-bag.js +81 -0
- package/dist/conversion/pipeline/schema/canonical-chat.d.ts +18 -0
- package/dist/conversion/pipeline/schema/canonical-chat.js +1 -0
- package/dist/conversion/pipeline/schema/index.d.ts +1 -0
- package/dist/conversion/pipeline/schema/index.js +1 -0
- package/dist/conversion/responses/responses-openai-bridge.d.ts +48 -0
- package/dist/conversion/responses/responses-openai-bridge.js +157 -1146
- package/dist/conversion/shared/anthropic-message-utils.d.ts +12 -0
- package/dist/conversion/shared/anthropic-message-utils.js +587 -0
- package/dist/conversion/shared/bridge-actions.d.ts +39 -0
- package/dist/conversion/shared/bridge-actions.js +709 -0
- package/dist/conversion/shared/bridge-conversation-store.d.ts +41 -0
- package/dist/conversion/shared/bridge-conversation-store.js +279 -0
- package/dist/conversion/shared/bridge-id-utils.d.ts +7 -0
- package/dist/conversion/shared/bridge-id-utils.js +42 -0
- package/dist/conversion/shared/bridge-instructions.d.ts +1 -0
- package/dist/conversion/shared/bridge-instructions.js +113 -0
- package/dist/conversion/shared/bridge-message-types.d.ts +39 -0
- package/dist/conversion/shared/bridge-message-types.js +1 -0
- package/dist/conversion/shared/bridge-message-utils.d.ts +22 -0
- package/dist/conversion/shared/bridge-message-utils.js +473 -0
- package/dist/conversion/shared/bridge-metadata.d.ts +1 -0
- package/dist/conversion/shared/bridge-metadata.js +1 -0
- package/dist/conversion/shared/bridge-policies.d.ts +18 -0
- package/dist/conversion/shared/bridge-policies.js +276 -0
- package/dist/conversion/shared/bridge-request-adapter.d.ts +28 -0
- package/dist/conversion/shared/bridge-request-adapter.js +430 -0
- package/dist/conversion/shared/chat-output-normalizer.d.ts +4 -0
- package/dist/conversion/shared/chat-output-normalizer.js +56 -0
- package/dist/conversion/shared/chat-request-filters.js +24 -1
- package/dist/conversion/shared/gemini-tool-utils.d.ts +5 -0
- package/dist/conversion/shared/gemini-tool-utils.js +130 -0
- package/dist/conversion/shared/metadata-passthrough.d.ts +11 -0
- package/dist/conversion/shared/metadata-passthrough.js +57 -0
- package/dist/conversion/shared/output-content-normalizer.d.ts +12 -0
- package/dist/conversion/shared/output-content-normalizer.js +119 -0
- package/dist/conversion/shared/reasoning-normalizer.d.ts +21 -0
- package/dist/conversion/shared/reasoning-normalizer.js +368 -0
- package/dist/conversion/shared/reasoning-tool-normalizer.d.ts +12 -0
- package/dist/conversion/shared/reasoning-tool-normalizer.js +132 -0
- package/dist/conversion/shared/reasoning-tool-parser.d.ts +10 -0
- package/dist/conversion/shared/reasoning-tool-parser.js +95 -0
- package/dist/conversion/shared/reasoning-utils.d.ts +2 -0
- package/dist/conversion/shared/reasoning-utils.js +42 -0
- package/dist/conversion/shared/responses-conversation-store.js +5 -11
- package/dist/conversion/shared/responses-message-utils.d.ts +15 -0
- package/dist/conversion/shared/responses-message-utils.js +206 -0
- package/dist/conversion/shared/responses-output-builder.d.ts +15 -0
- package/dist/conversion/shared/responses-output-builder.js +179 -0
- package/dist/conversion/shared/responses-output-utils.d.ts +7 -0
- package/dist/conversion/shared/responses-output-utils.js +108 -0
- package/dist/conversion/shared/responses-request-adapter.d.ts +28 -0
- package/dist/conversion/shared/responses-request-adapter.js +9 -40
- package/dist/conversion/shared/responses-response-utils.d.ts +3 -0
- package/dist/conversion/shared/responses-response-utils.js +209 -0
- package/dist/conversion/shared/responses-tool-utils.d.ts +12 -0
- package/dist/conversion/shared/responses-tool-utils.js +90 -0
- package/dist/conversion/shared/responses-types.d.ts +33 -0
- package/dist/conversion/shared/responses-types.js +1 -0
- package/dist/conversion/shared/tool-call-utils.d.ts +11 -0
- package/dist/conversion/shared/tool-call-utils.js +56 -0
- package/dist/conversion/shared/tool-governor.js +5 -0
- package/dist/conversion/shared/tool-mapping.d.ts +19 -0
- package/dist/conversion/shared/tool-mapping.js +124 -0
- package/dist/conversion/shared/tool-normalizers.d.ts +4 -0
- package/dist/conversion/shared/tool-normalizers.js +84 -0
- package/dist/router/virtual-router/bootstrap.js +18 -3
- package/dist/router/virtual-router/provider-registry.js +4 -2
- package/dist/router/virtual-router/types.d.ts +212 -0
- package/dist/sse/index.d.ts +38 -2
- package/dist/sse/index.js +27 -0
- package/dist/sse/json-to-sse/anthropic-json-to-sse-converter.d.ts +14 -0
- package/dist/sse/json-to-sse/anthropic-json-to-sse-converter.js +106 -73
- package/dist/sse/json-to-sse/chat-json-to-sse-converter.js +6 -2
- package/dist/sse/json-to-sse/gemini-json-to-sse-converter.d.ts +14 -0
- package/dist/sse/json-to-sse/gemini-json-to-sse-converter.js +99 -0
- package/dist/sse/json-to-sse/index.d.ts +7 -0
- package/dist/sse/json-to-sse/index.js +2 -0
- package/dist/sse/json-to-sse/sequencers/anthropic-sequencer.d.ts +13 -0
- package/dist/sse/json-to-sse/sequencers/anthropic-sequencer.js +150 -0
- package/dist/sse/json-to-sse/sequencers/chat-sequencer.d.ts +39 -0
- package/dist/sse/json-to-sse/sequencers/chat-sequencer.js +49 -3
- package/dist/sse/json-to-sse/sequencers/gemini-sequencer.d.ts +10 -0
- package/dist/sse/json-to-sse/sequencers/gemini-sequencer.js +95 -0
- package/dist/sse/json-to-sse/sequencers/responses-sequencer.js +31 -5
- package/dist/sse/registry/sse-codec-registry.d.ts +32 -0
- package/dist/sse/registry/sse-codec-registry.js +30 -1
- package/dist/sse/shared/reasoning-dispatcher.d.ts +10 -0
- package/dist/sse/shared/reasoning-dispatcher.js +25 -0
- package/dist/sse/shared/responses-output-normalizer.d.ts +12 -0
- package/dist/sse/shared/responses-output-normalizer.js +45 -0
- package/dist/sse/shared/serializers/anthropic-event-serializer.d.ts +2 -0
- package/dist/sse/shared/serializers/anthropic-event-serializer.js +9 -0
- package/dist/sse/shared/serializers/gemini-event-serializer.d.ts +2 -0
- package/dist/sse/shared/serializers/gemini-event-serializer.js +5 -0
- package/dist/sse/shared/serializers/index.d.ts +41 -0
- package/dist/sse/shared/serializers/index.js +2 -0
- package/dist/sse/shared/writer.d.ts +127 -0
- package/dist/sse/shared/writer.js +37 -1
- package/dist/sse/sse-to-json/anthropic-sse-to-json-converter.d.ts +11 -0
- package/dist/sse/sse-to-json/anthropic-sse-to-json-converter.js +92 -127
- package/dist/sse/sse-to-json/builders/anthropic-response-builder.d.ts +16 -0
- package/dist/sse/sse-to-json/builders/anthropic-response-builder.js +151 -0
- package/dist/sse/sse-to-json/builders/response-builder.d.ts +165 -0
- package/dist/sse/sse-to-json/builders/response-builder.js +27 -6
- package/dist/sse/sse-to-json/chat-sse-to-json-converter.d.ts +114 -0
- package/dist/sse/sse-to-json/chat-sse-to-json-converter.js +79 -3
- package/dist/sse/sse-to-json/gemini-sse-to-json-converter.d.ts +13 -0
- package/dist/sse/sse-to-json/gemini-sse-to-json-converter.js +160 -0
- package/dist/sse/sse-to-json/index.d.ts +7 -0
- package/dist/sse/sse-to-json/index.js +2 -0
- package/dist/sse/sse-to-json/parsers/sse-parser.js +53 -1
- package/dist/sse/types/anthropic-types.d.ts +170 -0
- package/dist/sse/types/anthropic-types.js +8 -5
- package/dist/sse/types/chat-types.d.ts +10 -0
- package/dist/sse/types/chat-types.js +2 -1
- package/dist/sse/types/core-interfaces.d.ts +1 -1
- package/dist/sse/types/gemini-types.d.ts +116 -0
- package/dist/sse/types/gemini-types.js +5 -0
- package/dist/sse/types/index.d.ts +5 -2
- package/dist/sse/types/index.js +2 -0
- 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);
|