@google/gemini-cli-a2a-server 0.54.0-preview.0 → 0.54.0

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.
@@ -220939,8 +220939,8 @@ var GIT_COMMIT_INFO, CLI_VERSION;
220939
220939
  var init_git_commit = __esm({
220940
220940
  "packages/core/dist/src/generated/git-commit.js"() {
220941
220941
  "use strict";
220942
- GIT_COMMIT_INFO = "d29268d36";
220943
- CLI_VERSION = "0.54.0-preview.0";
220942
+ GIT_COMMIT_INFO = "a81db2768";
220943
+ CLI_VERSION = "0.54.0";
220944
220944
  }
220945
220945
  });
220946
220946
 
@@ -291864,6 +291864,15 @@ var init_agentChatHistory = __esm({
291864
291864
  clear() {
291865
291865
  this.history = [];
291866
291866
  }
291867
+ /**
291868
+ * Rolls back the history to a specified length.
291869
+ * Useful when a stream fails and we need to remove the un-responded turn(s).
291870
+ */
291871
+ rollback(length) {
291872
+ if (length >= 0 && length <= this.history.length) {
291873
+ this.history = this.history.slice(0, length);
291874
+ }
291875
+ }
291867
291876
  get() {
291868
291877
  return this.history;
291869
291878
  }
@@ -292673,7 +292682,8 @@ var init_uiTelemetry = __esm({
292673
292682
  api: {
292674
292683
  totalRequests: 0,
292675
292684
  totalErrors: 0,
292676
- totalLatencyMs: 0
292685
+ totalLatencyMs: 0,
292686
+ errorsByType: {}
292677
292687
  },
292678
292688
  tokens: {
292679
292689
  input: 0,
@@ -292728,6 +292738,19 @@ var init_uiTelemetry = __esm({
292728
292738
  lastPromptTokenCount: this.#lastPromptTokenCount
292729
292739
  });
292730
292740
  }
292741
+ recordSemanticValidationError(model, errorType) {
292742
+ const modelMetrics = this.getOrCreateModelMetrics(model);
292743
+ modelMetrics.api.totalErrors++;
292744
+ if (!modelMetrics.api.errorsByType) {
292745
+ modelMetrics.api.errorsByType = {};
292746
+ }
292747
+ const type2 = errorType || "INVALID_STREAM";
292748
+ modelMetrics.api.errorsByType[type2] = (modelMetrics.api.errorsByType[type2] || 0) + 1;
292749
+ this.emit("update", {
292750
+ metrics: this.#metrics,
292751
+ lastPromptTokenCount: this.#lastPromptTokenCount
292752
+ });
292753
+ }
292731
292754
  getMetrics() {
292732
292755
  return this.#metrics;
292733
292756
  }
@@ -292849,6 +292872,11 @@ var init_uiTelemetry = __esm({
292849
292872
  modelMetrics.api.totalRequests++;
292850
292873
  modelMetrics.api.totalErrors++;
292851
292874
  modelMetrics.api.totalLatencyMs += event.duration_ms;
292875
+ if (!modelMetrics.api.errorsByType) {
292876
+ modelMetrics.api.errorsByType = {};
292877
+ }
292878
+ const errorType = event.error_type || "UNKNOWN";
292879
+ modelMetrics.api.errorsByType[errorType] = (modelMetrics.api.errorsByType[errorType] || 0) + 1;
292852
292880
  if (event.role) {
292853
292881
  if (!modelMetrics.roles[event.role]) {
292854
292882
  modelMetrics.roles[event.role] = createInitialRoleMetrics();
@@ -336558,7 +336586,7 @@ function getVersion() {
336558
336586
  }
336559
336587
  versionPromise = (async () => {
336560
336588
  const pkgJson = await getPackageJson(__dirname4);
336561
- return "0.54.0-preview.0";
336589
+ return "0.54.0";
336562
336590
  })();
336563
336591
  return versionPromise;
336564
336592
  }
@@ -337292,6 +337320,11 @@ function classifyGoogleError(error2) {
337292
337320
  if (errorInfo.reason === "INSUFFICIENT_G1_CREDITS_BALANCE") {
337293
337321
  return new TerminalQuotaError(googleApiError.message, googleApiError, delaySeconds, errorInfo.reason);
337294
337322
  }
337323
+ if (errorInfo.reason === "MODEL_CAPACITY_EXHAUSTED" || errorInfo.reason === "MODEL_CAPACITY_EXCEEDED") {
337324
+ if (delaySeconds === void 0) {
337325
+ return new TerminalQuotaError(googleApiError.message, googleApiError, delaySeconds, errorInfo.reason);
337326
+ }
337327
+ }
337295
337328
  if (errorInfo.domain) {
337296
337329
  if (isCloudCodeDomain(errorInfo.domain)) {
337297
337330
  if (errorInfo.reason === "RATE_LIMIT_EXCEEDED") {
@@ -365664,10 +365697,10 @@ var init_agent_loop_context = __esm({
365664
365697
 
365665
365698
  // packages/core/dist/src/utils/messageInspectors.js
365666
365699
  function isFunctionResponse(content) {
365667
- return content.role === "user" && !!content.parts && content.parts.every((part) => !!part.functionResponse);
365700
+ return content.role === "user" && !!content.parts && content.parts.some((part) => !!part.functionResponse);
365668
365701
  }
365669
365702
  function isFunctionCall(content) {
365670
- return content.role === "model" && !!content.parts && content.parts.every((part) => !!part.functionCall);
365703
+ return content.role === "model" && !!content.parts && content.parts.length > 0 && content.parts.every((part) => !!part.functionCall);
365671
365704
  }
365672
365705
  var init_messageInspectors = __esm({
365673
365706
  "packages/core/dist/src/utils/messageInspectors.js"() {
@@ -366301,15 +366334,18 @@ var init_geminiChat = __esm({
366301
366334
  */
366302
366335
  async sendMessageStream(modelConfigKey, message, prompt_id, signal, role, displayContent, apiHistoryOverride) {
366303
366336
  await this.sendPromise;
366337
+ const historyLengthBefore = this.agentHistory.length;
366338
+ const baselinePromptTokenCount = this.lastPromptTokenCount;
366304
366339
  let streamDoneResolver;
366305
366340
  const streamDonePromise = new Promise((resolve23) => {
366306
366341
  streamDoneResolver = resolve23;
366307
366342
  });
366308
366343
  this.sendPromise = streamDonePromise;
366309
366344
  let userContent = createUserContent(message);
366345
+ const isOriginalFunctionResponse = isFunctionResponse(userContent);
366310
366346
  const { model } = this.context.config.modelConfigService.getResolvedConfig(modelConfigKey);
366311
366347
  const isContextManagementEnabled = this.context.config.isContextManagementEnabled();
366312
- if (!isFunctionResponse(userContent)) {
366348
+ if (!isOriginalFunctionResponse) {
366313
366349
  const userMessageParts = userContent.parts || [];
366314
366350
  const userMessageContent = partListUnionToString(userMessageParts);
366315
366351
  let finalDisplayContent = void 0;
@@ -366388,13 +366424,14 @@ var init_geminiChat = __esm({
366388
366424
  const streamWithRetries = async function* () {
366389
366425
  try {
366390
366426
  const maxAttempts = this.context.config.getMaxAttempts();
366427
+ let lastStreamError = void 0;
366391
366428
  for (let attempt = 0; attempt < maxAttempts; attempt++) {
366392
366429
  let isConnectionPhase = true;
366393
366430
  try {
366394
366431
  if (attempt > 0) {
366395
366432
  yield { type: StreamEventType.RETRY };
366396
366433
  }
366397
- const currentConfigKey = attempt > 0 ? { ...modelConfigKey, isRetry: true } : modelConfigKey;
366434
+ const currentConfigKey = attempt > 0 ? { ...modelConfigKey, isRetry: true, lastStreamError } : modelConfigKey;
366398
366435
  isConnectionPhase = true;
366399
366436
  const stream2 = await this.makeApiCallAndProcessStream(currentConfigKey, requestHistory, prompt_id, signal, role, apiHistoryOverride);
366400
366437
  isConnectionPhase = false;
@@ -366403,6 +366440,9 @@ var init_geminiChat = __esm({
366403
366440
  }
366404
366441
  return;
366405
366442
  } catch (error2) {
366443
+ if (error2 instanceof InvalidStreamError) {
366444
+ lastStreamError = error2;
366445
+ }
366406
366446
  if (error2 instanceof AgentExecutionStoppedError) {
366407
366447
  yield {
366408
366448
  type: StreamEventType.AGENT_EXECUTION_STOPPED,
@@ -366428,7 +366468,7 @@ var init_geminiChat = __esm({
366428
366468
  }
366429
366469
  const isRetryable = isRetryableError(error2, this.context.config.getRetryFetchErrors());
366430
366470
  const isContentError = error2 instanceof InvalidStreamError;
366431
- const isRetryableContentError = isContentError && error2.type !== "NO_RESPONSE_TEXT";
366471
+ const isRetryableContentError = isContentError;
366432
366472
  const errorType = isContentError ? error2.type : getRetryErrorType(error2);
366433
366473
  if (isRetryableContentError || isRetryable && !signal.aborted) {
366434
366474
  const maxMidStreamAttempts = MID_STREAM_RETRY_OPTIONS.maxAttempts;
@@ -366457,6 +366497,13 @@ var init_geminiChat = __esm({
366457
366497
  throw error2;
366458
366498
  }
366459
366499
  }
366500
+ } catch (error2) {
366501
+ if (!isOriginalFunctionResponse) {
366502
+ this.agentHistory.rollback(historyLengthBefore);
366503
+ this.chatRecordingService.updateMessagesFromHistory(this.agentHistory.get());
366504
+ this.lastPromptTokenCount = baselinePromptTokenCount;
366505
+ }
366506
+ throw error2;
366460
366507
  } finally {
366461
366508
  streamDoneResolver();
366462
366509
  }
@@ -366515,6 +366562,22 @@ var init_geminiChat = __esm({
366515
366562
  tools: this.tools,
366516
366563
  abortSignal
366517
366564
  };
366565
+ if (modelConfigKey.isRetry && modelConfigKey.lastStreamError instanceof InvalidStreamError) {
366566
+ const lastError = modelConfigKey.lastStreamError;
366567
+ let nudgeMessage = "";
366568
+ if (lastError.type === "THINKING_ONLY_RESPONSE") {
366569
+ nudgeMessage = "\n[System: You previously generated thoughts but failed to provide a final user-facing response. Please ensure you provide your final answer or call a tool now.]";
366570
+ } else if (lastError.type === "NO_RESPONSE_TEXT") {
366571
+ nudgeMessage = "\n[System: You previously returned an empty response with no text or thoughts. Please ensure you provide your final answer or call a tool now.]";
366572
+ }
366573
+ if (nudgeMessage) {
366574
+ if (typeof config2.systemInstruction === "string") {
366575
+ config2.systemInstruction += nudgeMessage;
366576
+ } else if (config2.systemInstruction === void 0) {
366577
+ config2.systemInstruction = nudgeMessage;
366578
+ }
366579
+ }
366580
+ }
366518
366581
  let contentsToUse = supportsModernFeatures(modelToUse) || isGemini2Model(modelToUse) ? [...contentsForPreviewModel] : [...requestContents];
366519
366582
  const hookSystem = this.context.config.getHookSystem();
366520
366583
  if (hookSystem) {
@@ -366764,6 +366827,8 @@ This error was probably caused by cyclic schema references in one of the followi
366764
366827
  let hasToolCall = false;
366765
366828
  let hasThoughts = false;
366766
366829
  let finishReason;
366830
+ const bufferedThoughts = [];
366831
+ let bufferedUsageMetadata = void 0;
366767
366832
  const finalFunctionCallsMap = /* @__PURE__ */ new Map();
366768
366833
  const legacyFunctionCalls = [];
366769
366834
  const callIndexToId = /* @__PURE__ */ new Map();
@@ -366809,7 +366874,10 @@ This error was probably caused by cyclic schema references in one of the followi
366809
366874
  if (content?.parts) {
366810
366875
  if (content.parts.some((part) => part.thought)) {
366811
366876
  hasThoughts = true;
366812
- this.recordThoughtFromContent(content);
366877
+ const thought = this.extractThoughtFromContent(content);
366878
+ if (thought) {
366879
+ bufferedThoughts.push(thought);
366880
+ }
366813
366881
  }
366814
366882
  if (content.parts.some((part) => part.functionCall)) {
366815
366883
  hasToolCall = true;
@@ -366831,10 +366899,7 @@ This error was probably caused by cyclic schema references in one of the followi
366831
366899
  }
366832
366900
  }
366833
366901
  if (chunk.usageMetadata) {
366834
- this.chatRecordingService.recordMessageTokens(chunk.usageMetadata);
366835
- if (chunk.usageMetadata.promptTokenCount !== void 0) {
366836
- this.lastPromptTokenCount = chunk.usageMetadata.promptTokenCount;
366837
- }
366902
+ bufferedUsageMetadata = chunk.usageMetadata;
366838
366903
  }
366839
366904
  const hookSystem = this.context.config.getHookSystem();
366840
366905
  if (originalRequest && chunk && hookSystem) {
@@ -366890,17 +366955,14 @@ This error was probably caused by cyclic schema references in one of the followi
366890
366955
  }
366891
366956
  }
366892
366957
  }
366893
- const responseText = consolidatedParts.filter((part) => part.text).map((part) => part.text).join("").trim();
366894
- let id;
366895
- if (responseText || hasThoughts || hasToolCall) {
366896
- id = this.chatRecordingService.recordMessage({
366897
- model,
366898
- type: "gemini",
366899
- content: responseText
366900
- });
366901
- } else {
366902
- id = this.chatRecordingService.recordSyntheticMessage("gemini", consolidatedParts);
366903
- }
366958
+ const rawResponseText = consolidatedParts.filter((part) => part.text).map((part) => part.text).join("");
366959
+ let responseText = rawResponseText.replace(/[\u200B-\u200D\uFEFF\u200E\u200F]/g, "");
366960
+ let previous;
366961
+ do {
366962
+ previous = responseText;
366963
+ responseText = responseText.replace(/<!--[\s\S]*?-->/g, "");
366964
+ } while (responseText !== previous);
366965
+ responseText = responseText.trim();
366904
366966
  if (!hasToolCall) {
366905
366967
  if (!finishReason) {
366906
366968
  throw new InvalidStreamError("Model stream ended without a finish reason.", "NO_FINISH_REASON");
@@ -366912,9 +366974,43 @@ This error was probably caused by cyclic schema references in one of the followi
366912
366974
  throw new InvalidStreamError("Model stream ended with unexpected tool call.", "UNEXPECTED_TOOL_CALL");
366913
366975
  }
366914
366976
  if (!responseText) {
366977
+ if (finishReason === FinishReason.MAX_TOKENS) {
366978
+ throw new InvalidStreamError("Model stream ended due to token limit exhaustion (MAX_TOKENS) with empty response text.", "MAX_TOKENS_EXCEEDED");
366979
+ }
366980
+ if (finishReason === FinishReason.SAFETY) {
366981
+ throw new InvalidStreamError("Model stream ended due to safety settings (SAFETY) with empty response text.", "SAFETY_BLOCKED");
366982
+ }
366983
+ if (finishReason === FinishReason.RECITATION) {
366984
+ throw new InvalidStreamError("Model stream ended due to recitation settings (RECITATION) with empty response text.", "RECITATION_BLOCKED");
366985
+ }
366986
+ if (finishReason === FinishReason.OTHER) {
366987
+ throw new InvalidStreamError("Model stream ended due to other settings (OTHER) with empty response text.", "OTHER_BLOCKED");
366988
+ }
366989
+ if (hasThoughts) {
366990
+ throw new InvalidStreamError("Model stream ended with empty response text but contained reasoning thoughts.", "THINKING_ONLY_RESPONSE");
366991
+ }
366915
366992
  throw new InvalidStreamError("Model stream ended with empty response text.", "NO_RESPONSE_TEXT");
366916
366993
  }
366917
366994
  }
366995
+ for (const thought of bufferedThoughts) {
366996
+ this.chatRecordingService.recordThought(thought);
366997
+ }
366998
+ if (bufferedUsageMetadata) {
366999
+ this.chatRecordingService.recordMessageTokens(bufferedUsageMetadata);
367000
+ if (bufferedUsageMetadata.promptTokenCount !== void 0) {
367001
+ this.lastPromptTokenCount = bufferedUsageMetadata.promptTokenCount;
367002
+ }
367003
+ }
367004
+ let id;
367005
+ if (responseText || hasThoughts || hasToolCall) {
367006
+ id = this.chatRecordingService.recordMessage({
367007
+ model,
367008
+ type: "gemini",
367009
+ content: responseText
367010
+ });
367011
+ } else {
367012
+ id = this.chatRecordingService.recordSyntheticMessage("gemini", consolidatedParts);
367013
+ }
366918
367014
  this.agentHistory.push({
366919
367015
  id,
366920
367016
  content: { role: "model", parts: consolidatedParts }
@@ -366952,11 +367048,11 @@ This error was probably caused by cyclic schema references in one of the followi
366952
367048
  this.chatRecordingService.recordToolCalls(model, toolCallRecords);
366953
367049
  }
366954
367050
  /**
366955
- * Extracts and records thought from thought content.
367051
+ * Extracts thought from thought content.
366956
367052
  */
366957
- recordThoughtFromContent(content) {
367053
+ extractThoughtFromContent(content) {
366958
367054
  if (!content.parts || content.parts.length === 0) {
366959
- return;
367055
+ return void 0;
366960
367056
  }
366961
367057
  const thoughtPart = content.parts[0];
366962
367058
  if (thoughtPart.text) {
@@ -366964,11 +367060,12 @@ This error was probably caused by cyclic schema references in one of the followi
366964
367060
  const subjectStringMatches = rawText.match(/\*\*(.*?)\*\*/s);
366965
367061
  const subject = subjectStringMatches ? subjectStringMatches[1].trim() : "";
366966
367062
  const description = rawText.replace(/\*\*(.*?)\*\*/s, "").trim();
366967
- this.chatRecordingService.recordThought({
367063
+ return {
366968
367064
  subject,
366969
367065
  description
366970
- });
367066
+ };
366971
367067
  }
367068
+ return void 0;
366972
367069
  }
366973
367070
  };
366974
367071
  }
@@ -367221,7 +367318,13 @@ ${[...this.pendingCitations].sort().join("\n")}`
367221
367318
  return;
367222
367319
  }
367223
367320
  if (e3 instanceof InvalidStreamError) {
367224
- yield { type: GeminiEventType.InvalidStream };
367321
+ yield {
367322
+ type: GeminiEventType.InvalidStream,
367323
+ value: {
367324
+ type: e3.type,
367325
+ message: e3.message
367326
+ }
367327
+ };
367225
367328
  return;
367226
367329
  }
367227
367330
  const error2 = toFriendlyError(e3);
@@ -367571,6 +367674,16 @@ function renderOperationalGuidelines(options) {
367571
367674
  - **Security First:** Always apply security best practices. Never introduce code that exposes, logs, or commits secrets, API keys, or other sensitive information.
367572
367675
 
367573
367676
  ## Tool Usage
367677
+ - **Tool Execution Response Rules:**
367678
+ 1. After receiving a \`functionResponse\`, you MUST ALWAYS execute one of the following two actions:
367679
+ a) Call another tool to proceed with the task.
367680
+ b) Provide a user-facing text response explaining the tool output, your analysis, and next steps.
367681
+ 2. You MUST NEVER return an empty response with no text and no tool calls.
367682
+ - **Post-Edit Response Rules:**
367683
+ 1. After an edit tool execution (e.g. ${formatToolName(EDIT_TOOL_NAME)}, ${formatToolName(WRITE_FILE_TOOL_NAME)}), you MUST ALWAYS generate a user-facing text response summarizing:
367684
+ - What changes were made to the file.
367685
+ - Your verification plan or next steps (e.g. running tests).
367686
+ 2. You MUST NEVER return an empty response with 0 text tokens after completing an edit.
367574
367687
  - **Parallelism & Sequencing:** Tools execute in parallel by default. Execute multiple independent tool calls in parallel when feasible (e.g., searching, reading files, independent shell commands, or editing *different* files). If a tool depends on the output or side-effects of a previous tool in the same turn (e.g., running a shell command that depends on the success of a previous command), you MUST set the \`wait_for_previous\` parameter to \`true\` on the dependent tool to ensure sequential execution.
367575
367688
  - **File Editing Collisions:** Do NOT make multiple calls to the ${formatToolName(EDIT_TOOL_NAME)} tool for the SAME file in a single turn. To make multiple edits to the same file, you MUST perform them sequentially across multiple conversational turns to prevent race conditions and ensure the file state is accurate before each edit.
367576
367689
  - **Command Execution:** Use the ${formatToolName(SHELL_TOOL_NAME)} tool for running shell commands, remembering the safety rule to explain modifying commands first.${toolUsageInteractive(options.interactive, options.interactiveShellEnabled)}${toolUsageRememberingFacts(options)}
@@ -386134,6 +386247,7 @@ var init_scheduler = __esm({
386134
386247
  for (const activeCall of activeCalls) {
386135
386248
  if (!this.isTerminal(activeCall.status)) {
386136
386249
  this.state.updateStatus(activeCall.request.callId, CoreToolCallStatus.Cancelled, "Operation cancelled by user");
386250
+ this.state.finalizeCall(activeCall.request.callId);
386137
386251
  }
386138
386252
  }
386139
386253
  this.state.cancelAllQueued("Operation cancelled by user");
@@ -386248,6 +386362,12 @@ var init_scheduler = __esm({
386248
386362
  */
386249
386363
  async _processNextItem(signal) {
386250
386364
  if (signal.aborted || this.isCancelling) {
386365
+ const activeCalls2 = this.state.allActiveCalls;
386366
+ for (const call of activeCalls2) {
386367
+ if (this.isTerminal(call.status)) {
386368
+ this.state.finalizeCall(call.request.callId);
386369
+ }
386370
+ }
386251
386371
  this.state.cancelAllQueued("Operation cancelled");
386252
386372
  return false;
386253
386373
  }