@jsonstudio/llms 0.6.1172 → 0.6.1354

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/codecs/gemini-openai-codec.d.ts +3 -1
  2. package/dist/conversion/codecs/gemini-openai-codec.js +10 -4
  3. package/dist/conversion/compat/actions/gemini-web-search.d.ts +1 -1
  4. package/dist/conversion/compat/actions/gemini-web-search.js +5 -2
  5. package/dist/conversion/compat/actions/iflow-tool-text-fallback.d.ts +12 -0
  6. package/dist/conversion/compat/actions/iflow-tool-text-fallback.js +199 -0
  7. package/dist/conversion/compat/actions/iflow-web-search.d.ts +1 -1
  8. package/dist/conversion/compat/actions/iflow-web-search.js +5 -2
  9. package/dist/conversion/hub/operation-table/semantic-mappers/anthropic-mapper.js +47 -56
  10. package/dist/conversion/hub/operation-table/semantic-mappers/chat-mapper.js +1 -13
  11. package/dist/conversion/hub/operation-table/semantic-mappers/gemini-mapper.js +523 -50
  12. package/dist/conversion/hub/operation-table/semantic-mappers/responses-mapper.js +18 -38
  13. package/dist/conversion/hub/pipeline/compat/compat-pipeline-executor.js +6 -0
  14. package/dist/conversion/hub/pipeline/compat/compat-types.d.ts +3 -0
  15. package/dist/conversion/hub/pipeline/hub-pipeline/adapter-context.d.ts +10 -0
  16. package/dist/conversion/hub/pipeline/hub-pipeline/adapter-context.js +134 -0
  17. package/dist/conversion/hub/pipeline/hub-pipeline/anthropic-alias-map.d.ts +6 -0
  18. package/dist/conversion/hub/pipeline/hub-pipeline/anthropic-alias-map.js +79 -0
  19. package/dist/conversion/hub/pipeline/hub-pipeline/apply-patch-tool-mode.d.ts +3 -0
  20. package/dist/conversion/hub/pipeline/hub-pipeline/apply-patch-tool-mode.js +46 -0
  21. package/dist/conversion/hub/pipeline/hub-pipeline/execute-chat-process-entry.d.ts +8 -0
  22. package/dist/conversion/hub/pipeline/hub-pipeline/execute-chat-process-entry.js +366 -0
  23. package/dist/conversion/hub/pipeline/hub-pipeline/execute-request-stage.d.ts +9 -0
  24. package/dist/conversion/hub/pipeline/hub-pipeline/execute-request-stage.js +384 -0
  25. package/dist/conversion/hub/pipeline/hub-pipeline/node-results.d.ts +3 -0
  26. package/dist/conversion/hub/pipeline/hub-pipeline/node-results.js +14 -0
  27. package/dist/conversion/hub/pipeline/hub-pipeline/payload-normalize.d.ts +2 -0
  28. package/dist/conversion/hub/pipeline/hub-pipeline/payload-normalize.js +144 -0
  29. package/dist/conversion/hub/pipeline/hub-pipeline/policy.d.ts +4 -0
  30. package/dist/conversion/hub/pipeline/hub-pipeline/policy.js +32 -0
  31. package/dist/conversion/hub/pipeline/hub-pipeline/protocol.d.ts +8 -0
  32. package/dist/conversion/hub/pipeline/hub-pipeline/protocol.js +63 -0
  33. package/dist/conversion/hub/pipeline/hub-pipeline/resolve-protocol-hooks.d.ts +2 -0
  34. package/dist/conversion/hub/pipeline/hub-pipeline/resolve-protocol-hooks.js +43 -0
  35. package/dist/conversion/hub/pipeline/hub-pipeline/semantic-gate.d.ts +1 -0
  36. package/dist/conversion/hub/pipeline/hub-pipeline/semantic-gate.js +29 -0
  37. package/dist/conversion/hub/pipeline/hub-pipeline/servertool-runtime-config.d.ts +2 -0
  38. package/dist/conversion/hub/pipeline/hub-pipeline/servertool-runtime-config.js +16 -0
  39. package/dist/conversion/hub/pipeline/hub-pipeline/types.d.ts +116 -0
  40. package/dist/conversion/hub/pipeline/hub-pipeline/types.js +1 -0
  41. package/dist/conversion/hub/pipeline/hub-pipeline.d.ts +3 -95
  42. package/dist/conversion/hub/pipeline/hub-pipeline.js +19 -1281
  43. package/dist/conversion/hub/pipeline/stages/req_inbound/req_inbound_stage1_format_parse/index.js +1 -1
  44. package/dist/conversion/hub/pipeline/stages/req_inbound/req_inbound_stage2_semantic_map/index.d.ts +7 -0
  45. package/dist/conversion/hub/pipeline/stages/req_inbound/req_inbound_stage2_semantic_map/index.js +65 -1
  46. package/dist/conversion/hub/pipeline/stages/req_inbound/req_inbound_stage3_context_capture/index.js +25 -22
  47. package/dist/conversion/hub/pipeline/stages/req_outbound/req_outbound_stage1_semantic_map/index.js +1 -1
  48. package/dist/conversion/hub/pipeline/stages/req_outbound/req_outbound_stage2_format_build/index.d.ts +1 -1
  49. package/dist/conversion/hub/pipeline/stages/req_outbound/req_outbound_stage2_format_build/index.js +2 -2
  50. package/dist/conversion/hub/pipeline/stages/req_outbound/req_outbound_stage3_compat/index.js +2 -2
  51. package/dist/conversion/hub/pipeline/stages/req_process/req_process_stage1_tool_governance/index.js +1 -1
  52. package/dist/conversion/hub/pipeline/stages/req_process/req_process_stage2_route_select/index.js +1 -1
  53. package/dist/conversion/hub/pipeline/stages/resp_inbound/resp_inbound_stage1_sse_decode/index.js +11 -11
  54. package/dist/conversion/hub/pipeline/stages/resp_inbound/resp_inbound_stage2_format_parse/index.js +1 -1
  55. package/dist/conversion/hub/pipeline/stages/resp_inbound/resp_inbound_stage3_semantic_map/index.d.ts +1 -0
  56. package/dist/conversion/hub/pipeline/stages/resp_inbound/resp_inbound_stage3_semantic_map/index.js +4 -2
  57. package/dist/conversion/hub/pipeline/stages/resp_outbound/resp_outbound_stage1_client_remap/index.d.ts +1 -0
  58. package/dist/conversion/hub/pipeline/stages/resp_outbound/resp_outbound_stage1_client_remap/index.js +17 -9
  59. package/dist/conversion/hub/pipeline/stages/resp_outbound/resp_outbound_stage2_sse_stream/index.js +2 -2
  60. package/dist/conversion/hub/pipeline/stages/resp_process/resp_process_stage1_tool_governance/index.js +40 -2
  61. package/dist/conversion/hub/pipeline/stages/resp_process/resp_process_stage2_finalize/index.js +1 -1
  62. package/dist/conversion/hub/pipeline/target-utils.js +9 -5
  63. package/dist/conversion/hub/process/chat-process.js +256 -16
  64. package/dist/conversion/hub/response/provider-response.d.ts +8 -0
  65. package/dist/conversion/hub/response/provider-response.js +85 -27
  66. package/dist/conversion/hub/response/response-mappers.d.ts +10 -3
  67. package/dist/conversion/hub/response/response-mappers.js +30 -6
  68. package/dist/conversion/hub/response/response-runtime.js +4 -38
  69. package/dist/conversion/hub/snapshot-recorder.js +5 -1
  70. package/dist/conversion/hub/standardized-bridge.js +23 -15
  71. package/dist/conversion/pipeline/codecs/v2/anthropic-openai-pipeline.js +36 -5
  72. package/dist/conversion/responses/responses-openai-bridge.js +20 -4
  73. package/dist/conversion/shared/gemini-tool-utils.d.ts +8 -1
  74. package/dist/conversion/shared/gemini-tool-utils.js +580 -108
  75. package/dist/conversion/shared/jsonish.js +1 -1
  76. package/dist/conversion/shared/mcp-injection.js +67 -33
  77. package/dist/conversion/shared/openai-finalizer.js +2 -1
  78. package/dist/conversion/shared/openai-message-normalize.js +76 -21
  79. package/dist/conversion/shared/responses-output-builder.js +6 -0
  80. package/dist/conversion/shared/runtime-metadata.d.ts +7 -0
  81. package/dist/conversion/shared/runtime-metadata.js +23 -0
  82. package/dist/conversion/shared/text-markup-normalizer.d.ts +2 -0
  83. package/dist/conversion/shared/text-markup-normalizer.js +284 -4
  84. package/dist/conversion/shared/tool-canonicalizer.js +2 -1
  85. package/dist/conversion/shared/tool-governor.js +3 -3
  86. package/dist/filters/engine.js +5 -5
  87. package/dist/filters/special/request-tool-list-filter.js +194 -60
  88. package/dist/filters/special/request-tools-normalize.js +1 -1
  89. package/dist/filters/special/response-tool-text-canonicalize.d.ts +4 -7
  90. package/dist/filters/special/response-tool-text-canonicalize.js +7 -35
  91. package/dist/filters/special/tool-filter-hooks.js +58 -62
  92. package/dist/guidance/index.js +5 -1
  93. package/dist/http/sse-response.js +6 -6
  94. package/dist/router/virtual-router/bootstrap.js +48 -4
  95. package/dist/router/virtual-router/engine-health.d.ts +1 -1
  96. package/dist/router/virtual-router/engine-health.js +11 -110
  97. package/dist/router/virtual-router/engine-selection/alias-selection.d.ts +15 -0
  98. package/dist/router/virtual-router/engine-selection/alias-selection.js +156 -0
  99. package/dist/router/virtual-router/engine-selection/context-weight-multipliers.d.ts +11 -0
  100. package/dist/router/virtual-router/engine-selection/context-weight-multipliers.js +23 -0
  101. package/dist/router/virtual-router/engine-selection/direct-provider-model.d.ts +9 -0
  102. package/dist/router/virtual-router/engine-selection/direct-provider-model.js +49 -0
  103. package/dist/router/virtual-router/engine-selection/instruction-target.d.ts +6 -0
  104. package/dist/router/virtual-router/engine-selection/instruction-target.js +54 -0
  105. package/dist/router/virtual-router/engine-selection/key-parsing.d.ts +8 -0
  106. package/dist/router/virtual-router/engine-selection/key-parsing.js +64 -0
  107. package/dist/router/virtual-router/engine-selection/route-utils.d.ts +12 -0
  108. package/dist/router/virtual-router/engine-selection/route-utils.js +150 -0
  109. package/dist/router/virtual-router/engine-selection/routing-state-filter.d.ts +4 -0
  110. package/dist/router/virtual-router/engine-selection/routing-state-filter.js +50 -0
  111. package/dist/router/virtual-router/engine-selection/selection-deps.d.ts +39 -0
  112. package/dist/router/virtual-router/engine-selection/selection-deps.js +1 -0
  113. package/dist/router/virtual-router/engine-selection/sticky-pool.d.ts +11 -0
  114. package/dist/router/virtual-router/engine-selection/sticky-pool.js +109 -0
  115. package/dist/router/virtual-router/engine-selection/tier-priority.d.ts +12 -0
  116. package/dist/router/virtual-router/engine-selection/tier-priority.js +55 -0
  117. package/dist/router/virtual-router/engine-selection/tier-selection-select.d.ts +22 -0
  118. package/dist/router/virtual-router/engine-selection/tier-selection-select.js +400 -0
  119. package/dist/router/virtual-router/engine-selection/tier-selection.d.ts +3 -0
  120. package/dist/router/virtual-router/engine-selection/tier-selection.js +225 -0
  121. package/dist/router/virtual-router/engine-selection.d.ts +4 -30
  122. package/dist/router/virtual-router/engine-selection.js +10 -962
  123. package/dist/router/virtual-router/engine.d.ts +1 -0
  124. package/dist/router/virtual-router/engine.js +55 -10
  125. package/dist/router/virtual-router/routing-instructions.js +6 -1
  126. package/dist/router/virtual-router/stop-message-state-sync.d.ts +5 -0
  127. package/dist/router/virtual-router/stop-message-state-sync.js +6 -14
  128. package/dist/router/virtual-router/types.d.ts +25 -1
  129. package/dist/servertool/clock/config.d.ts +8 -0
  130. package/dist/servertool/clock/config.js +22 -0
  131. package/dist/servertool/clock/log.d.ts +3 -0
  132. package/dist/servertool/clock/log.js +13 -0
  133. package/dist/servertool/clock/task-store.d.ts +1 -1
  134. package/dist/servertool/clock/task-store.js +1 -1
  135. package/dist/servertool/clock/tasks.js +1 -1
  136. package/dist/servertool/engine.js +146 -21
  137. package/dist/servertool/handlers/clock-auto.js +11 -6
  138. package/dist/servertool/handlers/clock.js +36 -10
  139. package/dist/servertool/handlers/followup-request-builder.js +8 -2
  140. package/dist/servertool/handlers/gemini-empty-reply-continue.js +15 -9
  141. package/dist/servertool/handlers/iflow-model-error-retry.js +6 -4
  142. package/dist/servertool/handlers/recursive-detection-guard.js +4 -2
  143. package/dist/servertool/handlers/stop-message-auto.js +100 -10
  144. package/dist/servertool/handlers/vision.js +4 -1
  145. package/dist/servertool/handlers/web-search.js +3 -1
  146. package/dist/servertool/pending-session.d.ts +19 -0
  147. package/dist/servertool/pending-session.js +97 -0
  148. package/dist/servertool/reenter-backend.js +5 -3
  149. package/dist/servertool/server-side-tools.js +235 -6
  150. package/dist/servertool/types.d.ts +13 -0
  151. package/dist/sse/json-to-sse/event-generators/responses.js +1 -1
  152. package/dist/sse/shared/chat-serializer.js +2 -2
  153. package/dist/sse/shared/constants.js +1 -1
  154. package/dist/sse/sse-to-json/anthropic-sse-to-json-converter.d.ts +7 -1
  155. package/dist/sse/sse-to-json/builders/response-builder.js +16 -0
  156. package/dist/sse/sse-to-json/responses-sse-to-json-converter.d.ts +1 -1
  157. package/dist/tools/apply-patch/execution-capturer.js +1 -1
  158. package/dist/tools/exec-command/normalize.js +4 -0
  159. package/dist/tools/exec-command/regression-capturer.js +1 -1
  160. package/package.json +10 -5
