@librechat/agents 3.2.61 → 3.2.62
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.
- package/dist/cjs/common/enum.cjs +2 -0
- package/dist/cjs/common/enum.cjs.map +1 -1
- package/dist/cjs/hooks/HookRegistry.cjs +32 -0
- package/dist/cjs/hooks/HookRegistry.cjs.map +1 -1
- package/dist/cjs/hooks/executeHooks.cjs +6 -0
- package/dist/cjs/hooks/executeHooks.cjs.map +1 -1
- package/dist/cjs/hooks/index.cjs +12 -0
- package/dist/cjs/hooks/index.cjs.map +1 -0
- package/dist/cjs/hooks/types.cjs.map +1 -1
- package/dist/cjs/langfuse.cjs +62 -1
- package/dist/cjs/langfuse.cjs.map +1 -1
- package/dist/cjs/llm/bedrock/index.cjs +266 -43
- package/dist/cjs/llm/bedrock/index.cjs.map +1 -1
- package/dist/cjs/llm/openai/index.cjs +274 -7
- package/dist/cjs/llm/openai/index.cjs.map +1 -1
- package/dist/cjs/llm/openai/streamMetadata.cjs +69 -0
- package/dist/cjs/llm/openai/streamMetadata.cjs.map +1 -0
- package/dist/cjs/llm/openrouter/index.cjs +5 -6
- package/dist/cjs/llm/openrouter/index.cjs.map +1 -1
- package/dist/cjs/main.cjs +6 -1
- package/dist/cjs/messages/format.cjs +61 -0
- package/dist/cjs/messages/format.cjs.map +1 -1
- package/dist/cjs/messages/prune.cjs +31 -19
- package/dist/cjs/messages/prune.cjs.map +1 -1
- package/dist/cjs/stream.cjs +13 -3
- package/dist/cjs/stream.cjs.map +1 -1
- package/dist/cjs/tools/ToolNode.cjs +75 -10
- package/dist/cjs/tools/ToolNode.cjs.map +1 -1
- package/dist/cjs/tools/subagent/SubagentExecutor.cjs +1 -0
- package/dist/cjs/tools/subagent/SubagentExecutor.cjs.map +1 -1
- package/dist/cjs/utils/tokens.cjs +111 -0
- package/dist/cjs/utils/tokens.cjs.map +1 -1
- package/dist/esm/common/enum.mjs +2 -0
- package/dist/esm/common/enum.mjs.map +1 -1
- package/dist/esm/hooks/HookRegistry.mjs +32 -0
- package/dist/esm/hooks/HookRegistry.mjs.map +1 -1
- package/dist/esm/hooks/executeHooks.mjs +6 -0
- package/dist/esm/hooks/executeHooks.mjs.map +1 -1
- package/dist/esm/hooks/index.mjs +12 -1
- package/dist/esm/hooks/index.mjs.map +1 -0
- package/dist/esm/hooks/types.mjs.map +1 -1
- package/dist/esm/langfuse.mjs +62 -1
- package/dist/esm/langfuse.mjs.map +1 -1
- package/dist/esm/llm/bedrock/index.mjs +266 -43
- package/dist/esm/llm/bedrock/index.mjs.map +1 -1
- package/dist/esm/llm/openai/index.mjs +274 -7
- package/dist/esm/llm/openai/index.mjs.map +1 -1
- package/dist/esm/llm/openai/streamMetadata.mjs +69 -0
- package/dist/esm/llm/openai/streamMetadata.mjs.map +1 -0
- package/dist/esm/llm/openrouter/index.mjs +5 -6
- package/dist/esm/llm/openrouter/index.mjs.map +1 -1
- package/dist/esm/main.mjs +3 -3
- package/dist/esm/messages/format.mjs +61 -0
- package/dist/esm/messages/format.mjs.map +1 -1
- package/dist/esm/messages/prune.mjs +31 -19
- package/dist/esm/messages/prune.mjs.map +1 -1
- package/dist/esm/stream.mjs +13 -3
- package/dist/esm/stream.mjs.map +1 -1
- package/dist/esm/tools/ToolNode.mjs +75 -10
- package/dist/esm/tools/ToolNode.mjs.map +1 -1
- package/dist/esm/tools/subagent/SubagentExecutor.mjs +1 -0
- package/dist/esm/tools/subagent/SubagentExecutor.mjs.map +1 -1
- package/dist/esm/utils/tokens.mjs +108 -1
- package/dist/esm/utils/tokens.mjs.map +1 -1
- package/dist/types/common/enum.d.ts +3 -1
- package/dist/types/hooks/HookRegistry.d.ts +10 -0
- package/dist/types/hooks/index.d.ts +7 -0
- package/dist/types/hooks/types.d.ts +18 -1
- package/dist/types/llm/bedrock/index.d.ts +5 -0
- package/dist/types/llm/openai/index.d.ts +19 -0
- package/dist/types/llm/openai/streamMetadata.d.ts +16 -0
- package/dist/types/messages/prune.d.ts +4 -3
- package/dist/types/tools/ToolNode.d.ts +1 -0
- package/dist/types/types/llm.d.ts +12 -2
- package/dist/types/types/tools.d.ts +1 -1
- package/dist/types/utils/tokens.d.ts +30 -0
- package/package.json +6 -6
- package/src/__tests__/stream.eagerEventExecution.test.ts +100 -2
- package/src/common/enum.ts +2 -0
- package/src/hooks/HookRegistry.ts +45 -0
- package/src/hooks/__tests__/HookRegistry.test.ts +48 -0
- package/src/hooks/__tests__/executeHooks.test.ts +85 -2
- package/src/hooks/executeHooks.ts +15 -0
- package/src/hooks/index.ts +7 -0
- package/src/hooks/types.ts +18 -1
- package/src/langfuse.ts +134 -1
- package/src/llm/bedrock/index.ts +434 -83
- package/src/llm/bedrock/streamSealDispatch.test.ts +97 -0
- package/src/llm/custom-chat-models.smoke.test.ts +7 -0
- package/src/llm/openai/index.ts +604 -6
- package/src/llm/openai/managedRequests.test.ts +182 -0
- package/src/llm/openai/streamMetadata.spec.ts +86 -0
- package/src/llm/openai/streamMetadata.ts +95 -0
- package/src/llm/openai/streamMetadataDedup.spec.ts +166 -0
- package/src/llm/openrouter/index.ts +9 -5
- package/src/messages/format.ts +96 -3
- package/src/messages/formatAgentMessages.steer.test.ts +326 -0
- package/src/messages/labelContentByAgent.test.ts +75 -0
- package/src/messages/prune.ts +56 -30
- package/src/specs/anthropic.simple.test.ts +4 -2
- package/src/specs/cache.simple.test.ts +17 -4
- package/src/specs/langfuse-callbacks.test.ts +61 -0
- package/src/specs/openai.simple.test.ts +4 -2
- package/src/specs/spec.utils.ts +12 -0
- package/src/specs/summarization.test.ts +9 -13
- package/src/specs/token-accounting-pipeline.test.ts +130 -4
- package/src/specs/tokens.test.ts +214 -0
- package/src/stream.ts +22 -3
- package/src/tools/ToolNode.ts +107 -12
- package/src/tools/__tests__/ToolNode.eagerEventExecution.test.ts +554 -0
- package/src/tools/__tests__/ToolNode.onResultCompletion.test.ts +49 -2
- package/src/tools/__tests__/hitl.test.ts +112 -0
- package/src/tools/subagent/SubagentExecutor.ts +2 -0
- package/src/types/llm.ts +12 -2
- package/src/types/tools.ts +1 -1
- package/src/utils/tokens.ts +181 -3
package/src/tools/ToolNode.ts
CHANGED
|
@@ -822,10 +822,11 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
822
822
|
private recordEventToolPlanningTurn(
|
|
823
823
|
toolName: string,
|
|
824
824
|
turn: number,
|
|
825
|
-
callId?: string
|
|
825
|
+
callId?: string,
|
|
826
|
+
runId?: string
|
|
826
827
|
): void {
|
|
827
828
|
this.recordToolUsageTurn(toolName, turn, callId);
|
|
828
|
-
if (this.canConsumeEagerEventExecution()) {
|
|
829
|
+
if (this.canConsumeEagerEventExecution(runId)) {
|
|
829
830
|
this.eagerEventToolUsageCount?.set(
|
|
830
831
|
toolName,
|
|
831
832
|
Math.max(this.eagerEventToolUsageCount.get(toolName) ?? 0, turn + 1)
|
|
@@ -2091,6 +2092,7 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
2091
2092
|
const postToolBatchEntryByCallId = new Map<string, PostToolBatchEntry>();
|
|
2092
2093
|
const HOOK_FALLBACK: AggregatedHookResult = Object.freeze({
|
|
2093
2094
|
additionalContexts: [] as string[],
|
|
2095
|
+
injectedMessages: [] as t.InjectedMessage[],
|
|
2094
2096
|
errors: [] as string[],
|
|
2095
2097
|
});
|
|
2096
2098
|
|
|
@@ -2101,6 +2103,32 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
2101
2103
|
* to defensively `?.` it across every reference inside.
|
|
2102
2104
|
*/
|
|
2103
2105
|
const hookRegistry = this.hookRegistry;
|
|
2106
|
+
/**
|
|
2107
|
+
* Pull each call's prestarted eager record BEFORE awaiting the hooks:
|
|
2108
|
+
* an async deny must never race the eager host promise into emitting a
|
|
2109
|
+
* successful completion (`dispatchEagerToolCompletions`' map-identity
|
|
2110
|
+
* check skips deleted records). Allowed entries get their record
|
|
2111
|
+
* restored in the decision loop below so consumption still works;
|
|
2112
|
+
* denied entries stay deleted. Ask/interrupt configs never have eager
|
|
2113
|
+
* records (HITL disables the reservation gate).
|
|
2114
|
+
*/
|
|
2115
|
+
const preemptedEagerRecords = new Map<
|
|
2116
|
+
string,
|
|
2117
|
+
t.EagerEventToolExecution
|
|
2118
|
+
>();
|
|
2119
|
+
if (this.eagerEventToolExecutions != null) {
|
|
2120
|
+
for (const entry of preToolCalls) {
|
|
2121
|
+
const callId = entry.call.id;
|
|
2122
|
+
if (callId == null) {
|
|
2123
|
+
continue;
|
|
2124
|
+
}
|
|
2125
|
+
const record = this.eagerEventToolExecutions.get(callId);
|
|
2126
|
+
if (record != null) {
|
|
2127
|
+
preemptedEagerRecords.set(callId, record);
|
|
2128
|
+
this.eagerEventToolExecutions.delete(callId);
|
|
2129
|
+
}
|
|
2130
|
+
}
|
|
2131
|
+
}
|
|
2104
2132
|
const preResults = await Promise.all(
|
|
2105
2133
|
preToolCalls.map((entry) =>
|
|
2106
2134
|
executeHooks({
|
|
@@ -2183,6 +2211,14 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
2183
2211
|
contentString,
|
|
2184
2212
|
reason,
|
|
2185
2213
|
});
|
|
2214
|
+
/**
|
|
2215
|
+
* A prestarted eager execution for a now-blocked call must neither be
|
|
2216
|
+
* consumed nor emit its completion — the run reports this call as
|
|
2217
|
+
* blocked. Deleting the record makes `dispatchEagerToolCompletions`'
|
|
2218
|
+
* map-identity check skip the pending emission (same pattern as the
|
|
2219
|
+
* rejected-results cleanup below).
|
|
2220
|
+
*/
|
|
2221
|
+
this.eagerEventToolExecutions?.delete(entry.call.id!);
|
|
2186
2222
|
};
|
|
2187
2223
|
|
|
2188
2224
|
const flushDeferredBlockedSideEffects = async (): Promise<void> => {
|
|
@@ -2312,6 +2348,12 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
2312
2348
|
if (hookResult.updatedInput != null) {
|
|
2313
2349
|
applyInputOverride(entry, hookResult.updatedInput);
|
|
2314
2350
|
}
|
|
2351
|
+
if (entry.call.id != null) {
|
|
2352
|
+
const preempted = preemptedEagerRecords.get(entry.call.id);
|
|
2353
|
+
if (preempted != null) {
|
|
2354
|
+
this.eagerEventToolExecutions?.set(entry.call.id, preempted);
|
|
2355
|
+
}
|
|
2356
|
+
}
|
|
2315
2357
|
approvedEntries.push(entry);
|
|
2316
2358
|
}
|
|
2317
2359
|
|
|
@@ -2592,7 +2634,12 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
2592
2634
|
usageCount: this.toolUsageCount,
|
|
2593
2635
|
invalidArgsBehavior: 'error-result',
|
|
2594
2636
|
recordTurn: (toolName, reservedTurn, callId) => {
|
|
2595
|
-
this.recordEventToolPlanningTurn(
|
|
2637
|
+
this.recordEventToolPlanningTurn(
|
|
2638
|
+
toolName,
|
|
2639
|
+
reservedTurn,
|
|
2640
|
+
callId,
|
|
2641
|
+
runId
|
|
2642
|
+
);
|
|
2596
2643
|
},
|
|
2597
2644
|
});
|
|
2598
2645
|
if (plan == null) {
|
|
@@ -2637,7 +2684,20 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
2637
2684
|
* released if the dispatch fails, letting the batch path re-emit.
|
|
2638
2685
|
*/
|
|
2639
2686
|
const canEmitEarlyCompletions =
|
|
2640
|
-
this.hookRegistry
|
|
2687
|
+
this.hookRegistry?.hasResultAlteringHooks(runId) !== true &&
|
|
2688
|
+
this.humanInTheLoop?.enabled !== true;
|
|
2689
|
+
/**
|
|
2690
|
+
* Snapshot the post-hook gates at the same instant as the early-emission
|
|
2691
|
+
* gate: a result-altering hook registered while this batch is in flight
|
|
2692
|
+
* applies from the NEXT batch. Evaluating these after results settle
|
|
2693
|
+
* would let a late hook rewrite the ToolMessage AFTER an early
|
|
2694
|
+
* completion already emitted the pre-hook output — two views of the
|
|
2695
|
+
* same call.
|
|
2696
|
+
*/
|
|
2697
|
+
const hasPostHook =
|
|
2698
|
+
this.hookRegistry?.hasHookFor('PostToolUse', runId) === true;
|
|
2699
|
+
const hasFailureHook =
|
|
2700
|
+
this.hookRegistry?.hasHookFor('PostToolUseFailure', runId) === true;
|
|
2641
2701
|
const earlyCompletionDispatchedIds = new Set<string>();
|
|
2642
2702
|
const earlyCompletionDispatches: Array<Promise<void>> = [];
|
|
2643
2703
|
const dispatchRequestById = new Map(
|
|
@@ -2753,11 +2813,6 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
2753
2813
|
|
|
2754
2814
|
this.storeCodeSessionFromResults(results, requestMap);
|
|
2755
2815
|
|
|
2756
|
-
const hasPostHook =
|
|
2757
|
-
this.hookRegistry?.hasHookFor('PostToolUse', runId) === true;
|
|
2758
|
-
const hasFailureHook =
|
|
2759
|
-
this.hookRegistry?.hasHookFor('PostToolUseFailure', runId) === true;
|
|
2760
|
-
|
|
2761
2816
|
for (const result of results) {
|
|
2762
2817
|
if (result.injectedMessages && result.injectedMessages.length > 0) {
|
|
2763
2818
|
try {
|
|
@@ -2895,6 +2950,15 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
2895
2950
|
this.maxToolResultChars
|
|
2896
2951
|
);
|
|
2897
2952
|
finalToolOutput = hookResult.updatedOutput;
|
|
2953
|
+
/**
|
|
2954
|
+
* The hook ACTUALLY rewrote this output: any completion the
|
|
2955
|
+
* stream already emitted for the consumed eager result showed
|
|
2956
|
+
* the raw content, so un-mark it and let the dispatch below
|
|
2957
|
+
* re-emit the corrected version. Hooks that merely observe or
|
|
2958
|
+
* add context leave the original emission final — no
|
|
2959
|
+
* duplicates.
|
|
2960
|
+
*/
|
|
2961
|
+
eagerCompletionDispatchedIds.delete(result.toolCallId);
|
|
2898
2962
|
}
|
|
2899
2963
|
}
|
|
2900
2964
|
|
|
@@ -2971,11 +3035,12 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
2971
3035
|
return { toolMessages, injected };
|
|
2972
3036
|
}
|
|
2973
3037
|
|
|
2974
|
-
|
|
3038
|
+
/** Run-scoped so another run's session hooks can't flip this run's gate. */
|
|
3039
|
+
private canConsumeEagerEventExecution(runId?: string): boolean {
|
|
2975
3040
|
return (
|
|
2976
3041
|
this.eventDrivenMode &&
|
|
2977
3042
|
this.eagerEventToolExecution?.enabled === true &&
|
|
2978
|
-
this.hookRegistry
|
|
3043
|
+
this.hookRegistry?.hasResultAlteringHooks(runId) !== true &&
|
|
2979
3044
|
this.humanInTheLoop?.enabled !== true
|
|
2980
3045
|
);
|
|
2981
3046
|
}
|
|
@@ -2983,7 +3048,15 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
2983
3048
|
private takeMatchingEagerEventExecution(
|
|
2984
3049
|
request: t.ToolCallRequest
|
|
2985
3050
|
): t.EagerEventToolExecution | undefined {
|
|
2986
|
-
|
|
3051
|
+
// Static enablement only: a stored record means the reservation-time
|
|
3052
|
+
// gates passed and the host already dispatched the execution. Re-checking
|
|
3053
|
+
// the dynamic hook gate here could decline consumption after a mid-run
|
|
3054
|
+
// registration and send the same call through normal dispatch — executing
|
|
3055
|
+
// the tool twice. PostToolUse hooks still process consumed results below.
|
|
3056
|
+
if (
|
|
3057
|
+
!this.eventDrivenMode ||
|
|
3058
|
+
this.eagerEventToolExecution?.enabled !== true
|
|
3059
|
+
) {
|
|
2987
3060
|
return undefined;
|
|
2988
3061
|
}
|
|
2989
3062
|
|
|
@@ -3098,6 +3171,7 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
3098
3171
|
orderedBatchEntries.push(entry);
|
|
3099
3172
|
}
|
|
3100
3173
|
}
|
|
3174
|
+
const hookInjectedMessages: t.InjectedMessage[] = [];
|
|
3101
3175
|
if (
|
|
3102
3176
|
this.hookRegistry?.hasHookFor('PostToolBatch', runId) === true &&
|
|
3103
3177
|
orderedBatchEntries.length > 0
|
|
@@ -3118,6 +3192,9 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
3118
3192
|
for (const ctx of batchHookResult.additionalContexts) {
|
|
3119
3193
|
batchAdditionalContexts.push(ctx);
|
|
3120
3194
|
}
|
|
3195
|
+
for (const msg of batchHookResult.injectedMessages) {
|
|
3196
|
+
hookInjectedMessages.push(msg);
|
|
3197
|
+
}
|
|
3121
3198
|
}
|
|
3122
3199
|
}
|
|
3123
3200
|
|
|
@@ -3137,6 +3214,24 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
3137
3214
|
})
|
|
3138
3215
|
);
|
|
3139
3216
|
}
|
|
3217
|
+
|
|
3218
|
+
/**
|
|
3219
|
+
* Hook-returned `injectedMessages` land AFTER the consolidated context
|
|
3220
|
+
* message: one converted `HumanMessage` per entry (role/source kept in
|
|
3221
|
+
* `additional_kwargs`), preserving per-message identity so verbatim
|
|
3222
|
+
* user speech (e.g. steering) sits closest to the next model call.
|
|
3223
|
+
*/
|
|
3224
|
+
if (hookInjectedMessages.length > 0) {
|
|
3225
|
+
try {
|
|
3226
|
+
injected.push(...this.convertInjectedMessages(hookInjectedMessages));
|
|
3227
|
+
} catch (e) {
|
|
3228
|
+
// eslint-disable-next-line no-console
|
|
3229
|
+
console.warn(
|
|
3230
|
+
'[ToolNode] Failed to convert PostToolBatch injectedMessages:',
|
|
3231
|
+
e instanceof Error ? e.message : e
|
|
3232
|
+
);
|
|
3233
|
+
}
|
|
3234
|
+
}
|
|
3140
3235
|
}
|
|
3141
3236
|
|
|
3142
3237
|
private async dispatchStepCompleted(
|