@librechat/agents 3.1.87 → 3.1.89

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.
@@ -1910,14 +1910,26 @@ class ToolNode extends run.RunnableCallable {
1910
1910
  })
1911
1911
  .catch(reject);
1912
1912
  });
1913
- const eagerResultsPromise = Promise.all(eagerExecutions.map(({ request, execution }) => this.resolveEagerEventExecution(request, execution))).then((results) => results.flat());
1913
+ const eagerResultsPromise = Promise.all(eagerExecutions.map(async ({ request, execution }) => {
1914
+ const results = await this.resolveEagerEventExecution(request, execution);
1915
+ return {
1916
+ results,
1917
+ completionDispatched: execution.completionDispatched === true &&
1918
+ execution.request.turn === request.turn,
1919
+ toolCallId: request.id,
1920
+ };
1921
+ }));
1914
1922
  const [eagerResults, dispatchedResults] = await Promise.all([
1915
1923
  eagerResultsPromise,
1916
1924
  dispatchPromise,
1917
1925
  ]);
1926
+ const eagerCompletionDispatchedIds = new Set(eagerResults
1927
+ .filter((result) => result.completionDispatched)
1928
+ .map((result) => result.toolCallId));
1929
+ const flattenedEagerResults = eagerResults.flatMap((result) => result.results);
1918
1930
  const results = [
1919
1931
  ...plan.rejectedResults,
1920
- ...eagerResults,
1932
+ ...flattenedEagerResults,
1921
1933
  ...dispatchedResults,
1922
1934
  ];
1923
1935
  this.storeCodeSessionFromResults(results, requestMap);
@@ -2058,7 +2070,9 @@ class ToolNode extends run.RunnableCallable {
2058
2070
  }),
2059
2071
  });
2060
2072
  }
2061
- await this.dispatchStepCompleted(result.toolCallId, toolName, request?.args ?? {}, contentString, config, request?.turn);
2073
+ if (!eagerCompletionDispatchedIds.has(result.toolCallId)) {
2074
+ await this.dispatchStepCompleted(result.toolCallId, toolName, request?.args ?? {}, contentString, config, request?.turn);
2075
+ }
2062
2076
  postToolBatchEntryByCallId.set(result.toolCallId, {
2063
2077
  toolName,
2064
2078
  toolInput: request?.args ?? {},
@@ -2090,8 +2104,7 @@ class ToolNode extends run.RunnableCallable {
2090
2104
  return (this.eventDrivenMode &&
2091
2105
  this.eagerEventToolExecution?.enabled === true &&
2092
2106
  this.hookRegistry == null &&
2093
- this.humanInTheLoop?.enabled !== true &&
2094
- this.toolOutputRegistry == null);
2107
+ this.humanInTheLoop?.enabled !== true);
2095
2108
  }
2096
2109
  takeMatchingEagerEventExecution(request) {
2097
2110
  if (!this.canConsumeEagerEventExecution()) {
@@ -2102,9 +2115,13 @@ class ToolNode extends run.RunnableCallable {
2102
2115
  return undefined;
2103
2116
  }
2104
2117
  this.eagerEventToolExecutions?.delete(request.id);
2118
+ // Only tool identity + canonical args define side-effect identity here.
2119
+ // `request.turn` is final-planning metadata; if it drifts between the
2120
+ // streamed eager reservation and model-end materialization, consume the
2121
+ // same-name/same-args eager result and let the final request drive refs,
2122
+ // completion metadata, and PostToolBatch state.
2105
2123
  if (execution.toolName !== request.name ||
2106
- !eagerEventExecution.recordArgsEqual(execution.args, request.args) ||
2107
- execution.request.turn !== request.turn) {
2124
+ !eagerEventExecution.recordArgsEqual(execution.args, request.args)) {
2108
2125
  return {
2109
2126
  toolCallId: request.id,
2110
2127
  toolName: request.name,