@@ -132,7 +132,7 @@ export function repairArgumentsToString(value) {
132
132
  catch { /* ignore */ }
133
133
  // 6) quote unquoted keys
134
134
  try {
135
- s = s.replace(/([\{,\s])([A-Za-z_][A-Za-z0-9_-]*)\s*:/g, '$1"$2":');
135
+ s = s.replace(/([{,\s])([A-Za-z_][A-Za-z0-9_-]*)\s*:/g, '$1"$2":');
136
136
  }
137
137
  catch { /* ignore */ }
138
138
  // 7) try parse after cleanup
@@ -11,10 +11,48 @@ const obj = (props, req = []) => {
11
11
  return schema;
12
12
  };
13
13
  function uniq(arr) { return Array.from(new Set(arr)); }
14
+ function buildKnownServersLine(discoveredServers) {
15
+ if (!Array.isArray(discoveredServers) || discoveredServers.length === 0)
16
+ return '';
17
+ const list = uniq(discoveredServers).slice(0, 8);
18
+ const suffix = discoveredServers.length > list.length ? ` (+${discoveredServers.length - list.length} more)` : '';
19
+ return `Known MCP servers: ${list.join(', ')}${suffix}.`;
20
+ }
21
+ function buildMcpServerReminder() {
22
+ return 'Note: arguments.server is an MCP server label (NOT a tool name like shell/exec_command/apply_patch).';
23
+ }
24
+ function buildListResourcesDescription(discoveredServers) {
25
+ return [
26
+ 'List resources exposed by MCP servers.',
27
+ 'Only use this for MCP resources (not MCP tools). Many MCP servers expose tools only; if the result is empty, do not retry.',
28
+ 'If you do not know the MCP server name yet, call this tool with {} once; then reuse the returned server names for subsequent calls.',
29
+ buildMcpServerReminder(),
30
+ buildKnownServersLine(discoveredServers)
31
+ ].filter(Boolean).join('\n');
32
+ }
33
+ function buildListTemplatesDescription(discoveredServers) {
34
+ return [
35
+ 'List resource templates exposed by MCP servers.',
36
+ 'Only use this for MCP resources (not MCP tools). If list_mcp_resources returns empty, do not retry.',
37
+ 'If you do not know the MCP server name yet, call list_mcp_resources({}) once first.',
38
+ buildMcpServerReminder(),
39
+ buildKnownServersLine(discoveredServers)
40
+ ].filter(Boolean).join('\n');
41
+ }
42
+ function buildReadResourceDescription(discoveredServers) {
43
+ return [
44
+ 'Read a specific MCP resource by { server, uri }.',
45
+ 'Only use this for MCP resources (not MCP tools). If list_mcp_resources returns empty, do not retry.',
46
+ 'If you do not know the MCP server name yet, call list_mcp_resources({}) once first.',
47
+ buildMcpServerReminder(),
48
+ buildKnownServersLine(discoveredServers)
49
+ ].filter(Boolean).join('\n');
50
+ }
14
51
  export function injectMcpToolsForChat(tools, discoveredServers) {
15
52
  const out = [];
16
53
  const keep = new Set();
17
- const listServers = discoveredServers && discoveredServers.length ? { type: 'string', enum: uniq(discoveredServers) } : { type: 'string' };
54
+ const listServers = discoveredServers && discoveredServers.length ? { type: 'string', enum: uniq(discoveredServers), minLength: 1 } : { type: 'string', minLength: 1 };
55
+ const templateServer = discoveredServers && discoveredServers.length ? { type: 'string', enum: uniq(discoveredServers), minLength: 1 } : { type: 'string', minLength: 1 };
18
56
  const ensure = (name, def) => { if (!keep.has(name)) {
19
57
  out.push(def);
20
58
  keep.add(name);
@@ -28,35 +66,45 @@ export function injectMcpToolsForChat(tools, discoveredServers) {
28
66
  continue;
29
67
  }
30
68
  if (lower === 'list_mcp_resources') {
31
- ensure('list_mcp_resources', { type: 'function', function: { name: 'list_mcp_resources', description: t.function?.description || 'List resources from a given MCP server (arguments.server = server label).', parameters: obj({ server: listServers, filter: { type: 'string' }, root: { type: 'string' } }) } });
69
+ ensure('list_mcp_resources', { type: 'function', function: { name: 'list_mcp_resources', description: (t.function?.description && String(t.function.description).trim()) ? t.function.description : buildListResourcesDescription(discoveredServers), parameters: obj({ server: listServers, filter: { type: 'string' }, root: { type: 'string' } }) } });
70
+ continue;
71
+ }
72
+ if (lower === 'list_mcp_resource_templates') {
73
+ ensure('list_mcp_resource_templates', {
74
+ type: 'function',
75
+ function: {
76
+ name: 'list_mcp_resource_templates',
77
+ description: (t.function?.description && String(t.function.description).trim()) ? t.function.description : buildListTemplatesDescription(discoveredServers),
78
+ parameters: obj({ server: templateServer, cursor: { type: 'string' } })
79
+ }
80
+ });
32
81
  continue;
33
82
  }
34
- if (lower === 'read_mcp_resource' || lower === 'list_mcp_resource_templates') {
83
+ if (lower === 'read_mcp_resource') {
84
+ // Phase gating: do not expose read until we have known server labels.
35
85
  if (discoveredServers.length > 0) {
36
86
  const srv = { type: 'string', enum: uniq(discoveredServers) };
37
- if (lower === 'read_mcp_resource') {
38
- ensure('read_mcp_resource', { type: 'function', function: { name: 'read_mcp_resource', description: t.function?.description || 'Read a resource from a given MCP server.', parameters: obj({ server: srv, uri: { type: 'string' }, cursor: { type: 'string' } }, ['server', 'uri']) } });
39
- }
40
- else {
41
- ensure('list_mcp_resource_templates', { type: 'function', function: { name: 'list_mcp_resource_templates', description: t.function?.description || 'List resource templates from a given MCP server.', parameters: obj({ server: srv, cursor: { type: 'string' } }, ['server']) } });
42
- }
43
- continue;
87
+ ensure('read_mcp_resource', {
88
+ type: 'function',
89
+ function: {
90
+ name: 'read_mcp_resource',
91
+ description: (t.function?.description && String(t.function.description).trim()) ? t.function.description : buildReadResourceDescription(discoveredServers),
92
+ parameters: obj({ server: srv, uri: { type: 'string' } }, ['server', 'uri'])
93
+ }
94
+ });
44
95
  }
96
+ continue;
45
97
  }
46
98
  out.push(t);
47
99
  keep.add(lower);
48
100
  }
49
- // Ensure list exists when user did not provide it
50
- if (!keep.has('list_mcp_resources')) {
51
- out.push({ type: 'function', function: { name: 'list_mcp_resources', description: 'List resources from a given MCP server (arguments.server = server label).', parameters: obj({ server: listServers, filter: { type: 'string' }, root: { type: 'string' } }) } });
52
- }
53
101
  return out;
54
102
  }
55
103
  export function injectMcpToolsForResponses(tools, discoveredServers) {
56
104
  const out = [];
57
105
  const keep = new Set();
58
- const listServers = { type: 'string' };
59
- const templateServer = discoveredServers && discoveredServers.length ? { type: 'string', enum: uniq(discoveredServers) } : { type: 'string' };
106
+ const listServers = { type: 'string', minLength: 1 };
107
+ const templateServer = discoveredServers && discoveredServers.length ? { type: 'string', enum: uniq(discoveredServers), minLength: 1 } : { type: 'string', minLength: 1 };
60
108
  const ensure = (name, def) => { if (!keep.has(name)) {
61
109
  out.push(def);
62
110
  keep.add(name);
@@ -70,7 +118,7 @@ export function injectMcpToolsForResponses(tools, discoveredServers) {
70
118
  continue;
71
119
  }
72
120
  if (lower === 'list_mcp_resources') {
73
- ensure('list_mcp_resources', { type: 'function', function: { name: 'list_mcp_resources', description: t.function?.description || 'List resources from a given MCP server (arguments.server = server label).', parameters: obj({ server: listServers, filter: { type: 'string' }, root: { type: 'string' } }) } });
121
+ ensure('list_mcp_resources', { type: 'function', function: { name: 'list_mcp_resources', description: (t.function?.description && String(t.function.description).trim()) ? t.function.description : buildListResourcesDescription(discoveredServers), parameters: obj({ server: listServers, filter: { type: 'string' }, root: { type: 'string' } }) } });
74
122
  continue;
75
123
  }
76
124
  if (lower === 'list_mcp_resource_templates') {
@@ -78,7 +126,7 @@ export function injectMcpToolsForResponses(tools, discoveredServers) {
78
126
  type: 'function',
79
127
  function: {
80
128
  name: 'list_mcp_resource_templates',
81
- description: t.function?.description || 'List resource templates from MCP servers.',
129
+ description: (t.function?.description && String(t.function.description).trim()) ? t.function.description : buildListTemplatesDescription(discoveredServers),
82
130
  parameters: obj({ server: templateServer, cursor: { type: 'string' } })
83
131
  }
84
132
  });
@@ -86,25 +134,11 @@ export function injectMcpToolsForResponses(tools, discoveredServers) {
86
134
  }
87
135
  if (lower === 'read_mcp_resource' && discoveredServers.length > 0) {
88
136
  const srv = { type: 'string', enum: uniq(discoveredServers) };
89
- ensure('read_mcp_resource', { type: 'function', function: { name: 'read_mcp_resource', description: t.function?.description || 'Read a resource via MCP server.', parameters: obj({ server: srv, uri: { type: 'string' } }, ['server', 'uri']) } });
137
+ ensure('read_mcp_resource', { type: 'function', function: { name: 'read_mcp_resource', description: (t.function?.description && String(t.function.description).trim()) ? t.function.description : buildReadResourceDescription(discoveredServers), parameters: obj({ server: srv, uri: { type: 'string' } }, ['server', 'uri']) } });
90
138
  continue;
91
139
  }
92
140
  out.push(t);
93
141
  keep.add(lower);
94
142
  }
95
- if (!keep.has('list_mcp_resources')) {
96
- out.push({ type: 'function', function: { name: 'list_mcp_resources', description: 'List resources from a given MCP server (arguments.server = server label).', parameters: obj({ server: listServers, filter: { type: 'string' }, root: { type: 'string' } }) } });
97
- keep.add('list_mcp_resources');
98
- }
99
- if (!keep.has('list_mcp_resource_templates')) {
100
- out.push({
101
- type: 'function',
102
- function: {
103
- name: 'list_mcp_resource_templates',
104
- description: 'List resource templates from MCP servers.',
105
- parameters: obj({ server: templateServer, cursor: { type: 'string' } })
106
- }
107
- });
108
- }
109
143
  return out;
110
144
  }
@@ -59,8 +59,9 @@ export async function finalizeOpenAIChatResponse(chatLike, opts) {
59
59
  }
60
60
  catch { /* ignore */ }
61
61
  }
62
- if (first.finish_reason == null)
62
+ if (String(first.finish_reason || '').toLowerCase() !== 'tool_calls') {
63
63
  first.finish_reason = 'tool_calls';
64
+ }
64
65
  }
65
66
  }
66
67
  // nothing more to do for Chat completion response
@@ -3,6 +3,14 @@ import { injectMcpToolsForChat } from './mcp-injection.js';
3
3
  // with the deprecated "openai-normalizer" module entry). This file contains the
4
4
  // previously-implemented logic from openai-normalize.ts.
5
5
  // Legacy tooling stage removed for Chat; tool canonicalization lives in codecs
6
+ function isMcpToolName(name) {
7
+ if (typeof name !== 'string')
8
+ return false;
9
+ const lowered = name.trim().toLowerCase();
10
+ return (lowered === 'list_mcp_resources' ||
11
+ lowered === 'list_mcp_resource_templates' ||
12
+ lowered === 'read_mcp_resource');
13
+ }
6
14
  export function normalizeChatRequest(request) {
7
15
  if (!request || typeof request !== 'object')
8
16
  return request;
@@ -54,38 +62,85 @@ export function normalizeChatRequest(request) {
54
62
  const enableMcp = String(process?.env?.ROUTECODEX_MCP_ENABLE ?? '1') !== '0';
55
63
  if (enableMcp) {
56
64
  const known = new Set();
65
+ const fromEnv = String(process?.env?.RCC_MCP_SERVERS || '').trim();
66
+ if (fromEnv) {
67
+ for (const s of fromEnv.split(',').map((x) => x.trim()).filter(Boolean))
68
+ known.add(s);
69
+ }
70
+ const addServer = (v) => {
71
+ if (typeof v === 'string') {
72
+ const s = v.trim();
73
+ if (s)
74
+ known.add(s);
75
+ }
76
+ };
77
+ const extractFromOutput = (output) => {
78
+ try {
79
+ if (Array.isArray(output)) {
80
+ for (const item of output) {
81
+ if (typeof item === 'string')
82
+ addServer(item);
83
+ else if (item && typeof item === 'object' && !Array.isArray(item))
84
+ addServer(item.server);
85
+ }
86
+ return;
87
+ }
88
+ if (!output || typeof output !== 'object' || Array.isArray(output))
89
+ return;
90
+ const o = output;
91
+ if (Array.isArray(o.servers))
92
+ for (const s of o.servers)
93
+ addServer(s);
94
+ if (Array.isArray(o.resources))
95
+ for (const r of o.resources)
96
+ addServer(r?.server ?? r?.source?.server);
97
+ if (Array.isArray(o.resourceTemplates))
98
+ for (const t of o.resourceTemplates)
99
+ addServer(t?.server ?? t?.source?.server);
100
+ }
101
+ catch {
102
+ // best-effort
103
+ }
104
+ };
105
+ // IMPORTANT: do NOT treat assistant tool_calls as authoritative for MCP server labels
106
+ // (the model may guess "shell"/"exec_command"/etc). Only trust tool results.
57
107
  try {
58
108
  const msgs = Array.isArray(normalized.messages) ? normalized.messages : [];
59
109
  for (const m of msgs) {
60
110
  if (!m || typeof m !== 'object')
61
111
  continue;
62
- if (m.role === 'tool' && typeof m.content === 'string') {
63
- try {
64
- const obj = JSON.parse(m.content);
65
- const sv = obj?.arguments?.server;
66
- if (typeof sv === 'string' && sv.trim())
67
- known.add(sv.trim());
68
- }
69
- catch { /* ignore */ }
70
- }
71
- if (m.role === 'assistant' && Array.isArray(m.tool_calls)) {
72
- for (const tc of m.tool_calls) {
73
- try {
74
- const argStr = String(tc?.function?.arguments ?? '');
75
- const parsed = JSON.parse(argStr);
76
- const sv = parsed?.server;
77
- if (typeof sv === 'string' && sv.trim())
78
- known.add(sv.trim());
112
+ if (String(m.role || '').toLowerCase() !== 'tool')
113
+ continue;
114
+ const content = m.content;
115
+ if (typeof content !== 'string' || content.trim().length === 0)
116
+ continue;
117
+ try {
118
+ const parsed = JSON.parse(content);
119
+ if (parsed && typeof parsed === 'object' && parsed.version === 'rcc.tool.v1' && parsed.tool?.name) {
120
+ const toolName = String(parsed.tool.name).toLowerCase();
121
+ if (toolName === 'list_mcp_resources') {
122
+ extractFromOutput(parsed.result?.output);
79
123
  }
80
- catch { /* ignore */ }
124
+ }
125
+ else {
126
+ extractFromOutput(parsed?.output ?? parsed);
81
127
  }
82
128
  }
129
+ catch {
130
+ // ignore
131
+ }
83
132
  }
84
133
  }
85
134
  catch { /* ignore */ }
86
- const discovered = Array.from(known);
87
- const currentTools = Array.isArray(normalized.tools) ? normalized.tools : [];
88
- normalized.tools = injectMcpToolsForChat(currentTools, discovered);
135
+ const currentTools = Array.isArray(normalized.tools)
136
+ ? normalized.tools
137
+ : undefined;
138
+ // IMPORTANT: Do not force-inject MCP tools. If the inbound request doesn't include MCP tools,
139
+ // keep tools untouched (and do not create a tools=[] placeholder).
140
+ if (currentTools && currentTools.some((t) => isMcpToolName(t?.function?.name))) {
141
+ const discovered = Array.from(known);
142
+ normalized.tools = injectMcpToolsForChat(currentTools, discovered);
143
+ }
89
144
  }
90
145
  }
91
146
  catch { /* ignore MCP injection */ }
@@ -243,8 +243,14 @@ function buildRequiredActionFromNormalized(calls) {
243
243
  if (!calls.length)
244
244
  return undefined;
245
245
  const submitCalls = calls.map((entry) => ({
246
+ // Internal + client compat:
247
+ // - keep OpenAI-style `id` + `function`
248
+ // - also expose `tool_call_id` + top-level `name` for legacy/bridge clients
246
249
  id: entry.id,
250
+ tool_call_id: entry.id,
247
251
  type: 'function',
252
+ name: entry.name,
253
+ arguments: entry.args,
248
254
  function: {
249
255
  name: entry.name,
250
256
  arguments: entry.args
@@ -0,0 +1,7 @@
1
+ import { type JsonObject } from '../hub/types/json.js';
2
+ export type RuntimeMetadataCarrier = Record<string, unknown> & {
3
+ __rt?: JsonObject;
4
+ };
5
+ export declare function readRuntimeMetadata(carrier?: Record<string, unknown> | null): JsonObject | undefined;
6
+ export declare function ensureRuntimeMetadata(carrier: Record<string, unknown>): JsonObject;
7
+ export declare function cloneRuntimeMetadata(carrier?: Record<string, unknown> | null): JsonObject | undefined;
@@ -0,0 +1,23 @@
1
+ import { isJsonObject, jsonClone } from '../hub/types/json.js';
2
+ export function readRuntimeMetadata(carrier) {
3
+ if (!carrier || typeof carrier !== 'object') {
4
+ return undefined;
5
+ }
6
+ const candidate = carrier.__rt;
7
+ return candidate && isJsonObject(candidate) ? candidate : undefined;
8
+ }
9
+ export function ensureRuntimeMetadata(carrier) {
10
+ if (!carrier || typeof carrier !== 'object') {
11
+ throw new Error('ensureRuntimeMetadata requires object carrier');
12
+ }
13
+ const existing = readRuntimeMetadata(carrier);
14
+ if (existing) {
15
+ return existing;
16
+ }
17
+ carrier.__rt = {};
18
+ return carrier.__rt;
19
+ }
20
+ export function cloneRuntimeMetadata(carrier) {
21
+ const rt = readRuntimeMetadata(carrier);
22
+ return rt ? jsonClone(rt) : undefined;
23
+ }
@@ -3,7 +3,9 @@ export type ToolCallLite = {
3
3
  name: string;
4
4
  args: string;
5
5
  };
6
+ export declare function extractToolNamespaceXmlBlocksFromText(text: string): ToolCallLite[] | null;
6
7
  export declare function extractParameterXmlToolsFromText(text: string): ToolCallLite[] | null;
8
+ export declare function extractInvokeToolsFromText(text: string): ToolCallLite[] | null;
7
9
  export declare function extractBareExecCommandFromText(text: string): ToolCallLite[] | null;
8
10
  export declare function extractApplyPatchCallsFromText(text: string): ToolCallLite[] | null;
9
11
  export declare function extractExecuteBlocksFromText(text: string): ToolCallLite[] | null;