@jsonstudio/llms 0.6.795 → 0.6.938

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 (184) hide show
  1. package/dist/bridge/routecodex-adapter.d.ts +74 -0
  2. package/dist/config-unified/enhanced-path-resolver.d.ts +5 -0
  3. package/dist/config-unified/unified-config.d.ts +26 -0
  4. package/dist/conversion/codec-registry.d.ts +10 -0
  5. package/dist/conversion/codecs/gemini-openai-codec.d.ts +16 -0
  6. package/dist/conversion/codecs/openai-openai-codec.d.ts +12 -0
  7. package/dist/conversion/codecs/responses-openai-codec.d.ts +12 -0
  8. package/dist/conversion/compat/profiles/chat-gemini.json +12 -0
  9. package/dist/conversion/config/config-manager.d.ts +212 -0
  10. package/dist/conversion/hub/config/types.d.ts +26 -0
  11. package/dist/conversion/hub/core/detour-registry.d.ts +9 -0
  12. package/dist/conversion/hub/core/hub-context.d.ts +21 -0
  13. package/dist/conversion/hub/core/index.d.ts +3 -0
  14. package/dist/conversion/hub/core/stage-driver.d.ts +30 -0
  15. package/dist/conversion/hub/format-adapters/anthropic-format-adapter.d.ts +16 -0
  16. package/dist/conversion/hub/format-adapters/chat-format-adapter.d.ts +17 -0
  17. package/dist/conversion/hub/format-adapters/gemini-format-adapter.d.ts +16 -0
  18. package/dist/conversion/hub/format-adapters/index.d.ts +21 -0
  19. package/dist/conversion/hub/hub-feature.d.ts +1 -0
  20. package/dist/conversion/hub/node-support.d.ts +19 -0
  21. package/dist/conversion/hub/pipeline/compat/compat-pipeline-executor.js +11 -0
  22. package/dist/conversion/hub/pipeline/compat/compat-types.d.ts +3 -0
  23. package/dist/conversion/hub/pipeline/hub-pipeline.d.ts +7 -0
  24. package/dist/conversion/hub/pipeline/hub-pipeline.js +71 -14
  25. package/dist/conversion/hub/pipeline/stages/req_process/req_process_stage1_tool_governance/index.js +4 -0
  26. package/dist/conversion/hub/pipeline/stages/resp_outbound/resp_outbound_stage1_client_remap/index.js +23 -1
  27. package/dist/conversion/hub/pipelines/inbound.d.ts +22 -0
  28. package/dist/conversion/hub/pipelines/outbound.d.ts +22 -0
  29. package/dist/conversion/hub/policy/policy-engine.d.ts +46 -0
  30. package/dist/conversion/hub/policy/policy-engine.js +176 -0
  31. package/dist/conversion/hub/policy/protocol-spec.d.ts +50 -0
  32. package/dist/conversion/hub/policy/protocol-spec.js +105 -0
  33. package/dist/conversion/hub/process/chat-process.d.ts +32 -0
  34. package/dist/conversion/hub/registry.d.ts +28 -0
  35. package/dist/conversion/hub/response/chat-response-utils.d.ts +6 -0
  36. package/dist/conversion/hub/response/provider-response.js +31 -0
  37. package/dist/conversion/hub/semantic-mappers/gemini-mapper.d.ts +7 -0
  38. package/dist/conversion/hub/semantic-mappers/gemini-mapper.js +87 -1
  39. package/dist/conversion/hub/semantic-mappers/index.d.ts +4 -0
  40. package/dist/conversion/hub/semantic-mappers/responses-mapper.d.ts +21 -0
  41. package/dist/conversion/hub/standardized-bridge.d.ts +12 -0
  42. package/dist/conversion/hub/types/chat-schema.d.ts +112 -0
  43. package/dist/conversion/hub/types/errors.d.ts +5 -0
  44. package/dist/conversion/hub/types/format-envelope.d.ts +7 -0
  45. package/dist/conversion/hub/types/index.d.ts +6 -0
  46. package/dist/conversion/hub/types/json.d.ts +9 -0
  47. package/dist/conversion/hub/types/node.d.ts +31 -0
  48. package/dist/conversion/responses/responses-openai-bridge.js +263 -10
  49. package/dist/conversion/schema-validator.d.ts +7 -0
  50. package/dist/conversion/shared/args-mapping.d.ts +18 -0
  51. package/dist/conversion/shared/chat-request-filters.d.ts +9 -0
  52. package/dist/conversion/shared/errors.d.ts +1 -1
  53. package/dist/conversion/shared/gemini-tool-utils.js +61 -0
  54. package/dist/conversion/shared/jsonish.d.ts +3 -0
  55. package/dist/conversion/shared/mcp-injection.d.ts +2 -0
  56. package/dist/conversion/shared/media.d.ts +1 -0
  57. package/dist/conversion/shared/openai-message-normalize.d.ts +1 -0
  58. package/dist/conversion/shared/payload-budget.d.ts +13 -0
  59. package/dist/conversion/shared/reasoning-mapping.d.ts +5 -0
  60. package/dist/conversion/shared/responses-request-adapter.d.ts +1 -28
  61. package/dist/conversion/shared/responses-request-adapter.js +1 -430
  62. package/dist/conversion/shared/snapshot-hooks.js +112 -4
  63. package/dist/conversion/shared/tool-governor.js +8 -2
  64. package/dist/conversion/shared/tool-harvester.d.ts +31 -0
  65. package/dist/conversion/shared/tool-mapping.js +10 -29
  66. package/dist/conversion/types.d.ts +33 -0
  67. package/dist/filters/builtin/add-fields-filter.d.ts +8 -0
  68. package/dist/filters/builtin/blacklist-filter.d.ts +8 -0
  69. package/dist/filters/builtin/whitelist-filter.d.ts +8 -0
  70. package/dist/filters/engine.d.ts +16 -0
  71. package/dist/filters/special/request-tool-choice-policy.d.ts +11 -0
  72. package/dist/filters/special/response-finish-invariants.d.ts +11 -0
  73. package/dist/filters/special/response-openai-to-responses-bridge.d.ts +13 -0
  74. package/dist/filters/special/response-tool-arguments-blacklist.d.ts +12 -0
  75. package/dist/filters/special/response-tool-arguments-schema-converge.d.ts +13 -0
  76. package/dist/filters/special/response-tool-arguments-stringify.d.ts +9 -0
  77. package/dist/filters/special/response-tool-arguments-whitelist.d.ts +11 -0
  78. package/dist/filters/special/tool-filter-hooks.d.ts +19 -0
  79. package/dist/filters/special/tool-post-constraints.d.ts +31 -0
  80. package/dist/filters/types.d.ts +68 -0
  81. package/dist/filters/utils/fieldmap-loader.d.ts +2 -0
  82. package/dist/filters/utils/snapshot-writer.d.ts +10 -0
  83. package/dist/guidance/index.d.ts +3 -0
  84. package/dist/guidance/index.js +78 -83
  85. package/dist/http/sse-response.d.ts +22 -0
  86. package/dist/router/virtual-router/bootstrap.d.ts +6 -0
  87. package/dist/router/virtual-router/bootstrap.js +49 -5
  88. package/dist/router/virtual-router/classifier.d.ts +10 -0
  89. package/dist/router/virtual-router/engine-selection.js +147 -15
  90. package/dist/router/virtual-router/engine.js +177 -31
  91. package/dist/router/virtual-router/error-center.d.ts +10 -0
  92. package/dist/router/virtual-router/features.d.ts +3 -0
  93. package/dist/router/virtual-router/routing-instructions.d.ts +23 -1
  94. package/dist/router/virtual-router/routing-instructions.js +120 -30
  95. package/dist/router/virtual-router/types.d.ts +11 -0
  96. package/dist/servertool/engine.js +189 -16
  97. package/dist/servertool/handlers/apply-patch-guard.js +269 -0
  98. package/dist/servertool/handlers/exec-command-guard.js +558 -0
  99. package/dist/servertool/handlers/followup-message-trimmer.d.ts +16 -0
  100. package/dist/servertool/handlers/followup-message-trimmer.js +198 -0
  101. package/dist/servertool/handlers/followup-request-builder.d.ts +17 -0
  102. package/dist/servertool/handlers/followup-request-builder.js +122 -0
  103. package/dist/servertool/handlers/gemini-empty-reply-continue.js +252 -51
  104. package/dist/servertool/handlers/iflow-model-error-retry.js +12 -22
  105. package/dist/servertool/handlers/stop-message-auto.js +237 -75
  106. package/dist/servertool/handlers/vision.js +15 -27
  107. package/dist/servertool/handlers/web-search.js +17 -43
  108. package/dist/servertool/server-side-tools.d.ts +3 -0
  109. package/dist/servertool/server-side-tools.js +3 -0
  110. package/dist/sse/json-to-sse/anthropic-json-to-sse-converter.d.ts +2 -1
  111. package/dist/sse/json-to-sse/chat-json-to-sse-converter.d.ts +80 -0
  112. package/dist/sse/json-to-sse/event-generators/chat.d.ts +55 -0
  113. package/dist/sse/json-to-sse/event-generators/responses.d.ts +99 -0
  114. package/dist/sse/json-to-sse/gemini-json-to-sse-converter.d.ts +2 -1
  115. package/dist/sse/json-to-sse/responses-json-to-sse-converter.d.ts +80 -0
  116. package/dist/sse/json-to-sse/sequencers/anthropic-sequencer.d.ts +1 -1
  117. package/dist/sse/json-to-sse/sequencers/chat-sequencer.d.ts +2 -2
  118. package/dist/sse/json-to-sse/sequencers/gemini-sequencer.d.ts +1 -1
  119. package/dist/sse/json-to-sse/sequencers/responses-sequencer.d.ts +40 -0
  120. package/dist/sse/shared/chat-serializer.d.ts +4 -0
  121. package/dist/sse/shared/constants.d.ts +272 -0
  122. package/dist/sse/shared/serializers/anthropic-event-serializer.d.ts +1 -1
  123. package/dist/sse/shared/serializers/base-serializer.d.ts +158 -0
  124. package/dist/sse/shared/serializers/chat-event-serializer.d.ts +82 -0
  125. package/dist/sse/shared/serializers/gemini-event-serializer.d.ts +1 -1
  126. package/dist/sse/shared/serializers/index.d.ts +2 -1
  127. package/dist/sse/shared/serializers/responses-event-serializer.d.ts +123 -0
  128. package/dist/sse/shared/serializers/types.d.ts +51 -0
  129. package/dist/sse/shared/utils.d.ts +254 -0
  130. package/dist/sse/shared/writer.d.ts +2 -2
  131. package/dist/sse/sse-to-json/anthropic-sse-to-json-converter.d.ts +1 -1
  132. package/dist/sse/sse-to-json/builders/anthropic-response-builder.d.ts +1 -1
  133. package/dist/sse/sse-to-json/builders/response-builder.d.ts +1 -1
  134. package/dist/sse/sse-to-json/chat-sse-to-json-converter.d.ts +2 -1
  135. package/dist/sse/sse-to-json/gemini-sse-to-json-converter.d.ts +2 -1
  136. package/dist/sse/sse-to-json/parsers/sse-parser.d.ts +73 -0
  137. package/dist/sse/sse-to-json/responses-sse-to-json-converter.d.ts +1 -1
  138. package/dist/sse/types/chat-types.d.ts +1 -1
  139. package/dist/sse/types/responses-types.d.ts +1 -1
  140. package/dist/tools/apply-patch/execution-capturer.d.ts +13 -0
  141. package/dist/tools/apply-patch/execution-capturer.js +158 -0
  142. package/dist/tools/apply-patch/regression-capturer.d.ts +1 -0
  143. package/dist/tools/apply-patch/regression-capturer.js +5 -4
  144. package/dist/tools/apply-patch/structured.js +109 -13
  145. package/dist/tools/apply-patch/validator.js +261 -17
  146. package/dist/tools/tool-registry.d.ts +8 -0
  147. package/dist/tools/tool-registry.js +2 -1
  148. package/package.json +4 -4
  149. package/dist/conversion/compat/actions/apply-patch-format-fixer.js +0 -233
  150. package/dist/conversion/config/compat-profiles.json +0 -38
  151. package/dist/conversion/hub/response/server-side-tools.d.ts +0 -26
  152. package/dist/conversion/hub/response/server-side-tools.js +0 -383
  153. package/dist/conversion/shared/bridge-conversation-store.d.ts +0 -41
  154. package/dist/conversion/shared/bridge-conversation-store.js +0 -279
  155. package/dist/conversion/shared/bridge-request-adapter.d.ts +0 -28
  156. package/dist/conversion/shared/bridge-request-adapter.js +0 -430
  157. package/dist/conversion/shared/responses-id-utils.js +0 -42
  158. package/dist/conversion/shared/responses-instructions.js +0 -113
  159. package/dist/conversion/shared/responses-message-utils.d.ts +0 -15
  160. package/dist/conversion/shared/responses-message-utils.js +0 -206
  161. package/dist/conversion/shared/responses-metadata.js +0 -1
  162. package/dist/conversion/shared/responses-output-utils.d.ts +0 -7
  163. package/dist/conversion/shared/responses-output-utils.js +0 -108
  164. package/dist/conversion/shared/responses-types.d.ts +0 -33
  165. package/dist/conversion/shared/tool-normalizers.d.ts +0 -4
  166. package/dist/conversion/shared/tool-normalizers.js +0 -84
  167. package/dist/filters/special/request-streaming-to-nonstreaming.d.ts +0 -13
  168. package/dist/filters/special/request-streaming-to-nonstreaming.js +0 -39
  169. package/dist/filters/special/response-apply-patch-toon-decode.d.ts +0 -23
  170. package/dist/filters/special/response-apply-patch-toon-decode.js +0 -460
  171. package/dist/filters/special/response-tool-arguments-toon-decode.d.ts +0 -10
  172. package/dist/filters/special/response-tool-arguments-toon-decode.js +0 -154
  173. package/dist/servertool/flow-types.d.ts +0 -40
  174. package/dist/servertool/flow-types.js +0 -1
  175. package/dist/servertool/orchestration-types.d.ts +0 -33
  176. package/dist/servertool/orchestration-types.js +0 -1
  177. package/dist/servertool/vision-tool.d.ts +0 -2
  178. package/dist/servertool/vision-tool.js +0 -185
  179. package/dist/tools/patch-args-normalizer.d.ts +0 -15
  180. package/dist/tools/patch-args-normalizer.js +0 -472
  181. package/dist/utils/toon.d.ts +0 -4
  182. package/dist/utils/toon.js +0 -75
  183. /package/dist/{conversion/compat/actions/apply-patch-format-fixer.d.ts → servertool/handlers/apply-patch-guard.d.ts} +0 -0
  184. /package/dist/{conversion/shared/responses-types.js → servertool/handlers/exec-command-guard.d.ts} +0 -0
