@jsonstudio/llms 0.6.3275 → 0.6.3405

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 (72) hide show
  1. package/dist/conversion/bridge-message-utils.d.ts +4 -4
  2. package/dist/conversion/bridge-message-utils.js +28 -538
  3. package/dist/conversion/compat/actions/claude-thinking-tools.d.ts +1 -14
  4. package/dist/conversion/compat/actions/claude-thinking-tools.js +3 -71
  5. package/dist/conversion/compat/actions/lmstudio-responses-fc-ids.d.ts +0 -8
  6. package/dist/conversion/compat/actions/lmstudio-responses-fc-ids.js +2 -57
  7. package/dist/conversion/compat/actions/normalize-tool-call-ids.d.ts +0 -9
  8. package/dist/conversion/compat/actions/normalize-tool-call-ids.js +6 -136
  9. package/dist/conversion/compat/actions/request-rules.js +2 -61
  10. package/dist/conversion/compat/actions/response-blacklist.d.ts +0 -4
  11. package/dist/conversion/compat/actions/response-blacklist.js +2 -77
  12. package/dist/conversion/compat/actions/response-normalize.js +2 -119
  13. package/dist/conversion/compat/actions/response-validate.js +2 -74
  14. package/dist/conversion/compat/actions/strip-orphan-function-calls-tag.js +2 -150
  15. package/dist/conversion/compat/profiles/responses-crs.json +15 -0
  16. package/dist/conversion/hub/operation-table/semantic-mappers/anthropic-mapper.js +24 -1
  17. package/dist/conversion/hub/operation-table/semantic-mappers/chat-mapper.js +16 -5
  18. package/dist/conversion/hub/pipeline/hub-pipeline.js +91 -0
  19. package/dist/conversion/hub/pipeline/stages/resp_process/resp_process_stage1_tool_governance/index.js +1 -6
  20. package/dist/conversion/hub/response/response-runtime.js +14 -6
  21. package/dist/conversion/responses/responses-openai-bridge/response-payload.js +11 -11
  22. package/dist/conversion/shared/anthropic-message-utils.js +2 -12
  23. package/dist/conversion/shared/chat-request-filters.js +2 -61
  24. package/dist/conversion/shared/reasoning-mapping.js +3 -0
  25. package/dist/conversion/shared/reasoning-normalizer.d.ts +1 -0
  26. package/dist/conversion/shared/reasoning-normalizer.js +35 -388
  27. package/dist/conversion/shared/reasoning-tool-normalizer.js +8 -15
  28. package/dist/conversion/shared/reasoning-tool-parser.js +7 -8
  29. package/dist/conversion/shared/reasoning-utils.js +13 -35
  30. package/dist/conversion/shared/responses-response-utils.js +3 -48
  31. package/dist/conversion/shared/responses-tool-utils.d.ts +1 -1
  32. package/dist/conversion/shared/responses-tool-utils.js +74 -180
  33. package/dist/conversion/shared/streaming-text-extractor.d.ts +0 -5
  34. package/dist/conversion/shared/streaming-text-extractor.js +18 -111
  35. package/dist/conversion/shared/text-markup-normalizer/normalize.d.ts +1 -1
  36. package/dist/conversion/shared/text-markup-normalizer/normalize.js +3 -91
  37. package/dist/conversion/shared/thought-signature-validator.js +19 -133
  38. package/dist/conversion/shared/tool-argument-repairer.js +16 -19
  39. package/dist/conversion/shared/tool-call-id-manager.d.ts +1 -5
  40. package/dist/conversion/shared/tool-call-id-manager.js +74 -98
  41. package/dist/conversion/shared/tool-harvester.js +19 -382
  42. package/dist/conversion/shared/tool-mapping.d.ts +2 -3
  43. package/dist/conversion/shared/tool-mapping.js +28 -184
  44. package/dist/conversion/shared/tooling.js +20 -151
  45. package/dist/filters/special/response-tool-arguments-stringify.js +9 -1
  46. package/dist/guidance/index.js +2 -2
  47. package/dist/native/router_hotpath_napi.node +0 -0
  48. package/dist/router/virtual-router/bootstrap/web-search-config.js +25 -0
  49. package/dist/router/virtual-router/bootstrap.js +21 -16
  50. package/dist/router/virtual-router/engine-legacy/helpers.js +1 -1
  51. package/dist/router/virtual-router/engine-selection/native-compat-action-semantics.d.ts +6 -0
  52. package/dist/router/virtual-router/engine-selection/native-compat-action-semantics.js +171 -0
  53. package/dist/router/virtual-router/engine-selection/native-hub-bridge-action-semantics.d.ts +39 -0
  54. package/dist/router/virtual-router/engine-selection/native-hub-bridge-action-semantics.js +196 -0
  55. package/dist/router/virtual-router/engine-selection/native-hub-pipeline-req-inbound-semantics.d.ts +1 -0
  56. package/dist/router/virtual-router/engine-selection/native-hub-pipeline-req-inbound-semantics.js +27 -0
  57. package/dist/router/virtual-router/engine-selection/native-router-hotpath-loader.js +45 -0
  58. package/dist/router/virtual-router/engine-selection/native-shared-conversion-semantics.d.ts +70 -1
  59. package/dist/router/virtual-router/engine-selection/native-shared-conversion-semantics.js +993 -55
  60. package/dist/router/virtual-router/engine.js +3 -2
  61. package/dist/router/virtual-router/routing-instructions/parse.js +30 -3
  62. package/dist/router/virtual-router/types.d.ts +23 -0
  63. package/dist/servertool/handlers/web-search.js +26 -1
  64. package/dist/servertool/server-side-tools.js +11 -2
  65. package/dist/servertool/types.d.ts +4 -0
  66. package/dist/sse/sse-to-json/builders/anthropic-response-builder.js +28 -3
  67. package/dist/sse/types/anthropic-types.d.ts +3 -1
  68. package/dist/tools/apply-patch/args-normalizer/extract-patch.js +2 -2
  69. package/dist/tools/apply-patch/patch-text/looks-like-patch.js +3 -6
  70. package/dist/tools/apply-patch/patch-text/normalize.js +14 -3
  71. package/dist/tools/tool-registry.js +12 -0
  72. package/package.json +6 -1
