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