@@ -0,0 +1,269 @@
1
+ import { registerServerToolHandler } from '../registry.js';
2
+ import { cloneJson } from '../server-side-tools.js';
3
+ import { validateToolCall } from '../../tools/tool-registry.js';
4
+ import { buildEntryAwareFollowupPayload, extractCapturedChatSeed } from './followup-request-builder.js';
5
+ const FLOW_ID = 'apply_patch_guard';
6
+ const handler = async (ctx) => {
7
+ const toolCall = ctx.toolCall;
8
+ if (!toolCall) {
9
+ return null;
10
+ }
11
+ if (!ctx.options.reenterPipeline) {
12
+ return null;
13
+ }
14
+ const rawArgs = typeof toolCall.arguments === 'string' ? toolCall.arguments : '';
15
+ const validation = validateToolCall('apply_patch', rawArgs);
16
+ if (validation?.ok) {
17
+ return null;
18
+ }
19
+ const reason = typeof validation?.reason === 'string' && validation.reason.trim()
20
+ ? validation.reason.trim()
21
+ : 'unknown';
22
+ const snippet = rawArgs && rawArgs.trim().length
23
+ ? rawArgs.trim().slice(0, 200).replace(/\s+/g, ' ')
24
+ : '';
25
+ const isExecCommandShape = extractLikelyExecCommandShape(rawArgs);
26
+ // For obviously wrong shapes (e.g. exec_command-like payload passed into apply_patch),
27
+ // do NOT attempt a followup "recovery" loop. Return a plain assistant message instead.
28
+ if (isExecCommandShape && (reason === 'missing_changes' || reason === 'missing_payload' || reason === 'invalid_json')) {
29
+ const patched = injectApplyPatchHardFailureMessage(ctx.base, toolCall, {
30
+ reason,
31
+ snippet,
32
+ rawArgs
33
+ });
34
+ return {
35
+ chatResponse: patched,
36
+ execution: { flowId: FLOW_ID }
37
+ };
38
+ }
39
+ const patched = injectApplyPatchRejectedToolResult(ctx.base, toolCall, {
40
+ reason,
41
+ snippet,
42
+ rawArgs
43
+ });
44
+ const followupPayload = buildToolFollowupPayload(ctx.adapterContext, patched, ctx.options.entryEndpoint || ctx.adapterContext?.entryEndpoint || '/v1/chat/completions');
45
+ if (!followupPayload) {
46
+ // Fail-closed: if we cannot perform reenter, do not intercept.
47
+ // (This should be rare because HubPipeline always provides capturedChatRequest.)
48
+ return null;
49
+ }
50
+ return {
51
+ chatResponse: patched,
52
+ execution: {
53
+ flowId: FLOW_ID,
54
+ followup: {
55
+ requestIdSuffix: ':apply_patch_guard_followup',
56
+ payload: followupPayload
57
+ }
58
+ }
59
+ };
60
+ };
61
+ registerServerToolHandler('apply_patch', handler);
62
+ function injectApplyPatchHardFailureMessage(base, toolCall, options) {
63
+ const cloned = cloneJson(base);
64
+ const choices = Array.isArray(cloned.choices)
65
+ ? cloned.choices
66
+ : [];
67
+ if (!choices.length) {
68
+ return cloned;
69
+ }
70
+ const first = choices[0];
71
+ if (!first || typeof first !== 'object' || Array.isArray(first)) {
72
+ return cloned;
73
+ }
74
+ const message = first.message;
75
+ if (!message || typeof message !== 'object' || Array.isArray(message)) {
76
+ return cloned;
77
+ }
78
+ // Replace tool-call response with a plain assistant message.
79
+ const guidance = buildApplyPatchGuidance(options.rawArgs);
80
+ const text = `[apply_patch rejected] reason=${options.reason}` +
81
+ (options.snippet ? ` args=${options.snippet}` : '') +
82
+ `\n\n${guidance}`;
83
+ message.content = text;
84
+ if (Object.prototype.hasOwnProperty.call(message, 'tool_calls')) {
85
+ delete message.tool_calls;
86
+ }
87
+ if (typeof first.finish_reason === 'string' && first.finish_reason === 'tool_calls') {
88
+ first.finish_reason = 'stop';
89
+ }
90
+ // Remove tool_outputs to avoid confusing clients that don't implement tool loops.
91
+ if (Object.prototype.hasOwnProperty.call(cloned, 'tool_outputs')) {
92
+ delete cloned.tool_outputs;
93
+ }
94
+ return cloned;
95
+ }
96
+ function injectApplyPatchRejectedToolResult(base, toolCall, options) {
97
+ const cloned = cloneJson(base);
98
+ const existingOutputs = Array.isArray(cloned.tool_outputs)
99
+ ? cloned.tool_outputs
100
+ : [];
101
+ const payload = {
102
+ ok: false,
103
+ tool: 'apply_patch',
104
+ reason: options.reason,
105
+ ...(options.snippet ? { argsSnippet: options.snippet } : {}),
106
+ guidance: buildApplyPatchGuidance(options.rawArgs)
107
+ };
108
+ cloned.tool_outputs = [
109
+ ...existingOutputs,
110
+ {
111
+ tool_call_id: toolCall.id,
112
+ name: 'apply_patch',
113
+ content: JSON.stringify(payload)
114
+ }
115
+ ];
116
+ return cloned;
117
+ }
118
+ function buildApplyPatchGuidance(rawArgs) {
119
+ const base = 'apply_patch 参数预检失败。请改用统一 diff(*** Begin Patch ... *** End Patch),' +
120
+ '或使用结构化 JSON:{ file, changes:[{ file, kind, target/anchor, lines/newText, ... }] }。';
121
+ const schemaHint = extractLikelyExecCommandShape(rawArgs);
122
+ if (schemaHint) {
123
+ return (base +
124
+ '\n\n' +
125
+ '你传入的参数看起来像 exec_command(例如包含 "command"/"cmd" 字段),但工具名是 apply_patch。\n' +
126
+ 'apply_patch 必须提供非空的 "changes" 数组;如果你需要执行命令请改用 exec_command(注意:禁止通过 shell 写文件)。');
127
+ }
128
+ // User-requested hint (directory deletion is not supported via delete_file).
129
+ // Heuristic: detect delete_file with a directory-like target (trailing slash).
130
+ const hintTarget = extractDeleteFileTarget(rawArgs);
131
+ if (!hintTarget) {
132
+ return base;
133
+ }
134
+ return (base +
135
+ '\n\n' +
136
+ `- {"changes":[{"kind":"delete_file","target":"${hintTarget}"}]} ❌ 仍会 invalid_file(因为是目录)\n\n` +
137
+ '如果你真要删目录:请用 exec_command(例如 rm -rf tmp web-container-manager),或者用 apply_patch 逐个删目录下的文件(更安全、可审计)。');
138
+ }
139
+ function extractDeleteFileTarget(rawArgs) {
140
+ if (!rawArgs || typeof rawArgs !== 'string') {
141
+ return null;
142
+ }
143
+ let parsed;
144
+ try {
145
+ parsed = JSON.parse(rawArgs);
146
+ }
147
+ catch {
148
+ return null;
149
+ }
150
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
151
+ return null;
152
+ }
153
+ const record = parsed;
154
+ const changes = Array.isArray(record.changes) ? record.changes : [];
155
+ for (const change of changes) {
156
+ if (!change || typeof change !== 'object' || Array.isArray(change))
157
+ continue;
158
+ const c = change;
159
+ const kind = typeof c.kind === 'string' ? c.kind.trim().toLowerCase() : '';
160
+ if (kind !== 'delete_file')
161
+ continue;
162
+ const target = typeof c.target === 'string' ? c.target.trim() : '';
163
+ if (!target)
164
+ continue;
165
+ if (target.endsWith('/')) {
166
+ return target;
167
+ }
168
+ }
169
+ return null;
170
+ }
171
+ function extractLikelyExecCommandShape(rawArgs) {
172
+ if (!rawArgs || typeof rawArgs !== 'string') {
173
+ return false;
174
+ }
175
+ let parsed;
176
+ try {
177
+ parsed = JSON.parse(rawArgs);
178
+ }
179
+ catch {
180
+ return false;
181
+ }
182
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
183
+ return false;
184
+ }
185
+ const record = parsed;
186
+ const command = record.command;
187
+ const cmd = record.cmd;
188
+ if (typeof command === 'string' && command.trim()) {
189
+ return true;
190
+ }
191
+ if (typeof cmd === 'string' && cmd.trim()) {
192
+ return true;
193
+ }
194
+ return false;
195
+ }
196
+ function buildToolFollowupPayload(adapterContext, chatResponse, entryEndpoint) {
197
+ const captured = adapterContext && typeof adapterContext === 'object'
198
+ ? adapterContext.capturedChatRequest
199
+ : undefined;
200
+ const seed = extractCapturedChatSeed(captured);
201
+ if (!seed) {
202
+ return null;
203
+ }
204
+ const assistantMessage = extractAssistantMessage(chatResponse);
205
+ if (!assistantMessage) {
206
+ return null;
207
+ }
208
+ const toolMessages = buildToolMessages(chatResponse);
209
+ if (!toolMessages.length) {
210
+ return null;
211
+ }
212
+ const reconstructed = [...seed.messages, assistantMessage, ...toolMessages];
213
+ return buildEntryAwareFollowupPayload({
214
+ entryEndpoint,
215
+ model: seed.model,
216
+ messages: reconstructed,
217
+ ...(seed.tools ? { tools: seed.tools } : {}),
218
+ ...(seed.parameters ? { parameters: seed.parameters } : {})
219
+ });
220
+ }
221
+ function extractAssistantMessage(chatResponse) {
222
+ const choices = Array.isArray(chatResponse.choices)
223
+ ? chatResponse.choices
224
+ : [];
225
+ if (!choices.length)
226
+ return null;
227
+ const first = choices[0];
228
+ if (!first || typeof first !== 'object' || Array.isArray(first))
229
+ return null;
230
+ const message = first.message;
231
+ if (!message || typeof message !== 'object' || Array.isArray(message))
232
+ return null;
233
+ return cloneJson(message);
234
+ }
235
+ function buildToolMessages(chatResponse) {
236
+ const toolOutputs = Array.isArray(chatResponse.tool_outputs)
237
+ ? chatResponse.tool_outputs
238
+ : [];
239
+ const messages = [];
240
+ for (const entry of toolOutputs) {
241
+ if (!entry || typeof entry !== 'object' || Array.isArray(entry))
242
+ continue;
243
+ const record = entry;
244
+ const toolCallId = typeof record.tool_call_id === 'string' ? record.tool_call_id : undefined;
245
+ if (!toolCallId)
246
+ continue;
247
+ const name = typeof record.name === 'string' && record.name.trim() ? record.name.trim() : 'apply_patch';
248
+ const rawContent = record.content;
249
+ let contentText;
250
+ if (typeof rawContent === 'string') {
251
+ contentText = rawContent;
252
+ }
253
+ else {
254
+ try {
255
+ contentText = JSON.stringify(rawContent ?? {});
256
+ }
257
+ catch {
258
+ contentText = String(rawContent ?? '');
259
+ }
260
+ }
261
+ messages.push({
262
+ role: 'tool',
263
+ tool_call_id: toolCallId,
264
+ name,
265
+ content: contentText
266
+ });
267
+ }
268
+ return messages;
269
+ }