@jsonstudio/llms 0.6.1449 → 0.6.1643

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 (71) hide show
  1. package/dist/conversion/codecs/gemini-openai-codec.js +6 -1
  2. package/dist/conversion/compat/actions/anthropic-claude-code-system-prompt.d.ts +4 -6
  3. package/dist/conversion/compat/actions/anthropic-claude-code-system-prompt.js +179 -41
  4. package/dist/conversion/compat/actions/antigravity-thought-signature-cache.js +73 -14
  5. package/dist/conversion/compat/actions/antigravity-thought-signature-prepare.js +165 -10
  6. package/dist/conversion/compat/actions/gemini-cli-request.js +72 -13
  7. package/dist/conversion/compat/antigravity-session-signature.d.ts +68 -1
  8. package/dist/conversion/compat/antigravity-session-signature.js +833 -21
  9. package/dist/conversion/compat/profiles/anthropic-claude-code.json +17 -0
  10. package/dist/conversion/compat/profiles/chat-gemini-cli.json +1 -0
  11. package/dist/conversion/hub/operation-table/semantic-mappers/gemini-mapper.js +33 -8
  12. package/dist/conversion/hub/pipeline/compat/compat-pipeline-executor.js +17 -1
  13. package/dist/conversion/hub/pipeline/compat/compat-profile-store.js +12 -3
  14. package/dist/conversion/hub/pipeline/hub-pipeline.d.ts +1 -0
  15. package/dist/conversion/hub/pipeline/hub-pipeline.js +24 -0
  16. package/dist/conversion/hub/pipeline/stages/req_inbound/req_inbound_stage2_semantic_map/index.js +20 -0
  17. package/dist/conversion/hub/pipeline/stages/resp_outbound/resp_outbound_stage1_client_remap/index.js +26 -1
  18. package/dist/conversion/hub/process/chat-process.js +300 -67
  19. package/dist/conversion/hub/response/provider-response.js +4 -3
  20. package/dist/conversion/shared/gemini-tool-utils.js +134 -9
  21. package/dist/conversion/shared/text-markup-normalizer.js +90 -1
  22. package/dist/conversion/shared/thought-signature-validator.d.ts +1 -1
  23. package/dist/conversion/shared/thought-signature-validator.js +2 -1
  24. package/dist/quota/apikey-reset.d.ts +17 -0
  25. package/dist/quota/apikey-reset.js +43 -0
  26. package/dist/quota/index.d.ts +2 -0
  27. package/dist/quota/index.js +1 -0
  28. package/dist/quota/quota-manager.d.ts +44 -0
  29. package/dist/quota/quota-manager.js +491 -0
  30. package/dist/quota/quota-state.d.ts +6 -0
  31. package/dist/quota/quota-state.js +167 -0
  32. package/dist/quota/types.d.ts +61 -0
  33. package/dist/quota/types.js +1 -0
  34. package/dist/router/virtual-router/bootstrap.js +103 -6
  35. package/dist/router/virtual-router/engine-health.js +104 -0
  36. package/dist/router/virtual-router/engine-selection/selection-deps.d.ts +18 -0
  37. package/dist/router/virtual-router/engine-selection/tier-priority.d.ts +1 -2
  38. package/dist/router/virtual-router/engine-selection/tier-priority.js +2 -2
  39. package/dist/router/virtual-router/engine-selection/tier-selection-select.js +34 -10
  40. package/dist/router/virtual-router/engine-selection/tier-selection.js +250 -6
  41. package/dist/router/virtual-router/engine-selection.js +2 -2
  42. package/dist/router/virtual-router/engine.d.ts +16 -1
  43. package/dist/router/virtual-router/engine.js +320 -42
  44. package/dist/router/virtual-router/features.js +20 -2
  45. package/dist/router/virtual-router/success-center.d.ts +10 -0
  46. package/dist/router/virtual-router/success-center.js +32 -0
  47. package/dist/router/virtual-router/types.d.ts +48 -0
  48. package/dist/servertool/clock/config.d.ts +2 -0
  49. package/dist/servertool/clock/config.js +10 -2
  50. package/dist/servertool/clock/daemon.js +3 -0
  51. package/dist/servertool/clock/ntp.d.ts +18 -0
  52. package/dist/servertool/clock/ntp.js +318 -0
  53. package/dist/servertool/clock/paths.d.ts +1 -0
  54. package/dist/servertool/clock/paths.js +3 -0
  55. package/dist/servertool/clock/state.d.ts +2 -0
  56. package/dist/servertool/clock/state.js +15 -2
  57. package/dist/servertool/clock/tasks.d.ts +1 -0
  58. package/dist/servertool/clock/tasks.js +24 -1
  59. package/dist/servertool/clock/types.d.ts +21 -0
  60. package/dist/servertool/engine.js +105 -1
  61. package/dist/servertool/handlers/antigravity-thought-signature-bootstrap.d.ts +1 -0
  62. package/dist/servertool/handlers/antigravity-thought-signature-bootstrap.js +201 -0
  63. package/dist/servertool/handlers/clock-auto.js +39 -4
  64. package/dist/servertool/handlers/clock.js +145 -16
  65. package/dist/servertool/handlers/followup-request-builder.js +84 -0
  66. package/dist/servertool/handlers/stop-message-auto.js +1 -1
  67. package/dist/servertool/server-side-tools.d.ts +1 -0
  68. package/dist/servertool/server-side-tools.js +1 -0
  69. package/dist/servertool/types.d.ts +2 -0
  70. package/dist/tools/apply-patch/execution-capturer.js +24 -3
  71. package/package.json +3 -2