@@ -1,152 +1,4 @@
1
- function isRecord(value) {
2
- return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
3
- }
4
- const ORPHAN_TAG_RE = /^\s*(?:[•*+-]\s*)?(?:<\/?\s*function_calls\s*\/?\s*>|<\/\s*(?:parameter|function|tool_call)\s*>)\s*$/i;
5
- function stripOrphanTagLines(text) {
6
- const raw = String(text ?? '');
7
- if (!raw)
8
- return { text: raw, changed: false };
9
- const lines = raw.split(/\r?\n/);
10
- const kept = [];
11
- let changed = false;
12
- for (const line of lines) {
13
- if (ORPHAN_TAG_RE.test(line)) {
14
- changed = true;
15
- continue;
16
- }
17
- kept.push(line);
18
- }
19
- return { text: kept.join('\n'), changed };
20
- }
21
- function stripInMessageInPlace(message) {
22
- let changed = false;
23
- const content = message.content;
24
- if (typeof content === 'string') {
25
- const res = stripOrphanTagLines(content);
26
- if (res.changed) {
27
- message.content = res.text;
28
- changed = true;
29
- }
30
- }
31
- else if (Array.isArray(content)) {
32
- const next = content.map((part) => {
33
- if (typeof part === 'string') {
34
- const res = stripOrphanTagLines(part);
35
- if (res.changed)
36
- changed = true;
37
- return res.text;
38
- }
39
- if (!isRecord(part))
40
- return part;
41
- const p = { ...part };
42
- if (typeof p.text === 'string') {
43
- const res = stripOrphanTagLines(p.text);
44
- if (res.changed) {
45
- p.text = res.text;
46
- changed = true;
47
- }
48
- }
49
- if (typeof p.content === 'string') {
50
- const res = stripOrphanTagLines(p.content);
51
- if (res.changed) {
52
- p.content = res.text;
53
- changed = true;
54
- }
55
- }
56
- return p;
57
- });
58
- if (changed) {
59
- message.content = next;
60
- }
61
- }
62
- for (const key of ['reasoning', 'thinking', 'reasoning_content']) {
63
- const raw = message[key];
64
- if (typeof raw === 'string' && raw.trim().length) {
65
- const res = stripOrphanTagLines(raw);
66
- if (res.changed) {
67
- message[key] = res.text;
68
- changed = true;
69
- }
70
- }
71
- }
72
- return changed;
73
- }
74
- function stripInChatPayloadInPlace(root) {
75
- const choices = Array.isArray(root.choices) ? root.choices : [];
76
- if (!choices.length)
77
- return false;
78
- let changed = false;
79
- for (const choice of choices) {
80
- if (!isRecord(choice))
81
- continue;
82
- const message = choice.message;
83
- if (!isRecord(message))
84
- continue;
85
- const role = typeof message.role === 'string' ? String(message.role).trim().toLowerCase() : 'assistant';
86
- if (role !== 'assistant')
87
- continue;
88
- if (stripInMessageInPlace(message)) {
89
- changed = true;
90
- choice.message = message;
91
- }
92
- }
93
- return changed;
94
- }
95
- function stripInResponsesPayloadInPlace(root) {
96
- const output = Array.isArray(root.output) ? root.output : [];
97
- if (!output.length)
98
- return false;
99
- let changed = false;
100
- for (const item of output) {
101
- if (!isRecord(item))
102
- continue;
103
- const type = typeof item.type === 'string' ? String(item.type).trim().toLowerCase() : '';
104
- if (type !== 'message')
105
- continue;
106
- const role = typeof item.role === 'string' ? String(item.role).trim().toLowerCase() : 'assistant';
107
- if (role !== 'assistant')
108
- continue;
109
- const content = Array.isArray(item.content) ? item.content : [];
110
- if (content.length) {
111
- const next = content.map((part) => {
112
- if (!isRecord(part))
113
- return part;
114
- const p = { ...part };
115
- for (const key of ['text', 'content', 'value']) {
116
- if (typeof p[key] === 'string' && String(p[key]).trim().length) {
117
- const res = stripOrphanTagLines(String(p[key]));
118
- if (res.changed) {
119
- p[key] = res.text;
120
- changed = true;
121
- }
122
- }
123
- }
124
- return p;
125
- });
126
- if (changed) {
127
- item.content = next;
128
- }
129
- }
130
- if (typeof item.output_text === 'string' && String(item.output_text).trim().length) {
131
- const res = stripOrphanTagLines(String(item.output_text));
132
- if (res.changed) {
133
- item.output_text = res.text;
134
- changed = true;
135
- }
136
- }
137
- }
138
- return changed;
139
- }
1
+ import { stripOrphanFunctionCallsTagWithNative } from '../../../router/virtual-router/engine-selection/native-chat-process-governance-semantics.js';
140
2
  export function stripOrphanFunctionCallsTag(payload) {
141
- try {
142
- if (!payload || typeof payload !== 'object' || Array.isArray(payload)) {
143
- return payload;
144
- }
145
- const root = structuredClone(payload);
146
- const changed = stripInChatPayloadInPlace(root) || stripInResponsesPayloadInPlace(root);
147
- return (changed ? root : payload);
148
- }
149
- catch {
150
- return payload;
151
- }
3
+ return stripOrphanFunctionCallsTagWithNative(payload);
152
4
  }