@@ -0,0 +1,17 @@
1
+ {
2
+ "id": "anthropic:claude-code",
3
+ "protocol": "anthropic-messages",
4
+ "request": {
5
+ "mappings": [
6
+ { "action": "snapshot", "phase": "compat-pre" },
7
+ {
8
+ "action": "anthropic_claude_code_system_prompt",
9
+ "config": { "preserveExistingSystemAsUserMessage": true }
10
+ },
11
+ { "action": "snapshot", "phase": "compat-post" }
12
+ ]
13
+ },
14
+ "response": {
15
+ "mappings": []
16
+ }
17
+ }
@@ -5,6 +5,7 @@
5
5
  "mappings": [
6
6
  { "action": "snapshot", "phase": "compat-pre" },
7
7
  { "action": "claude_thinking_tool_schema" },
8
+ { "action": "antigravity_thought_signature_prepare" },
8
9
  { "action": "gemini_cli_request_wrap" },
9
10
  {
10
11
  "action": "shallow_pick",
@@ -6,6 +6,7 @@ import { prepareGeminiToolsForBridge, buildGeminiToolsFromBridge } from '../../.
6
6
  import { ensureProtocolState, getProtocolState } from '../../../shared/protocol-state.js';
7
7
  import { sanitizeReasoningTaggedText } from '../../../shared/reasoning-utils.js';
8
8
  import { applyClaudeThinkingToolSchemaCompat } from '../../../compat/actions/claude-thinking-tools.js';
9
+ import { extractAntigravityGeminiSessionId } from '../../../compat/antigravity-session-signature.js';
9
10
  const GENERATION_CONFIG_KEYS = [
10
11
  { source: 'temperature', target: 'temperature' },
11
12
  { source: 'topP', target: 'top_p' },
@@ -660,23 +661,25 @@ function buildGeminiRequestFromChat(chat, metadata) {
660
661
  const isAntigravityProvider = providerIdPrefix === 'antigravity';
661
662
  const isGeminiCliProvider = providerIdPrefix === 'gemini-cli';
662
663
  const requiresThoughtSignature = isAntigravityProvider || isGeminiCliProvider;
664
+ const parameters = chat.parameters && typeof chat.parameters === 'object' ? chat.parameters : {};
663
665
  const isAntigravityClaudeThinking = providerIdPrefix === 'antigravity' &&
664
- typeof chat.parameters?.model === 'string' &&
665
- chat.parameters.model.includes('claude-sonnet-4-5-thinking');
666
- const keepReasoning = Boolean(chat.parameters.keep_thinking) ||
667
- Boolean(chat.parameters.keep_reasoning);
666
+ typeof parameters.model === 'string' &&
667
+ String(parameters.model).includes('claude-sonnet-4-5-thinking');
668
+ const keepReasoning = Boolean(parameters.keep_thinking) ||
669
+ Boolean(parameters.keep_reasoning);
668
670
  const stripReasoningTags = isAntigravityProvider &&
669
- typeof chat.parameters?.model === 'string' &&
670
- chat.parameters.model.startsWith('claude-') &&
671
+ typeof parameters.model === 'string' &&
672
+ String(parameters.model).startsWith('claude-') &&
671
673
  !keepReasoning;
672
674
  // Cloud Code / Antigravity requires stable tool call IDs in context (maps to tool_use.id),
673
675
  // while standard Gemini endpoints do not require (and may reject) extra id fields.
674
676
  const includeToolCallIds = providerIdPrefix === 'antigravity';
675
677
  // Function calling protocol:
676
- // - For gemini-cli.* we keep a conservative path (text-only tool transcripts).
678
+ // - For gemini-cli.* we allow structured tool loops when tools are present (Codex/agent clients),
679
+ // otherwise keep a conservative path (text-only tool transcripts).
677
680
  // - For antigravity.* and other Gemini backends, send tool schemas and emit functionCall/functionResponse parts
678
681
  // so tool loops remain structured and recoverable.
679
- const allowFunctionCallingProtocol = providerIdPrefix !== 'gemini-cli';
682
+ const allowFunctionCallingProtocol = providerIdPrefix !== 'gemini-cli' || (Array.isArray(chat.tools) && chat.tools.length > 0);
680
683
  const omitFunctionCallPartsForCli = !allowFunctionCallingProtocol;
681
684
  const semanticsNode = readGeminiSemantics(chat);
682
685
  const systemTextBlocksFromSemantics = readSystemTextBlocksFromSemantics(chat);
@@ -959,12 +962,27 @@ function buildGeminiRequestFromChat(chat, metadata) {
959
962
  }
960
963
  deepCleanUndefined(requestPayload);
961
964
  const mappedLower = String(requestPayload.model || '').toLowerCase();
965
+ const isFlashModel = mappedLower.includes('flash');
962
966
  const isImageModel = config.requestType === 'image_gen' || mappedLower.includes('image');
963
967
  const isThinkingModel = !isImageModel && (mappedLower.includes('think') || mappedLower.includes('pro'));
964
968
  if (isThinkingModel && (!requestPayload.generationConfig || !isJsonObject(requestPayload.generationConfig))) {
965
969
  requestPayload.generationConfig = {};
966
970
  }
967
971
  const generationConfig = requestPayload.generationConfig;
972
+ if (isFlashModel && isJsonObject(generationConfig)) {
973
+ // Antigravity-Manager alignment: Gemini Flash models reject overly-large thinking budgets.
974
+ // Clamp proactively to avoid 400 Bad Request when clients set an aggressive budget.
975
+ const MAX_FLASH_THINKING_BUDGET = 24576;
976
+ const gc = generationConfig;
977
+ const thinkingConfigRaw = gc.thinkingConfig;
978
+ const thinkingConfig = isJsonObject(thinkingConfigRaw) ? thinkingConfigRaw : undefined;
979
+ const budgetRaw = thinkingConfig && thinkingConfig.thinkingBudget;
980
+ const budget = typeof budgetRaw === 'number' && Number.isFinite(budgetRaw) ? budgetRaw : undefined;
981
+ if (thinkingConfig && budget !== undefined && budget > MAX_FLASH_THINKING_BUDGET) {
982
+ thinkingConfig.thinkingBudget = MAX_FLASH_THINKING_BUDGET;
983
+ gc.thinkingConfig = thinkingConfig;
984
+ }
985
+ }
968
986
  if (isThinkingModel && isJsonObject(generationConfig)) {
969
987
  const gc = generationConfig;
970
988
  const thinkingConfig = isJsonObject(gc.thinkingConfig)
@@ -1061,6 +1079,13 @@ function buildGeminiRequestFromChat(chat, metadata) {
1061
1079
  request.metadata[key] = value;
1062
1080
  }
1063
1081
  }
1082
+ if (isAntigravityProvider) {
1083
+ request.metadata = request.metadata ?? {};
1084
+ const existing = request.metadata.antigravitySessionId;
1085
+ if (typeof existing !== 'string' || !existing.trim()) {
1086
+ request.metadata.antigravitySessionId = extractAntigravityGeminiSessionId(request);
1087
+ }
1088
+ }
1064
1089
  // Apply claude-thinking compat at Gemini mapping time to ensure it is always active
1065
1090
  // for Claude models, regardless of compatibilityProfile wiring. Provider层负责进一步的
1066
1091
  // 传输层收紧(如 session_id / generationConfig),这里不做非标裁剪。
@@ -28,6 +28,9 @@ export function runRequestCompatPipeline(profileId, payload, options) {
28
28
  if (!profile) {
29
29
  return { payload };
30
30
  }
31
+ if (!isProtocolCompatible(profile, options?.adapterContext)) {
32
+ return { payload };
33
+ }
31
34
  const stage = pickStageConfig(profile, 'request');
32
35
  if (!stage) {
33
36
  return { payload };
@@ -49,6 +52,9 @@ export function runResponseCompatPipeline(profileId, payload, options) {
49
52
  if (!profile) {
50
53
  return { payload };
51
54
  }
55
+ if (!isProtocolCompatible(profile, options?.adapterContext)) {
56
+ return { payload };
57
+ }
52
58
  const stage = pickStageConfig(profile, 'response');
53
59
  if (!stage) {
54
60
  return { payload };
@@ -95,6 +101,16 @@ function pickStageConfig(profile, stage) {
95
101
  }
96
102
  return null;
97
103
  }
104
+ function isProtocolCompatible(profile, adapterContext) {
105
+ const required = typeof profile.protocol === 'string' ? profile.protocol.trim().toLowerCase() : '';
106
+ if (!required) {
107
+ return true;
108
+ }
109
+ const actual = typeof adapterContext?.providerProtocol === 'string' && adapterContext.providerProtocol.trim()
110
+ ? adapterContext.providerProtocol.trim().toLowerCase()
111
+ : '';
112
+ return Boolean(actual) && actual === required;
113
+ }
98
114
  function applyMapping(root, mapping, state) {
99
115
  switch (mapping.action) {
100
116
  case 'remove':
@@ -221,7 +237,7 @@ function applyMapping(root, mapping, state) {
221
237
  break;
222
238
  case 'anthropic_claude_code_system_prompt':
223
239
  if (state.direction === 'request') {
224
- replaceRoot(root, applyAnthropicClaudeCodeSystemPromptCompat(root, mapping.config));
240
+ replaceRoot(root, applyAnthropicClaudeCodeSystemPromptCompat(root, mapping.config, state.adapterContext));
225
241
  }
226
242
  break;
227
243
  case 'glm_image_content':
@@ -16,10 +16,18 @@ function normalizeProfiles(profiles) {
16
16
  if (!id) {
17
17
  continue;
18
18
  }
19
- map.set(id, {
19
+ const normalized = {
20
20
  ...profile,
21
21
  id
22
- });
22
+ };
23
+ // Treat profile IDs as case-insensitive for lookup purposes.
24
+ // Many configs are hand-authored and may use different casing (e.g. "Chat:Claude-Code"),
25
+ // while built-in profiles are lower-case.
26
+ map.set(id, normalized);
27
+ const lower = id.toLowerCase();
28
+ if (lower !== id) {
29
+ map.set(lower, normalized);
30
+ }
23
31
  }
24
32
  return map;
25
33
  }
@@ -72,5 +80,6 @@ export function getCompatProfile(profileId) {
72
80
  if (!profileMap) {
73
81
  profileMap = buildProfileMap();
74
82
  }
75
- return profileMap.get(profileId.trim()) ?? null;
83
+ const key = profileId.trim();
84
+ return profileMap.get(key) ?? profileMap.get(key.toLowerCase()) ?? null;
76
85
  }
@@ -76,6 +76,7 @@ export declare class HubPipeline {
76
76
  private readonly routerEngine;
77
77
  private config;
78
78
  private unsubscribeProviderErrors?;
79
+ private unsubscribeProviderSuccess?;
79
80
  constructor(config: HubPipelineConfig);
80
81
  updateRuntimeDeps(deps: {
81
82
  healthStore?: HubPipelineConfig['healthStore'] | null;
@@ -2,6 +2,7 @@ import { Readable } from 'node:stream';
2
2
  import { isJsonObject, jsonClone } from '../types/json.js';
3
3
  import { VirtualRouterEngine } from '../../../router/virtual-router/engine.js';
4
4
  import { providerErrorCenter } from '../../../router/virtual-router/error-center.js';
5
+ import { providerSuccessCenter } from '../../../router/virtual-router/success-center.js';
5
6
  import { defaultSseCodecRegistry } from '../../../sse/index.js';
6
7
  import { ResponsesFormatAdapter } from '../format-adapters/responses-format-adapter.js';
7
8
  import { ResponsesSemanticMapper } from '../semantic-mappers/responses-mapper.js';
@@ -111,6 +112,7 @@ export class HubPipeline {
111
112
  routerEngine;
112
113
  config;
113
114
  unsubscribeProviderErrors;
115
+ unsubscribeProviderSuccess;
114
116
  constructor(config) {
115
117
  this.config = config;
116
118
  this.routerEngine = new VirtualRouterEngine({
@@ -133,6 +135,19 @@ export class HubPipeline {
133
135
  catch {
134
136
  this.unsubscribeProviderErrors = undefined;
135
137
  }
138
+ try {
139
+ this.unsubscribeProviderSuccess = providerSuccessCenter.subscribe((event) => {
140
+ try {
141
+ this.routerEngine.handleProviderSuccess(event);
142
+ }
143
+ catch {
144
+ // ignore subscriber errors
145
+ }
146
+ });
147
+ }
148
+ catch {
149
+ this.unsubscribeProviderSuccess = undefined;
150
+ }
136
151
  }
137
152
  updateRuntimeDeps(deps) {
138
153
  if (!deps || typeof deps !== 'object') {
@@ -175,6 +190,15 @@ export class HubPipeline {
175
190
  }
176
191
  this.unsubscribeProviderErrors = undefined;
177
192
  }
193
+ if (this.unsubscribeProviderSuccess) {
194
+ try {
195
+ this.unsubscribeProviderSuccess();
196
+ }
197
+ catch {
198
+ // ignore dispose failures
199
+ }
200
+ this.unsubscribeProviderSuccess = undefined;
201
+ }
178
202
  }
179
203
  async executeRequestStagePipeline(normalized, hooks) {
180
204
  const formatAdapter = hooks.createFormatAdapter();
@@ -1,5 +1,6 @@
1
1
  import { chatEnvelopeToStandardized } from '../../../../standardized-bridge.js';
2
2
  import { validateChatEnvelope } from '../../../../../shared/chat-envelope-validator.js';
3
+ import { buildAnthropicToolAliasMap } from '../../../../../shared/anthropic-message-utils.js';
3
4
  import { applyHubOperationTableInbound } from '../../../../operation-table/operation-table-runner.js';
4
5
  import { recordStage } from '../../../stages/utils.js';
5
6
  import { isJsonObject, jsonClone } from '../../../../types/json.js';
@@ -27,6 +28,25 @@ export async function runReqInboundStage2SemanticMap(options) {
27
28
  if (rawTools.length && toolsNode.clientToolsRaw === undefined) {
28
29
  toolsNode.clientToolsRaw = jsonClone(rawTools);
29
30
  }
31
+ // /v1/messages tool names are case-sensitive to the client (e.g. Bash/Glob/Read).
32
+ // Capture an alias map here (from the *raw* client tools) so later response remap
33
+ // can convert canonical tool calls (e.g. shell_command/glob/read) back to the
34
+ // original client tool names.
35
+ try {
36
+ const protocol = String(options.formatEnvelope?.protocol || '').toLowerCase();
37
+ const endpoint = String(options.adapterContext?.entryEndpoint || '').toLowerCase();
38
+ const shouldCapture = protocol === 'anthropic-messages' || endpoint.includes('/v1/messages');
39
+ const hasAliasMap = isJsonObject(toolsNode.toolNameAliasMap) || isJsonObject(toolsNode.toolAliasMap);
40
+ if (shouldCapture && !hasAliasMap && rawTools.length) {
41
+ const aliasMap = buildAnthropicToolAliasMap(rawTools);
42
+ if (aliasMap) {
43
+ toolsNode.toolNameAliasMap = jsonClone(aliasMap);
44
+ }
45
+ }
46
+ }
47
+ catch {
48
+ // best-effort: never block request handling due to alias map propagation failures
49
+ }
30
50
  if (options.responsesResume && isJsonObject(options.responsesResume)) {
31
51
  if (!semantics.responses || !isJsonObject(semantics.responses)) {
32
52
  semantics.responses = {};
@@ -1,5 +1,7 @@
1
+ import { isJsonObject, jsonClone } from '../../../../types/json.js';
1
2
  import { buildAnthropicResponseFromChat } from '../../../../response/response-runtime.js';
2
3
  import { buildResponsesPayloadFromChat } from '../../../../../responses/responses-openai-bridge.js';
4
+ import { buildAnthropicToolAliasMap } from '../../../../../shared/anthropic-message-utils.js';
3
5
  import { recordStage } from '../../../stages/utils.js';
4
6
  export function runRespOutboundStage1ClientRemap(options) {
5
7
  let clientPayload;
@@ -18,6 +20,16 @@ export function runRespOutboundStage1ClientRemap(options) {
18
20
  ...(toolsRaw ? { toolsRaw } : {})
19
21
  });
20
22
  }
23
+ // Preserve upstream error envelope when present (used by host status mapping + servertool auto flows).
24
+ try {
25
+ const err = options.payload.error;
26
+ if (isJsonObject(err)) {
27
+ clientPayload.error = jsonClone(err);
28
+ }
29
+ }
30
+ catch {
31
+ // best-effort
32
+ }
21
33
  recordStage(options.stageRecorder, 'chat_process.resp.stage9.client_remap', clientPayload);
22
34
  return clientPayload;
23
35
  }
@@ -29,7 +41,20 @@ function resolveAliasMapFromSemantics(semantics) {
29
41
  if (!toolsNode || typeof toolsNode !== 'object' || Array.isArray(toolsNode)) {
30
42
  return undefined;
31
43
  }
32
- const candidate = toolsNode.toolNameAliasMap;
44
+ const toolsRecord = toolsNode;
45
+ // Prefer explicit alias map.
46
+ const candidate = toolsRecord.toolNameAliasMap;
47
+ const fromCandidate = normalizeAliasMap(candidate);
48
+ if (fromCandidate) {
49
+ return fromCandidate;
50
+ }
51
+ // Fallback: derive from captured raw client tools if present.
52
+ // This covers callers that captured `clientToolsRaw` but did not persist `toolNameAliasMap`.
53
+ const rawTools = toolsRecord.clientToolsRaw;
54
+ const derived = buildAnthropicToolAliasMap(rawTools);
55
+ return normalizeAliasMap(derived);
56
+ }
57
+ function normalizeAliasMap(candidate) {
33
58
  if (!candidate || typeof candidate !== 'object' || Array.isArray(candidate)) {
34
59
  return undefined;
35
60
  }