@@ -0,0 +1,15 @@
1
+ {
2
+ "id": "responses:crs",
3
+ "protocol": "openai-responses",
4
+ "request": {
5
+ "mappings": [
6
+ {
7
+ "action": "remove",
8
+ "path": "temperature"
9
+ }
10
+ ]
11
+ },
12
+ "response": {
13
+ "mappings": []
14
+ }
15
+ }
@@ -179,6 +179,20 @@ function buildAnthropicThinkingFromReasoning(reasoning) {
179
179
  }
180
180
  return { type: 'enabled', budget_tokens: 4096 };
181
181
  }
182
+ function normalizeContextToken(value) {
183
+ return typeof value === 'string' ? value.trim().toLowerCase() : '';
184
+ }
185
+ function isArkCodingPlanContext(ctx) {
186
+ if (!ctx || typeof ctx !== 'object') {
187
+ return false;
188
+ }
189
+ const candidates = [
190
+ normalizeContextToken(ctx.providerId),
191
+ normalizeContextToken(ctx.providerKey),
192
+ normalizeContextToken(ctx.runtimeKey)
193
+ ];
194
+ return candidates.some((candidate) => candidate === 'ark-coding-plan' || candidate.startsWith('ark-coding-plan.'));
195
+ }
182
196
  function cloneAnthropicSystemBlocks(value) {
183
197
  if (value === undefined || value === null) {
184
198
  return undefined;
@@ -395,10 +409,19 @@ export class AnthropicSemanticMapper {
395
409
  if (baseRequest.max_output_tokens && !baseRequest.max_tokens) {
396
410
  baseRequest.max_tokens = baseRequest.max_output_tokens;
397
411
  }
398
- const mappedThinking = buildAnthropicThinkingFromReasoning(trimmedParameters?.reasoning);
412
+ const rawReasoning = trimmedParameters?.reasoning;
413
+ const mappedThinking = buildAnthropicThinkingFromReasoning(rawReasoning);
399
414
  if (mappedThinking && baseRequest.thinking === undefined) {
400
415
  baseRequest.thinking = mappedThinking;
401
416
  }
417
+ if (baseRequest.thinking === undefined &&
418
+ rawReasoning === undefined &&
419
+ isArkCodingPlanContext(ctx)) {
420
+ baseRequest.thinking = {
421
+ type: 'enabled',
422
+ budget_tokens: mapReasoningEffortToAnthropicBudget('high')
423
+ };
424
+ }
402
425
  if (responsesOrigin && trimmedParameters && Object.prototype.hasOwnProperty.call(trimmedParameters, 'reasoning')) {
403
426
  appendLossyFieldAudit(chat, {
404
427
  field: 'reasoning',
@@ -4,16 +4,27 @@ export function maybeAugmentApplyPatchErrorContent(content, toolName) {
4
4
  return content;
5
5
  const lower = content.toLowerCase();
6
6
  const isApplyPatch = (typeof toolName === 'string' && toolName.trim() === 'apply_patch') ||
7
- lower.includes('apply_patch verification failed');
7
+ lower.includes('apply_patch verification failed') ||
8
+ lower.includes('failed to apply patch');
8
9
  if (!isApplyPatch) {
9
10
  return content;
10
11
  }
11
- // Avoid duplicate hints.
12
- if (content.includes('[apply_patch hint]')) {
12
+ if (content.includes('[apply_patch hint]') || content.includes('[RouteCodex hint] apply_patch')) {
13
13
  return content;
14
14
  }
15
- const hint = '\n\n[apply_patch hint] \u5728\u4f7f\u7528 apply_patch \u4e4b\u524d\uff0c\u8bf7\u5148\u8bfb\u53d6\u76ee\u6807\u6587\u4ef6\u7684\u6700\u65b0\u5185\u5bb9\uff0c\u5e76\u57fa\u4e8e\u8be5\u5185\u5bb9\u751f\u6210\u8865\u4e01\uff1b\u540c\u65f6\u786e\u4fdd\u8865\u4e01\u683c\u5f0f\u7b26\u5408\u5de5\u5177\u89c4\u8303\uff08\u7edf\u4e00\u8865\u4e01\u683c\u5f0f\u6216\u7ed3\u6784\u5316\u53c2\u6570\uff09\uff0c\u907f\u514d\u4e0a\u4e0b\u6587\u4e0d\u5339\u914d\u6216\u8bed\u6cd5\u9519\u8bef\u3002';
16
- return content + hint;
15
+ const sandboxSignal = lower.includes('sandbox(signal(9))') || (lower.includes('sandbox') && lower.includes('signal(9)'));
16
+ if (sandboxSignal) {
17
+ return content +
18
+ '\n\n[RouteCodex hint] apply_patch \u88ab sandbox \u7ec8\u6b62 (Signal 9)\u3002\u5e38\u89c1\u539f\u56e0\u662f\u8865\u4e01\u6d89\u53ca workspace \u4e4b\u5916\u7684\u8def\u5f84\u3002\u8bf7\u6539\u7528\u5f53\u524d workspace \u5185\u8def\u5f84\uff0c\u6216\u5c06\u76ee\u6807\u4ed3\u52a0\u5165 workspaces/workdir \u540e\u518d\u8c03\u7528 apply_patch\u3002';
19
+ }
20
+ const missingPath = lower.includes('failed to read file to update') ||
21
+ lower.includes('no such file or directory');
22
+ if (missingPath) {
23
+ return content +
24
+ '\n\n[RouteCodex hint] apply_patch \u8bfb\u53d6\u76ee\u6807\u6587\u4ef6\u5931\u8d25\uff1a\u8def\u5f84\u4e0d\u5b58\u5728\u6216\u4e0d\u5728\u5f53\u524d workspace\u3002\u8bf7\u786e\u8ba4\u8def\u5f84\u5728\u5f53\u524d workspace \u5185\u4e14\u6587\u4ef6\u771f\u5b9e\u5b58\u5728\uff1b\u8def\u5f84\u5fc5\u987b\u4e3a workspace \u76f8\u5bf9\u8def\u5f84\uff08\u5982 src/...\uff09\uff0c\u4e0d\u8981\u4ee5 / \u6216\u76d8\u7b26\u5f00\u5934\u3002\u5fc5\u8981\u65f6\u5207\u6362 workspace/workdir\u3002';
25
+ }
26
+ return content +
27
+ '\n\n[apply_patch hint] \u5728\u4f7f\u7528 apply_patch \u4e4b\u524d\uff0c\u8bf7\u5148\u8bfb\u53d6\u76ee\u6807\u6587\u4ef6\u7684\u6700\u65b0\u5185\u5bb9\uff0c\u5e76\u57fa\u4e8e\u8be5\u5185\u5bb9\u751f\u6210\u8865\u4e01\uff1b\u540c\u65f6\u786e\u4fdd\u8865\u4e01\u683c\u5f0f\u7b26\u5408\u5de5\u5177\u89c4\u8303\uff08\u7edf\u4e00\u8865\u4e01\u683c\u5f0f\u6216\u7ed3\u6784\u5316\u53c2\u6570\uff09\uff0c\u907f\u514d\u4e0a\u4e0b\u6587\u4e0d\u5339\u914d\u6216\u8bed\u6cd5\u9519\u8bef\u3002';
17
28
  }
18
29
  export class ChatSemanticMapper {
19
30
  async toChat(format, ctx) {
@@ -69,6 +69,95 @@ function propagateAdapterContextMetadataFields(adapterContext, metadata, keys) {
69
69
  function resolveStopMessageRouterMetadata(metadata) {
70
70
  return resolveStopMessageRouterMetadataWithNative(metadata);
71
71
  }
72
+ function isSearchRouteId(routeId) {
73
+ const normalized = typeof routeId === 'string' ? routeId.trim().toLowerCase() : '';
74
+ return normalized.startsWith('web_search') || normalized.startsWith('search');
75
+ }
76
+ function isCanonicalWebSearchToolDefinition(tool) {
77
+ if (!tool || typeof tool !== 'object' || Array.isArray(tool)) {
78
+ return false;
79
+ }
80
+ const row = tool;
81
+ const rawType = typeof row.type === 'string' ? row.type.trim().toLowerCase() : '';
82
+ if (rawType === 'web_search_20250305' || rawType === 'web_search') {
83
+ return true;
84
+ }
85
+ const fnNode = row.function && typeof row.function === 'object' && !Array.isArray(row.function)
86
+ ? row.function
87
+ : undefined;
88
+ const name = typeof fnNode?.name === 'string'
89
+ ? fnNode.name.trim().toLowerCase()
90
+ : typeof row.name === 'string'
91
+ ? row.name.trim().toLowerCase()
92
+ : '';
93
+ return name === 'web_search' || name === 'websearch' || name === 'web-search';
94
+ }
95
+ function maybeApplyDirectBuiltinWebSearchTool(providerPayload, adapterContext, providerProtocol) {
96
+ if (providerProtocol !== 'anthropic-messages') {
97
+ return providerPayload;
98
+ }
99
+ if (!isSearchRouteId(adapterContext.routeId)) {
100
+ return providerPayload;
101
+ }
102
+ const modelId = typeof providerPayload.model === 'string' ? providerPayload.model.trim() : '';
103
+ if (!modelId) {
104
+ return providerPayload;
105
+ }
106
+ const rt = readRuntimeMetadata(adapterContext);
107
+ const webSearch = rt && typeof rt.webSearch === 'object' && rt.webSearch && !Array.isArray(rt.webSearch)
108
+ ? rt.webSearch
109
+ : undefined;
110
+ const enginesRaw = Array.isArray(webSearch?.engines) ? webSearch?.engines : [];
111
+ const matchedEngine = enginesRaw.find((entry) => {
112
+ if (!entry || typeof entry !== 'object' || Array.isArray(entry)) {
113
+ return false;
114
+ }
115
+ const row = entry;
116
+ const executionMode = typeof row.executionMode === 'string' ? row.executionMode.trim().toLowerCase() : '';
117
+ if (executionMode !== 'direct') {
118
+ return false;
119
+ }
120
+ const directActivation = typeof row.directActivation === 'string' ? row.directActivation.trim().toLowerCase() : 'route';
121
+ if (directActivation !== 'builtin') {
122
+ return false;
123
+ }
124
+ const configuredModelId = typeof row.modelId === 'string' ? row.modelId.trim() : '';
125
+ if (configuredModelId && configuredModelId === modelId) {
126
+ return true;
127
+ }
128
+ const providerKey = typeof row.providerKey === 'string' ? row.providerKey.trim() : '';
129
+ return providerKey.endsWith(`.${modelId}`);
130
+ });
131
+ if (!matchedEngine) {
132
+ return providerPayload;
133
+ }
134
+ const rawMaxUses = typeof matchedEngine.maxUses === 'number' ? matchedEngine.maxUses : Number(matchedEngine.maxUses);
135
+ const maxUses = Number.isFinite(rawMaxUses) && rawMaxUses > 0 ? Math.floor(rawMaxUses) : 2;
136
+ const builtinTool = {
137
+ type: 'web_search_20250305',
138
+ name: 'web_search',
139
+ max_uses: maxUses
140
+ };
141
+ const tools = Array.isArray(providerPayload.tools) ? providerPayload.tools : [];
142
+ let replaced = false;
143
+ const nextTools = [];
144
+ for (const tool of tools) {
145
+ if (!replaced && isCanonicalWebSearchToolDefinition(tool)) {
146
+ nextTools.push(builtinTool);
147
+ replaced = true;
148
+ continue;
149
+ }
150
+ if (isCanonicalWebSearchToolDefinition(tool)) {
151
+ continue;
152
+ }
153
+ nextTools.push(tool);
154
+ }
155
+ if (!replaced) {
156
+ nextTools.unshift(builtinTool);
157
+ }
158
+ providerPayload.tools = nextTools;
159
+ return providerPayload;
160
+ }
72
161
  function extractHubShadowCompareConfig(metadata) {
73
162
  const parsed = resolveHubShadowCompareConfigWithNative(metadata);
74
163
  if (!parsed) {
@@ -558,6 +647,7 @@ export class HubPipeline {
558
647
  stageRecorder: outboundRecorder,
559
648
  requestId: normalized.id
560
649
  });
650
+ providerPayload = maybeApplyDirectBuiltinWebSearchTool(providerPayload, outboundAdapterContext, outboundProtocol);
561
651
  recordHubPolicyObservation({
562
652
  policy: effectivePolicy,
563
653
  providerProtocol: outboundProtocol,
@@ -1003,6 +1093,7 @@ export class HubPipeline {
1003
1093
  stageRecorder: outboundRecorder,
1004
1094
  requestId: normalized.id
1005
1095
  });
1096
+ providerPayload = maybeApplyDirectBuiltinWebSearchTool(providerPayload, outboundAdapterContext, outboundProtocol);
1006
1097
  recordHubPolicyObservation({
1007
1098
  policy: effectivePolicy,
1008
1099
  providerProtocol: outboundProtocol,
@@ -3,12 +3,7 @@ import { buildChatResponseFromResponses } from '../../../../../shared/responses-
3
3
  import { normalizeAssistantTextToToolCalls } from '../../../../../shared/text-markup-normalizer.js';
4
4
  import { recordStage } from '../../../stages/utils.js';
5
5
  import { applyRespProcessToolGovernanceWithNative, stripOrphanFunctionCallsTagWithNative } from '../../../../../../router/virtual-router/engine-selection/native-chat-process-governance-semantics.js';
6
- const SHELL_TOOL_NAME_ALIASES = {
7
- shell_command: 'exec_command',
8
- shell: 'exec_command',
9
- bash: 'exec_command',
10
- terminal: 'exec_command'
11
- };
6
+ const SHELL_TOOL_NAME_ALIASES = {};
12
7
  /**
13
8
  * Unified text-to-tool-calls harvest config.
14
9
  * Shared across all providers via chat process tool governance.
@@ -223,8 +223,7 @@ export function buildOpenAIChatFromAnthropicMessage(payload, options) {
223
223
  };
224
224
  const includeToolCallIds = options?.includeToolCallIds === true;
225
225
  const canonicalToolCalls = toolCalls.map((tc) => ({
226
- id: tc.id,
227
- ...(includeToolCallIds ? { call_id: tc.id, tool_call_id: tc.id } : {}),
226
+ ...(includeToolCallIds ? { id: tc.id, call_id: tc.id, tool_call_id: tc.id } : {}),
228
227
  type: 'function',
229
228
  function: { name: tc.name, arguments: tc.args }
230
229
  }));
@@ -246,6 +245,14 @@ export function buildOpenAIChatFromAnthropicMessage(payload, options) {
246
245
  if (!("tool_call_id" in inferred))
247
246
  inferred.tool_call_id = inferredId;
248
247
  }
248
+ else if (!includeToolCallIds) {
249
+ if ("id" in inferred)
250
+ delete inferred.id;
251
+ if ("call_id" in inferred)
252
+ delete inferred.call_id;
253
+ if ("tool_call_id" in inferred)
254
+ delete inferred.tool_call_id;
255
+ }
249
256
  canonicalToolCalls.push(inferred);
250
257
  if (key)
251
258
  seen.add(key);
@@ -259,9 +266,13 @@ export function buildOpenAIChatFromAnthropicMessage(payload, options) {
259
266
  call.call_id = cid;
260
267
  if (!("tool_call_id" in call))
261
268
  call.tool_call_id = cid;
269
+ if (!("id" in call))
270
+ call.id = cid;
262
271
  }
263
272
  }
264
273
  else {
274
+ if ("id" in call)
275
+ delete call.id;
265
276
  if ("call_id" in call)
266
277
  delete call.call_id;
267
278
  if ("tool_call_id" in call)
@@ -443,10 +454,7 @@ function sanitizeAnthropicMessage(message) {
443
454
  .filter((block) => block !== null);
444
455
  const usage = message.usage;
445
456
  if (usage && typeof usage === 'object') {
446
- sanitized.usage = {
447
- input_tokens: usage.input_tokens ?? 0,
448
- output_tokens: usage.output_tokens ?? 0
449
- };
457
+ sanitized.usage = JSON.parse(JSON.stringify(usage));
450
458
  }
451
459
  return sanitized;
452
460
  }
@@ -238,12 +238,6 @@ function buildClientToolIndex(toolsRaw) {
238
238
  }
239
239
  return index;
240
240
  }
241
- const CLIENT_TOOL_NAME_ALIASES = new Map([
242
- ['shell_command', 'exec_command'],
243
- ['shell', 'exec_command'],
244
- ['bash', 'exec_command'],
245
- ['terminal', 'exec_command']
246
- ]);
247
241
  function resolveClientToolName(toolIndex, rawToolName) {
248
242
  const trimmed = rawToolName.trim();
249
243
  if (!trimmed.length)
@@ -256,12 +250,18 @@ function resolveClientToolName(toolIndex, rawToolName) {
256
250
  return key;
257
251
  }
258
252
  }
259
- const aliased = CLIENT_TOOL_NAME_ALIASES.get(lower);
260
- if (!aliased)
253
+ const hasBash = Array.from(toolIndex.keys()).some((key) => key.toLowerCase() === 'bash');
254
+ if (hasBash)
261
255
  return undefined;
262
- for (const key of toolIndex.keys()) {
263
- if (key.toLowerCase() === aliased) {
264
- return key;
256
+ if (!['shell_command', 'exec_command', 'shell', 'terminal', 'bash'].includes(lower)) {
257
+ return undefined;
258
+ }
259
+ const preferred = ['shell_command', 'exec_command', 'shell', 'terminal'];
260
+ for (const candidate of preferred) {
261
+ for (const key of toolIndex.keys()) {
262
+ if (key.toLowerCase() === candidate) {
263
+ return key;
264
+ }
265
265
  }
266
266
  }
267
267
  return undefined;
@@ -186,12 +186,7 @@ function requireSystemText(block, context) {
186
186
  }
187
187
  return text;
188
188
  }
189
- const ANTHROPIC_TOOL_NAME_ALIASES = new Map([
190
- ['bash', 'shell_command'],
191
- ['shell', 'shell_command'],
192
- ['terminal', 'shell_command'],
193
- ['exec_command', 'shell_command']
194
- ]);
189
+ const ANTHROPIC_TOOL_NAME_ALIASES = new Map();
195
190
  const CANONICAL_TO_ANTHROPIC_TOOL_NAMES = new Map([['shell_command', 'Bash']]);
196
191
  const ANTHROPIC_TOP_LEVEL_FIELDS = new Set([
197
192
  'model',
@@ -1398,12 +1393,7 @@ export function buildAnthropicToolAliasMap(rawTools) {
1398
1393
  continue;
1399
1394
  }
1400
1395
  aliasMap.set(canonicalKey, rawName);
1401
- // Anthropic shell-like tools (e.g. Bash) and exec_command are treated as one family.
1402
- // Keep an explicit exec_command key so reverse mapping can preserve
1403
- // canonical exec_command when upstream already uses that name.
1404
- if (canonicalKey === 'shell_command' && !aliasMap.has('exec_command')) {
1405
- aliasMap.set('exec_command', rawName);
1406
- }
1396
+ const lowered = canonicalKey.toLowerCase();
1407
1397
  const lowerKey = canonicalKey.toLowerCase();
1408
1398
  if (lowerKey !== canonicalKey && !aliasMap.has(lowerKey)) {
1409
1399
  aliasMap.set(lowerKey, rawName);
@@ -1,6 +1,7 @@
1
1
  import { normalizeChatRequest } from '../index.js';
2
2
  import { createSnapshotWriter } from '../snapshot-utils.js';
3
3
  import { buildGovernedFilterPayloadWithNativeFallback } from '../../router/virtual-router/engine-selection/native-chat-request-filter-semantics.js';
4
+ import { pruneChatRequestPayloadWithNative } from '../../router/virtual-router/engine-selection/native-hub-pipeline-req-inbound-semantics.js';
4
5
  /**
5
6
  * Native-primary Chat request filters.
6
7
  *
@@ -56,67 +57,7 @@ export async function runStandardChatRequestFilters(chatRequest, profile, contex
56
57
  normalized.__rcc_disable_mcp_tools = true;
57
58
  }
58
59
  const preserveStreamField = profile.incomingProtocol === 'openai-chat' && profile.outgoingProtocol === 'openai-chat';
59
- const pruned = pruneChatRequestPayload(normalized, { preserveStreamField });
60
+ const pruned = pruneChatRequestPayloadWithNative(normalized, preserveStreamField);
60
61
  snapshotStage('req_process_filters_output', pruned);
61
62
  return pruned;
62
63
  }
63
- function pruneChatRequestPayload(chatRequest, options) {
64
- if (!chatRequest || typeof chatRequest !== 'object')
65
- return chatRequest;
66
- const stripped = { ...chatRequest };
67
- stripSentinelKeys(stripped);
68
- if ('originalStream' in stripped) {
69
- delete stripped.originalStream;
70
- }
71
- if ('_originalStreamOptions' in stripped) {
72
- delete stripped._originalStreamOptions;
73
- }
74
- if ('metadata' in stripped) {
75
- delete stripped.metadata;
76
- }
77
- if (!options?.preserveStreamField && 'stream' in stripped && stripped.stream !== true) {
78
- delete stripped.stream;
79
- }
80
- if (Array.isArray(stripped.messages)) {
81
- stripped.messages = stripped.messages.map((message) => sanitizeMessageEntry(message));
82
- }
83
- return stripped;
84
- }
85
- function sanitizeMessageEntry(message) {
86
- if (!message || typeof message !== 'object')
87
- return message;
88
- const clone = { ...message };
89
- if (Array.isArray(clone.tool_calls) && clone.tool_calls.length) {
90
- clone.tool_calls = clone.tool_calls.map((call) => sanitizeToolCallEntry(call));
91
- }
92
- if (clone.role === 'tool') {
93
- if (typeof clone.tool_call_id !== 'string' && typeof clone.call_id === 'string') {
94
- clone.tool_call_id = clone.call_id;
95
- }
96
- if ('id' in clone) {
97
- delete clone.id;
98
- }
99
- }
100
- if ('call_id' in clone) {
101
- delete clone.call_id;
102
- }
103
- return clone;
104
- }
105
- function sanitizeToolCallEntry(call) {
106
- if (!call || typeof call !== 'object')
107
- return call;
108
- const clone = { ...call };
109
- delete clone.call_id;
110
- delete clone.tool_call_id;
111
- if (clone.function && typeof clone.function === 'object') {
112
- clone.function = { ...clone.function };
113
- }
114
- return clone;
115
- }
116
- function stripSentinelKeys(record) {
117
- Object.keys(record).forEach((key) => {
118
- if (key.startsWith('__rcc_')) {
119
- delete record[key];
120
- }
121
- });
122
- }
@@ -1,4 +1,7 @@
1
1
  import { mapReasoningContentToResponsesOutputWithNative } from '../../router/virtual-router/engine-selection/native-shared-conversion-semantics.js';
2
2
  export function mapReasoningContentToResponsesOutput(reasoningContent) {
3
+ if (typeof mapReasoningContentToResponsesOutputWithNative !== 'function') {
4
+ throw new Error('[reasoning-mapping] native bindings unavailable');
5
+ }
3
6
  return mapReasoningContentToResponsesOutputWithNative(reasoningContent);
4
7
  }
@@ -19,4 +19,5 @@ export declare function normalizeReasoningInResponsesPayload(payload: {
19
19
  } | null | undefined, options?: ResponsesReasoningNormalizeOptions): void;
20
20
  export declare function normalizeReasoningInGeminiPayload(payload: JsonObject | null | undefined): void;
21
21
  export declare function normalizeReasoningInAnthropicPayload(payload: JsonObject | null | undefined): void;
22
+ export declare function normalizeReasoningInOpenAIPayload(payload: JsonObject | null | undefined): void;
22
23
  export {};