@librechat/agents 3.7.7 → 3.7.9

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 (167) hide show
  1. package/dist/cjs/agents/AgentContext.cjs +26 -5
  2. package/dist/cjs/agents/AgentContext.cjs.map +1 -1
  3. package/dist/cjs/common/constants.cjs +12 -0
  4. package/dist/cjs/common/constants.cjs.map +1 -1
  5. package/dist/cjs/graphs/Graph.cjs +56 -11
  6. package/dist/cjs/graphs/Graph.cjs.map +1 -1
  7. package/dist/cjs/graphs/MultiAgentGraph.cjs +1 -1
  8. package/dist/cjs/hooks/HookRegistry.cjs.map +1 -1
  9. package/dist/cjs/hooks/executeHooks.cjs +8 -0
  10. package/dist/cjs/hooks/executeHooks.cjs.map +1 -1
  11. package/dist/cjs/hooks/index.cjs +2 -0
  12. package/dist/cjs/hooks/index.cjs.map +1 -1
  13. package/dist/cjs/hooks/types.cjs +1 -0
  14. package/dist/cjs/hooks/types.cjs.map +1 -1
  15. package/dist/cjs/langfuse.cjs +43 -3
  16. package/dist/cjs/langfuse.cjs.map +1 -1
  17. package/dist/cjs/langfuseTraceShaping.cjs +75 -0
  18. package/dist/cjs/langfuseTraceShaping.cjs.map +1 -1
  19. package/dist/cjs/llm/bedrock/toolCache.cjs +1 -1
  20. package/dist/cjs/llm/fake.cjs +9 -5
  21. package/dist/cjs/llm/fake.cjs.map +1 -1
  22. package/dist/cjs/llm/invoke.cjs +3 -7
  23. package/dist/cjs/llm/invoke.cjs.map +1 -1
  24. package/dist/cjs/llm/openrouter/toolCache.cjs +1 -1
  25. package/dist/cjs/llm/prepareProviderRequest.cjs +2 -2
  26. package/dist/cjs/llm/providers.cjs +1 -1
  27. package/dist/cjs/llm/request.cjs +6 -0
  28. package/dist/cjs/llm/request.cjs.map +1 -1
  29. package/dist/cjs/llm/truncation.cjs +1 -0
  30. package/dist/cjs/main.cjs +22 -13
  31. package/dist/cjs/messages/format.cjs +375 -3
  32. package/dist/cjs/messages/format.cjs.map +1 -1
  33. package/dist/cjs/messages/index.cjs +1 -1
  34. package/dist/cjs/messages/prune.cjs +1 -1
  35. package/dist/cjs/run.cjs +217 -52
  36. package/dist/cjs/run.cjs.map +1 -1
  37. package/dist/cjs/session/AgentSession.cjs +1 -1
  38. package/dist/cjs/session/JsonlSessionStore.cjs +1 -1
  39. package/dist/cjs/stream.cjs +1 -1
  40. package/dist/cjs/summarization/index.cjs +1 -0
  41. package/dist/cjs/summarization/index.cjs.map +1 -1
  42. package/dist/cjs/summarization/node.cjs +58 -85
  43. package/dist/cjs/summarization/node.cjs.map +1 -1
  44. package/dist/cjs/summarization/semanticIndex.cjs +366 -0
  45. package/dist/cjs/summarization/semanticIndex.cjs.map +1 -0
  46. package/dist/cjs/summarization/shared.cjs +83 -0
  47. package/dist/cjs/summarization/shared.cjs.map +1 -0
  48. package/dist/cjs/tools/ToolNode.cjs +5 -5
  49. package/dist/cjs/tools/local/CompileCheckTool.cjs +1 -1
  50. package/dist/cjs/tools/local/LocalCodingTools.cjs +1 -1
  51. package/dist/cjs/tools/local/LocalExecutionEngine.cjs +1 -1
  52. package/dist/cjs/tools/runStepResume.cjs +1 -1
  53. package/dist/cjs/tools/runStepResume.cjs.map +1 -1
  54. package/dist/cjs/tools/subagent/SubagentExecutor.cjs +8 -12
  55. package/dist/cjs/tools/subagent/SubagentExecutor.cjs.map +1 -1
  56. package/dist/cjs/tools/subagent/childGraphConfig.cjs +2 -1
  57. package/dist/cjs/tools/subagent/childGraphConfig.cjs.map +1 -1
  58. package/dist/cjs/utils/index.cjs +1 -1
  59. package/dist/cjs/utils/tokens.cjs +8 -1
  60. package/dist/cjs/utils/tokens.cjs.map +1 -1
  61. package/dist/esm/agents/AgentContext.mjs +26 -5
  62. package/dist/esm/agents/AgentContext.mjs.map +1 -1
  63. package/dist/esm/common/constants.mjs +11 -1
  64. package/dist/esm/common/constants.mjs.map +1 -1
  65. package/dist/esm/graphs/Graph.mjs +56 -11
  66. package/dist/esm/graphs/Graph.mjs.map +1 -1
  67. package/dist/esm/graphs/MultiAgentGraph.mjs +1 -1
  68. package/dist/esm/hooks/HookRegistry.mjs.map +1 -1
  69. package/dist/esm/hooks/executeHooks.mjs +8 -1
  70. package/dist/esm/hooks/executeHooks.mjs.map +1 -1
  71. package/dist/esm/hooks/index.mjs +2 -1
  72. package/dist/esm/hooks/index.mjs.map +1 -1
  73. package/dist/esm/hooks/types.mjs +1 -0
  74. package/dist/esm/hooks/types.mjs.map +1 -1
  75. package/dist/esm/langfuse.mjs +43 -3
  76. package/dist/esm/langfuse.mjs.map +1 -1
  77. package/dist/esm/langfuseTraceShaping.mjs +75 -0
  78. package/dist/esm/langfuseTraceShaping.mjs.map +1 -1
  79. package/dist/esm/llm/bedrock/toolCache.mjs +1 -1
  80. package/dist/esm/llm/fake.mjs +9 -5
  81. package/dist/esm/llm/fake.mjs.map +1 -1
  82. package/dist/esm/llm/invoke.mjs +3 -7
  83. package/dist/esm/llm/invoke.mjs.map +1 -1
  84. package/dist/esm/llm/openrouter/toolCache.mjs +1 -1
  85. package/dist/esm/llm/prepareProviderRequest.mjs +2 -2
  86. package/dist/esm/llm/providers.mjs +1 -1
  87. package/dist/esm/llm/request.mjs +6 -1
  88. package/dist/esm/llm/request.mjs.map +1 -1
  89. package/dist/esm/llm/truncation.mjs +1 -1
  90. package/dist/esm/main.mjs +15 -14
  91. package/dist/esm/messages/format.mjs +375 -3
  92. package/dist/esm/messages/format.mjs.map +1 -1
  93. package/dist/esm/messages/index.mjs +1 -1
  94. package/dist/esm/messages/prune.mjs +1 -1
  95. package/dist/esm/run.mjs +217 -52
  96. package/dist/esm/run.mjs.map +1 -1
  97. package/dist/esm/session/AgentSession.mjs +1 -1
  98. package/dist/esm/session/JsonlSessionStore.mjs +1 -1
  99. package/dist/esm/stream.mjs +1 -1
  100. package/dist/esm/summarization/index.mjs +1 -0
  101. package/dist/esm/summarization/index.mjs.map +1 -1
  102. package/dist/esm/summarization/node.mjs +57 -84
  103. package/dist/esm/summarization/node.mjs.map +1 -1
  104. package/dist/esm/summarization/semanticIndex.mjs +363 -0
  105. package/dist/esm/summarization/semanticIndex.mjs.map +1 -0
  106. package/dist/esm/summarization/shared.mjs +79 -0
  107. package/dist/esm/summarization/shared.mjs.map +1 -0
  108. package/dist/esm/tools/ToolNode.mjs +5 -5
  109. package/dist/esm/tools/local/CompileCheckTool.mjs +1 -1
  110. package/dist/esm/tools/local/LocalCodingTools.mjs +1 -1
  111. package/dist/esm/tools/local/LocalExecutionEngine.mjs +1 -1
  112. package/dist/esm/tools/runStepResume.mjs +1 -1
  113. package/dist/esm/tools/runStepResume.mjs.map +1 -1
  114. package/dist/esm/tools/subagent/SubagentExecutor.mjs +8 -12
  115. package/dist/esm/tools/subagent/SubagentExecutor.mjs.map +1 -1
  116. package/dist/esm/tools/subagent/childGraphConfig.mjs +2 -1
  117. package/dist/esm/tools/subagent/childGraphConfig.mjs.map +1 -1
  118. package/dist/esm/utils/index.mjs +1 -1
  119. package/dist/esm/utils/tokens.mjs +8 -2
  120. package/dist/esm/utils/tokens.mjs.map +1 -1
  121. package/dist/types/agents/AgentContext.d.ts +27 -1
  122. package/dist/types/common/constants.d.ts +17 -0
  123. package/dist/types/graphs/Graph.d.ts +24 -0
  124. package/dist/types/hooks/executeHooks.d.ts +5 -2
  125. package/dist/types/hooks/index.d.ts +7 -2
  126. package/dist/types/hooks/types.d.ts +45 -7
  127. package/dist/types/langfuse.d.ts +3 -1
  128. package/dist/types/llm/fake.d.ts +12 -2
  129. package/dist/types/llm/request.d.ts +10 -0
  130. package/dist/types/messages/format.d.ts +17 -2
  131. package/dist/types/run.d.ts +19 -6
  132. package/dist/types/summarization/index.d.ts +6 -0
  133. package/dist/types/summarization/node.d.ts +0 -4
  134. package/dist/types/summarization/semanticIndex.d.ts +21 -0
  135. package/dist/types/summarization/shared.d.ts +25 -0
  136. package/dist/types/types/graph.d.ts +9 -1
  137. package/dist/types/types/run.d.ts +8 -0
  138. package/dist/types/types/stream.d.ts +6 -0
  139. package/dist/types/types/summarize.d.ts +44 -0
  140. package/dist/types/utils/tokens.d.ts +9 -0
  141. package/package.json +2 -1
  142. package/src/agents/AgentContext.ts +63 -6
  143. package/src/common/constants.ts +19 -0
  144. package/src/graphs/Graph.ts +86 -8
  145. package/src/hooks/HookRegistry.ts +3 -1
  146. package/src/hooks/executeHooks.ts +20 -2
  147. package/src/hooks/index.ts +12 -1
  148. package/src/hooks/types.ts +47 -4
  149. package/src/langfuse.ts +70 -0
  150. package/src/langfuseTraceShaping.ts +94 -0
  151. package/src/llm/fake.ts +35 -4
  152. package/src/llm/invoke.ts +13 -29
  153. package/src/llm/request.ts +24 -0
  154. package/src/messages/format.ts +839 -5
  155. package/src/run.ts +393 -187
  156. package/src/summarization/index.ts +11 -0
  157. package/src/summarization/node.ts +202 -158
  158. package/src/summarization/semanticIndex.ts +662 -0
  159. package/src/summarization/shared.ts +130 -0
  160. package/src/tools/runStepResume.ts +9 -0
  161. package/src/tools/subagent/SubagentExecutor.ts +4 -27
  162. package/src/tools/subagent/childGraphConfig.ts +3 -0
  163. package/src/types/graph.ts +9 -0
  164. package/src/types/run.ts +8 -0
  165. package/src/types/stream.ts +6 -0
  166. package/src/types/summarize.ts +57 -0
  167. package/src/utils/tokens.ts +31 -8
@@ -8,7 +8,7 @@ import type { InjectedMessage } from '@/types/tools';
8
8
  * `docs/hooks-design-report.md` §3.2 for the mapping to existing
9
9
  * `@librechat/agents` emission points.
10
10
  */
11
- export declare const HOOK_EVENTS: readonly ["RunStart", "UserPromptSubmit", "PreToolUse", "PostToolUse", "PostToolUseFailure", "PostToolBatch", "PreemptBoundary", "PermissionDenied", "SubagentStart", "SubagentStop", "Stop", "StopFailure", "PreCompact", "PostCompact"];
11
+ export declare const HOOK_EVENTS: readonly ["RunStart", "UserPromptSubmit", "PreToolUse", "PostToolUse", "PostToolUseFailure", "PostToolBatch", "PreemptBoundary", "PermissionDenied", "SubagentStart", "SubagentStop", "Stop", "StopFinalize", "StopFailure", "PreCompact", "PostCompact"];
12
12
  export declare const TOOL_APPROVAL_EXECUTION_SCOPE_CONFIG_KEY = "__librechat_tool_approval_execution_scope";
13
13
  export type HookEvent = (typeof HOOK_EVENTS)[number];
14
14
  /** Tool-gating decision; executeHooks folds with `deny > ask > allow` precedence. */
@@ -174,7 +174,33 @@ export interface StopHookInput extends BaseHookInput {
174
174
  hook_event_name: 'Stop';
175
175
  messages: BaseMessage[];
176
176
  stopReason?: string;
177
+ /** True after this hook has already kept the current Run warm once. */
177
178
  stopHookActive: boolean;
179
+ /** Number of terminal continuations already admitted in this Run. */
180
+ continuationCount: number;
181
+ /**
182
+ * Remaining continuations the SDK can honor. A host that durably claims
183
+ * queued work MUST seal admission instead when this reaches zero.
184
+ */
185
+ continuationBudgetRemaining: number;
186
+ }
187
+ /**
188
+ * Serialized terminal admission phase. Unlike ordinary `Stop` hooks (which
189
+ * execute in parallel), this fires after their outputs have been folded so a
190
+ * durable host can make one final claim-or-seal decision with full knowledge
191
+ * of whether another hook already kept the Run warm.
192
+ */
193
+ export interface StopFinalizeHookInput extends BaseHookInput {
194
+ hook_event_name: 'StopFinalize';
195
+ messages: BaseMessage[];
196
+ stopReason?: string;
197
+ stopHookActive: boolean;
198
+ continuationCount: number;
199
+ continuationBudgetRemaining: number;
200
+ /** A regular Stop hook already supplied a valid warm continuation. */
201
+ continuationPlanned: boolean;
202
+ /** A halt reason or Stop output forbids another graph segment. */
203
+ continuationPrevented: boolean;
178
204
  }
179
205
  export interface StopFailureHookInput extends BaseHookInput {
180
206
  hook_event_name: 'StopFailure';
@@ -203,7 +229,7 @@ export interface PostCompactHookInput extends BaseHookInput {
203
229
  messagesAfterCount: number;
204
230
  }
205
231
  /** Discriminated union of every hook input shape. */
206
- export type HookInput = RunStartHookInput | UserPromptSubmitHookInput | PreToolUseHookInput | PostToolUseHookInput | PostToolUseFailureHookInput | PostToolBatchHookInput | PreemptBoundaryHookInput | PermissionDeniedHookInput | SubagentStartHookInput | SubagentStopHookInput | StopHookInput | StopFailureHookInput | PreCompactHookInput | PostCompactHookInput;
232
+ export type HookInput = RunStartHookInput | UserPromptSubmitHookInput | PreToolUseHookInput | PostToolUseHookInput | PostToolUseFailureHookInput | PostToolBatchHookInput | PreemptBoundaryHookInput | PermissionDeniedHookInput | SubagentStartHookInput | SubagentStopHookInput | StopHookInput | StopFinalizeHookInput | StopFailureHookInput | PreCompactHookInput | PostCompactHookInput;
207
233
  /** Compile-time map from event name to its input shape. */
208
234
  export type HookInputByEvent = {
209
235
  RunStart: RunStartHookInput;
@@ -217,6 +243,7 @@ export type HookInputByEvent = {
217
243
  SubagentStart: SubagentStartHookInput;
218
244
  SubagentStop: SubagentStopHookInput;
219
245
  Stop: StopHookInput;
246
+ StopFinalize: StopFinalizeHookInput;
220
247
  StopFailure: StopFailureHookInput;
221
248
  PreCompact: PreCompactHookInput;
222
249
  PostCompact: PostCompactHookInput;
@@ -238,10 +265,11 @@ export interface BaseHookOutput {
238
265
  * mid-run steering message). Accumulated across hooks in registration
239
266
  * order.
240
267
  *
241
- * Consumed at exactly two dispatch sites, both of which run the same
242
- * converter so the emitted shapes cannot drift: `PostToolBatch` (the tool
243
- * boundary) and `PreemptBoundary` (a cooperative mid-generation seal).
244
- * Every other event ignores the field.
268
+ * Consumed at three dispatch sites, all of which run the same converter so
269
+ * the emitted shapes cannot drift: `PostToolBatch` (the tool boundary),
270
+ * `PreemptBoundary` (a cooperative mid-generation seal), and a blocking
271
+ * `Stop` hook (terminal Run continuation). Every other event ignores the
272
+ * field.
245
273
  */
246
274
  injectedMessages?: InjectedMessage[];
247
275
  /** True to prevent the next model turn. Any hook can set this. */
@@ -362,9 +390,16 @@ export interface SubagentStartHookOutput extends BaseHookOutput {
362
390
  }
363
391
  export type SubagentStopHookOutput = BaseHookOutput;
364
392
  export interface StopHookOutput extends BaseHookOutput {
393
+ /**
394
+ * `block` keeps a naturally terminal Run warm only when the output also
395
+ * supplies non-empty `injectedMessages` or `additionalContext`. The SDK
396
+ * starts another graph segment inside the same `processStream` call; no
397
+ * RunStart/UserPromptSubmit hooks or terminal cleanup occur between them.
398
+ */
365
399
  decision?: StopDecision;
366
400
  reason?: string;
367
401
  }
402
+ export type StopFinalizeHookOutput = StopHookOutput;
368
403
  export type StopFailureHookOutput = BaseHookOutput;
369
404
  export type PreCompactHookOutput = BaseHookOutput;
370
405
  export type PostCompactHookOutput = BaseHookOutput;
@@ -381,12 +416,13 @@ export type HookOutputByEvent = {
381
416
  SubagentStart: SubagentStartHookOutput;
382
417
  SubagentStop: SubagentStopHookOutput;
383
418
  Stop: StopHookOutput;
419
+ StopFinalize: StopFinalizeHookOutput;
384
420
  StopFailure: StopFailureHookOutput;
385
421
  PreCompact: PreCompactHookOutput;
386
422
  PostCompact: PostCompactHookOutput;
387
423
  };
388
424
  /** Superset output shape used by the executor's fold loop. */
389
- export type HookOutput = RunStartHookOutput | UserPromptSubmitHookOutput | PreToolUseHookOutput | PostToolUseHookOutput | PostToolUseFailureHookOutput | PostToolBatchHookOutput | PreemptBoundaryHookOutput | PermissionDeniedHookOutput | SubagentStartHookOutput | SubagentStopHookOutput | StopHookOutput | StopFailureHookOutput | PreCompactHookOutput | PostCompactHookOutput;
425
+ export type HookOutput = RunStartHookOutput | UserPromptSubmitHookOutput | PreToolUseHookOutput | PostToolUseHookOutput | PostToolUseFailureHookOutput | PostToolBatchHookOutput | PreemptBoundaryHookOutput | PermissionDeniedHookOutput | SubagentStartHookOutput | SubagentStopHookOutput | StopHookOutput | StopFinalizeHookOutput | StopFailureHookOutput | PreCompactHookOutput | PostCompactHookOutput;
390
426
  /**
391
427
  * A hook callback is a plain async function registered against a specific
392
428
  * event. The `signal` is always supplied by `executeHooks` and combines the
@@ -494,6 +530,8 @@ export interface AggregatedHookResult {
494
530
  * `preventContinuation` do not overwrite the reason.
495
531
  */
496
532
  stopReason?: string;
533
+ /** True when any hook failed, including internal hooks with hidden diagnostics. */
534
+ hasHookFailures?: true;
497
535
  /** Error messages from hooks that threw; always present (possibly empty). */
498
536
  errors: string[];
499
537
  }
@@ -25,6 +25,8 @@ type LangfuseHandlerParams = {
25
25
  * only adopted when stamped with the same run (see
26
26
  * `LangfuseRuntimeContext.runId`). */
27
27
  runId?: string;
28
+ /** Keep this root open while one processStream call executes graph segments. */
29
+ deferRootRunId?: string;
28
30
  /** The run's resolved tool-output policy — for multi-agent streams the
29
31
  * conservative aggregate across agents, which `this.langfuse` (the
30
32
  * primary agent's config) cannot reproduce. Applied when a foreign
@@ -51,7 +53,7 @@ export declare function createLangfuseTraceMetadata({ messageId, parentMessageId
51
53
  export declare function getLangfuseTraceName(traceMetadata?: LangfuseTraceMetadata, fallback?: string): string;
52
54
  export declare function shouldCreateLangfuseHandler(langfuse?: t.LangfuseConfig): boolean;
53
55
  export declare function createLegacyLangfuseHandler(params: LangfuseHandlerParams): CallbackHandler;
54
- export declare function createLangfuseHandler({ langfuse, userId, sessionId, traceMetadata, tags, traceIdSeed, traceAnchor, agentId, parentSpanContext, inheritTraceIdentity, runId, toolOutputTracing, traceName, }: AgentLangfuseHandlerParams): CallbackHandler | undefined;
56
+ export declare function createLangfuseHandler({ langfuse, userId, sessionId, traceMetadata, tags, traceIdSeed, traceAnchor, agentId, parentSpanContext, inheritTraceIdentity, runId, deferRootRunId, toolOutputTracing, traceName, }: AgentLangfuseHandlerParams): CallbackHandler | undefined;
55
57
  export declare function withLangfuseAttributes<T>(params: LangfuseAttributeParams, action: () => T): T;
56
58
  export declare function hasExplicitLangfuseConfig(contexts: Iterable<{
57
59
  langfuse?: t.LangfuseConfig;
@@ -11,15 +11,25 @@ export declare class FakeChatModel extends FakeListChatModel {
11
11
  private splitStrategy;
12
12
  private toolCalls;
13
13
  private addedToolCalls;
14
- constructor({ responses, sleep, emitCustomEvent, splitStrategy, toolCalls, }: {
14
+ /**
15
+ * Attached to the last streamed chunk of every response, mirroring a real
16
+ * provider's terminal `finish_reason`/`stop_reason` on `generationInfo`.
17
+ * Lets truncation-path tests (`getTruncationStopReason`) drive a genuine
18
+ * `Run`/`StandardGraph` without a live provider. `undefined` (the
19
+ * default) reproduces the previous behavior exactly: no metadata on any
20
+ * chunk.
21
+ */
22
+ private finalChunkGenerationInfo?;
23
+ constructor({ responses, sleep, emitCustomEvent, splitStrategy, toolCalls, finalChunkGenerationInfo, }: {
15
24
  responses: string[];
16
25
  sleep?: number;
17
26
  emitCustomEvent?: boolean;
18
27
  splitStrategy?: SplitStrategy;
19
28
  toolCalls?: ToolCall[];
29
+ finalChunkGenerationInfo?: Record<string, unknown>;
20
30
  });
21
31
  private splitText;
22
- _createResponseChunk(text: string, tool_call_chunks?: ToolCallChunk[]): ChatGenerationChunk;
32
+ _createResponseChunk(text: string, tool_call_chunks?: ToolCallChunk[], responseMetadata?: Record<string, unknown>): ChatGenerationChunk;
23
33
  _streamResponseChunks(_messages: BaseMessage[], options: this['ParsedCallOptions'], runManager?: CallbackManagerForLLMRun): AsyncGenerator<ChatGenerationChunk>;
24
34
  }
25
35
  export declare function createFakeStreamingLLM({ responses, sleep, splitStrategy, toolCalls, }: {
@@ -5,6 +5,16 @@ import type * as t from '@/types';
5
5
  * and OpenAI-compat (modelKwargs.thinking).
6
6
  */
7
7
  export declare function isThinkingEnabled(provider: t.ProviderName, clientOptions?: t.ClientOptions): boolean;
8
+ /**
9
+ * Model configured on client options, under whichever of the two keys carries
10
+ * it. LangChain accepts `modelName` as an alias for `model` and this package
11
+ * writes both (see `buildSummarizationClientConfig`), while hosts configure
12
+ * agents through either. Reading only one key therefore does not fail loudly:
13
+ * it reports an unconfigured model, and every caller here treats that as a cue
14
+ * to fall back to a default, so a Claude agent configured through the alias is
15
+ * silently handled as something else.
16
+ */
17
+ export declare function resolveClientOptionsModel(clientOptions: t.ClientOptions | undefined): string | undefined;
8
18
  /**
9
19
  * Returns the correct key for setting max output tokens on the model
10
20
  * constructor options. Google/Vertex use `maxOutputTokens`, all others
@@ -1,7 +1,7 @@
1
1
  import { AIMessage, ToolMessage, BaseMessage, HumanMessage, SystemMessage } from '@langchain/core/messages';
2
2
  import type { MessageContentImageUrl } from '@langchain/core/messages';
3
3
  import type { RunnableConfig } from '@langchain/core/runnables';
4
- import type { MessageContentComplex, TPayload, ProviderName } from '@/types';
4
+ import type { MessageContentComplex, TPayload, ProviderName, CompactionSemanticIndex, CompactionSemanticIndexSnapshot } from '@/types';
5
5
  import { Providers } from '@/common';
6
6
  interface MediaMessageParams {
7
7
  message: {
@@ -89,7 +89,7 @@ interface LangChainMessage {
89
89
  * @returns - The formatted LangChain message.
90
90
  */
91
91
  export declare const formatFromLangChain: (message: LangChainMessage) => Record<string, any>;
92
- interface FormatAgentMessagesOptions {
92
+ export interface FormatAgentMessagesOptions {
93
93
  provider?: ProviderName;
94
94
  /** Emit flattenable text content as the joined string the legacy-content
95
95
  * projection would produce, so the per-request `formatContentStrings` pass
@@ -105,6 +105,17 @@ interface FormatAgentMessagesOptions {
105
105
  * historical `skill` tool_calls are not reconstructed into a HumanMessage,
106
106
  * so the same SKILL.md body is not injected twice in one request. */
107
107
  skipSkillBodyNames?: Set<string>;
108
+ /** Derive bounded compaction guidance during the formatter's existing
109
+ * persisted-content analysis. Tool intents are accepted only for names
110
+ * the host identifies as semantic-label fields; business `intent`
111
+ * parameters must remain ordinary tool input. */
112
+ compactionSemanticIndex?: {
113
+ /** Previously committed, serializable guidance to evolve with entries
114
+ * derived from this payload. The formatter snapshots and validates
115
+ * caller-owned data before scanning the new messages. */
116
+ baseSnapshot?: CompactionSemanticIndexSnapshot;
117
+ intentToolNames?: ReadonlySet<string>;
118
+ };
108
119
  }
109
120
  /**
110
121
  * Groups content parts by agent and formats them with agent labels
@@ -148,6 +159,10 @@ export declare const formatAgentMessages: (payload: TPayload, indexTokenCountMap
148
159
  /** When a positional summary boundary sliced content from a message, the token
149
160
  * count was proportionally reduced. Returned so the caller can log it. */
150
161
  boundaryTokenAdjustment?: SummaryTokenAdjustment;
162
+ /** Bounded semantic guidance derived during persisted-content analysis. */
163
+ compactionSemanticIndex?: CompactionSemanticIndex;
164
+ /** Serializable continuation state for incrementally evolving the index. */
165
+ compactionSemanticIndexSnapshot?: CompactionSemanticIndexSnapshot;
151
166
  };
152
167
  /**
153
168
  * Adds a value at key 0 for system messages and shifts all key indices by one in an indexTokenCountMap.
@@ -1,5 +1,5 @@
1
- import { Command } from '@langchain/langgraph';
2
1
  import { BaseMessage } from '@langchain/core/messages';
2
+ import { Command } from '@langchain/langgraph';
3
3
  import type { MessageContentComplex } from '@langchain/core/messages';
4
4
  import type { MultiAgentGraph } from '@/graphs/MultiAgentGraph';
5
5
  import type { StandardGraph } from '@/graphs/Graph';
@@ -19,6 +19,7 @@ export declare class Run<_T extends t.BaseGraphState> {
19
19
  private toolExecution?;
20
20
  private subagentUsageSink?;
21
21
  private preemption?;
22
+ private maxStopContinuations;
22
23
  private streamLimits?;
23
24
  private subagentTasks?;
24
25
  private indexTokenCountMap?;
@@ -134,6 +135,15 @@ export declare class Run<_T extends t.BaseGraphState> {
134
135
  */
135
136
  getPreemptStats(): t.PreemptStats;
136
137
  getToolCount(): number;
138
+ /**
139
+ * True when the run's last turn ended at `END` because the provider hit
140
+ * its output token ceiling while producing plain text/reasoning — no tool
141
+ * call, so `assertNotTruncatedToolCall` never sees it and the graph reads
142
+ * the turn as an ordinary completion. Hosts check this alongside
143
+ * `getPreemptStats()` / `getHaltReason()` to decide whether to persist the
144
+ * response as unfinished instead of a silently truncated "complete" one.
145
+ */
146
+ getOutputTruncated(): boolean;
137
147
  /**
138
148
  * Creates a custom event callback handler that intercepts custom events
139
149
  * and processes them through our handler registry instead of EventStreamCallbackHandler
@@ -175,13 +185,16 @@ export declare class Run<_T extends t.BaseGraphState> {
175
185
  */
176
186
  getInterrupt<TPayload = t.HumanInterruptPayload>(): t.RunInterruptResult<TPayload> | undefined;
177
187
  /**
178
- * Returns the reason a hook halted the run via
179
- * `preventContinuation: true`, or `undefined` if no hook halted.
188
+ * Returns why the run ended without a natural completion, or `undefined`
189
+ * when it completed normally. Reasons include hook- and prompt-driven
190
+ * halts, `preempt_incomplete` when a cooperative seal ended the turn
191
+ * without continuation content, and `output_truncated` when the provider
192
+ * stopped a plain-text/reasoning response at its output-token ceiling.
180
193
  *
181
194
  * Hosts inspect this after `processStream` returns to distinguish a
182
- * natural completion (`undefined`) from a hook-driven halt (a
183
- * truthy string). Independent from `getInterrupt()` — a halted run
184
- * has no interrupt; an interrupted run has no halt reason.
195
+ * natural completion from a terminal partial response. Independent from
196
+ * `getInterrupt()` — a halted run has no interrupt; an interrupted run has
197
+ * no halt reason.
185
198
  */
186
199
  getHaltReason(): string | undefined;
187
200
  /**
@@ -1,4 +1,10 @@
1
1
  import type { SummarizationTrigger } from '@/types';
2
+ /**
3
+ * The summary-boundary seam a caller compacting outside a run needs. The two
4
+ * default prompts stay out of it: LibreChat's manual flow deliberately words
5
+ * its own, so exporting these would publish an API with no consumer.
6
+ */
7
+ export { buildSummarizationInstruction, buildSummaryCarrierText, separateSummarizationParameters, } from './shared';
2
8
  /** For tests only. Resets the dedup set so warnings can be observed again. */
3
9
  export declare function _resetUnrecognizedTriggerWarnings(): void;
4
10
  /**
@@ -6,10 +6,6 @@ import type { HookRegistry } from '@/hooks';
6
6
  import type { OnChunk } from '@/llm/invoke';
7
7
  import type * as t from '@/types';
8
8
  import { type PromptCacheTtl } from '@/messages/cache';
9
- /** Structured checkpoint prompt for fresh summarization (no prior summary). */
10
- export declare const DEFAULT_SUMMARIZATION_PROMPT = "Hold on, before you continue I need you to write me a checkpoint of everything so far. Your context window is filling up and this checkpoint replaces the messages above, so capture everything you need to pick right back up.\n\nDon't second-guess or fact-check anything you did, your tool results reflect exactly what happened. If a tool result appears truncated, that's just a display artifact from context management: the tool executed fully. Just record what you did and what you observed. Only the checkpoint, don't respond to me or continue the conversation.\n\n## Checkpoint\n\n## Goal\nWhat I asked you to do and any sub-goals you identified.\n\n## Constraints & Preferences\nAny rules, preferences, or configuration I established.\n\n## Progress\n### Done\n- What you completed and the outcomes\n\n### In Progress\n- What you're currently working on\n\n## Key Decisions\nDecisions you made and why.\n\n## Next Steps\nConcrete task actions remaining, in priority order.\n\n## Critical Context\nExact identifiers, names, error messages, URLs, and details you need to preserve verbatim.\n\nRules:\n- Record what you did and observed, don't judge or re-evaluate it\n- For each tool call: the tool name, key inputs, and the outcome\n- Preserve exact identifiers, names, errors, and references verbatim\n- Short declarative sentences\n- Skip empty sections";
11
- /** Prompt for re-compaction when a prior summary exists. */
12
- export declare const DEFAULT_UPDATE_SUMMARIZATION_PROMPT = "Hold on again, update your checkpoint. Merge the new messages into your existing checkpoint and give me a single consolidated replacement.\n\nKeep it roughly the same length as your last checkpoint. Compress older details to make room for what's new, don't just append. Give recent actions more detail, compress older items to one-liners.\n\nDon't fact-check or second-guess anything, your tool results are ground truth. If a tool result appears truncated, that's just a display artifact: the tool executed fully. Only the checkpoint, don't respond to me or continue the conversation.\n\nRules:\n- Merge new progress into existing sections, don't duplicate headers\n- Compress older completed items into one-line entries\n- Move items from \"In Progress\" to \"Done\" when you completed them\n- Update \"Next Steps\" to reflect current task priorities.\n- For each new tool call: the tool name, key inputs, and the outcome\n- Preserve exact identifiers, names, errors, and references verbatim\n- Skip empty sections";
13
9
  interface CreateSummarizeNodeParams {
14
10
  agentContext: AgentContext;
15
11
  graph: {
@@ -0,0 +1,21 @@
1
+ import type { BaseMessage } from '@langchain/core/messages';
2
+ import type { CompactionSemanticIndex } from '@/types';
3
+ export { COMPACTION_SEMANTIC_INDEX_LIMITS } from '@/common';
4
+ /** Records producer-side omissions without retaining the discarded entries. */
5
+ export declare function setCompactionSemanticIndexProvidedEntryCount(index: CompactionSemanticIndex, providedEntryCount: number): void;
6
+ /** Reads producer-side cardinality without exposing snapshot bookkeeping. */
7
+ export declare function getCompactionSemanticIndexProvidedEntryCount(index: CompactionSemanticIndex): number;
8
+ export type RenderedCompactionSemanticIndex = {
9
+ appendix: string;
10
+ providedEntryCount: number;
11
+ entryCount: number;
12
+ charCount: number;
13
+ omittedEntryCount: number;
14
+ };
15
+ /** Captures caller-owned data before graph execution can cross an await. */
16
+ export declare function snapshotCompactionSemanticIndex(index: CompactionSemanticIndex | undefined): CompactionSemanticIndex | undefined;
17
+ /**
18
+ * Produces a deterministic, bounded compaction appendix. Invalid, stale,
19
+ * pending, redacted, conflicting, and out-of-range entries fail closed.
20
+ */
21
+ export declare function renderCompactionSemanticIndex(index: CompactionSemanticIndex | undefined, messagesToRefine: BaseMessage[]): RenderedCompactionSemanticIndex;
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Summarization primitives shared by the in-run summarize node and by callers
3
+ * that compact a conversation outside a run. Kept apart from `node.ts` so the
4
+ * package can export them without exporting the graph node itself.
5
+ */
6
+ /**
7
+ * Wraps a persisted summary in the carrier it is re-injected as, ahead of the
8
+ * messages that survived compaction.
9
+ *
10
+ * A stored summary costs what this returns, not what its body costs, so a
11
+ * caller budgeting for one measures this rather than adding a remembered
12
+ * constant to the bare text. The wrapper alone is ~48 tokens on `o200k_base`
13
+ * and more on Anthropic: too much to leave out of a context calculation, and
14
+ * too easy to get wrong from memory once the instruction is edited.
15
+ */
16
+ export declare function buildSummaryCarrierText(summaryText: string): string;
17
+ /** Structured checkpoint prompt for fresh summarization (no prior summary). */
18
+ export declare const DEFAULT_SUMMARIZATION_PROMPT = "Hold on, before you continue I need you to write me a checkpoint of everything so far. Your context window is filling up and this checkpoint replaces the messages above, so capture everything you need to pick right back up.\n\nDon't second-guess or fact-check anything you did, your tool results reflect exactly what happened. If a tool result appears truncated, that's just a display artifact from context management: the tool executed fully. Just record what you did and what you observed. Only the checkpoint, don't respond to me or continue the conversation.\n\n## Checkpoint\n\n## Goal\nWhat I asked you to do and any sub-goals you identified.\n\n## Constraints & Preferences\nAny rules, preferences, or configuration I established.\n\n## Progress\n### Done\n- What you completed and the outcomes\n\n### In Progress\n- What you're currently working on\n\n## Key Decisions\nDecisions you made and why.\n\n## Next Steps\nConcrete task actions remaining, in priority order.\n\n## Critical Context\nExact identifiers, names, error messages, URLs, and details you need to preserve verbatim.\n\nRules:\n- Record what you did and observed, don't judge or re-evaluate it\n- For each tool call: the tool name, key inputs, and the outcome\n- Preserve exact identifiers, names, errors, and references verbatim\n- Short declarative sentences\n- Skip empty sections";
19
+ /** Prompt for re-compaction when a prior summary exists. */
20
+ export declare const DEFAULT_UPDATE_SUMMARIZATION_PROMPT = "Hold on again, update your checkpoint. Merge the new messages into your existing checkpoint and give me a single consolidated replacement.\n\nKeep it roughly the same length as your last checkpoint. Compress older details to make room for what's new, don't just append. Give recent actions more detail, compress older items to one-liners.\n\nDon't fact-check or second-guess anything, your tool results are ground truth. If a tool result appears truncated, that's just a display artifact: the tool executed fully. Only the checkpoint, don't respond to me or continue the conversation.\n\nRules:\n- Merge new progress into existing sections, don't duplicate headers\n- Compress older completed items into one-line entries\n- Move items from \"In Progress\" to \"Done\" when you completed them\n- Update \"Next Steps\" to reflect current task priorities.\n- For each new tool call: the tool name, key inputs, and the outcome\n- Preserve exact identifiers, names, errors, and references verbatim\n- Skip empty sections";
21
+ export declare function separateSummarizationParameters(parameters: Record<string, unknown>): {
22
+ llmParams: Record<string, unknown>;
23
+ maxSummaryTokens?: number;
24
+ };
25
+ export declare function buildSummarizationInstruction(promptText: string, updatePromptText: string | undefined, priorSummaryText?: string, semanticIndexAppendix?: string): string;
@@ -4,7 +4,7 @@ import type { START, StateGraph, StateGraphArgs } from '@langchain/langgraph';
4
4
  import type { RunnableConfig, Runnable } from '@langchain/core/runnables';
5
5
  import type { ChatGenerationChunk } from '@langchain/core/outputs';
6
6
  import type { GoogleAIToolType } from '@langchain/google-common';
7
- import type { SummarizationNodeInput, SummarizeCompleteEvent, SummarizationConfig, SummarizeStartEvent, SummarizeDeltaEvent } from '@/types/summarize';
7
+ import type { SummarizationNodeInput, SummarizeCompleteEvent, CompactionSemanticIndex, SummarizationConfig, SummarizeStartEvent, SummarizeDeltaEvent } from '@/types/summarize';
8
8
  import type { RunStep, RunStepDeltaEvent, RunStepResumeState, RunStepClosedEvent, MessageDeltaEvent, ReasoningDeltaEvent } from '@/types/stream';
9
9
  import type { ToolMap, ToolSessionMap, ToolEndEvent, GenericTool, LCTool, ToolExecutionConfig, ToolExecuteBatchRequest } from '@/types/tools';
10
10
  import type { TokenCounter, StreamLimits, StreamPreemption, TokenBudgetBreakdown } from '@/types/run';
@@ -693,6 +693,14 @@ interface AgentInputFields {
693
693
  discoveredTools?: string[];
694
694
  summarizationEnabled?: boolean;
695
695
  summarizationConfig?: SummarizationConfig;
696
+ /**
697
+ * Optional host-supplied, user-visible guidance for compaction. The SDK
698
+ * validates, bounds, and scopes entries to the messages being compacted;
699
+ * raw conversation messages remain authoritative. Captured when the
700
+ * AgentContext is constructed; labels committed later in the same run are
701
+ * outside this construction-time interface.
702
+ */
703
+ compactionSemanticIndex?: CompactionSemanticIndex;
696
704
  /** Cross-run summary from a previous run, forwarded from formatAgentMessages.
697
705
  * Injected into the dynamic system tail via AgentContext. */
698
706
  initialSummary?: {
@@ -233,6 +233,14 @@ export type RunConfig = {
233
233
  * block to prevent leaks.
234
234
  */
235
235
  hooks?: HookRegistry;
236
+ /**
237
+ * Maximum number of times a `Stop` hook may keep this Run warm by returning
238
+ * `decision: 'block'` with messages to inject. Defaults to
239
+ * `DEFAULT_MAX_STOP_CONTINUATIONS`; non-finite values use the default and
240
+ * values at or below zero disable terminal continuation while preserving the
241
+ * final Stop notification.
242
+ */
243
+ maxStopContinuations?: number;
236
244
  /**
237
245
  * Opt-in cooperative preemption for this run. Requires a `hooks` registry
238
246
  * with a `PreemptBoundary` matcher — the seal only stops the stream, the
@@ -87,6 +87,12 @@ export interface RunStepResumeState {
87
87
  version: 1;
88
88
  revision: number;
89
89
  nextIndex: number;
90
+ /** Warm terminal continuations already admitted for this execution. */
91
+ stopContinuationCount?: number;
92
+ /** Identifies the fresh execution that owns this continuation lifecycle. */
93
+ stopContinuationExecutionId?: string;
94
+ /** Distinguishes LangGraph event keys when a warm continuation restarts steps. */
95
+ streamSegment?: number;
90
96
  toolCallSteps: Array<{
91
97
  toolCallId: string;
92
98
  stepId: string;
@@ -29,6 +29,45 @@ export type RetainRecentConfig = {
29
29
  */
30
30
  tokens?: number;
31
31
  };
32
+ export type CompactionSemanticIndexStatus = 'committed' | 'pending';
33
+ type CompactionSemanticIndexEntryBase = {
34
+ /** Persisted message that owns the indexed content. */
35
+ sourceMessageId: string;
36
+ /** Zero-based content-part index within the persisted source message. */
37
+ sourceContentIndex: number;
38
+ /** Monotonic host revision for this logical entry. */
39
+ revision: number;
40
+ /** Only committed entries may guide compaction. */
41
+ status: CompactionSemanticIndexStatus;
42
+ /** User-visible semantic guidance. Hidden reasoning must never be supplied. */
43
+ text: string;
44
+ /** Omits the entry entirely when host policy redacts its source. */
45
+ redacted?: boolean;
46
+ };
47
+ export type CompactionToolSemanticIndexEntry = CompactionSemanticIndexEntryBase & {
48
+ type: 'tool_intent' | 'tool_outcome';
49
+ toolCallId: string;
50
+ };
51
+ export type CompactionActivitySemanticIndexEntry = CompactionSemanticIndexEntryBase & {
52
+ type: 'activity_phase';
53
+ };
54
+ export type CompactionReasoningSemanticIndexEntry = CompactionSemanticIndexEntryBase & {
55
+ type: 'reasoning_label';
56
+ /** Stable identity shared by every user-visible label revision. */
57
+ reasoningStepId: string;
58
+ };
59
+ /**
60
+ * Source-addressed navigation hints for the compaction model. Entries remain
61
+ * advisory: raw messages are always sent and remain authoritative.
62
+ */
63
+ export type CompactionSemanticIndexEntry = CompactionToolSemanticIndexEntry | CompactionActivitySemanticIndexEntry | CompactionReasoningSemanticIndexEntry;
64
+ export type CompactionSemanticIndex = readonly CompactionSemanticIndexEntry[];
65
+ /** Serializable continuation state for a bounded compaction semantic index. */
66
+ export type CompactionSemanticIndexSnapshot = {
67
+ entries: CompactionSemanticIndex;
68
+ /** Cumulative entries supplied before validation and bounded retention. */
69
+ providedEntryCount: number;
70
+ };
32
71
  export type SummarizationConfig = {
33
72
  provider?: ProviderName;
34
73
  model?: string;
@@ -86,6 +125,10 @@ export interface SummarizeStartEvent {
86
125
  messagesToRefineCount: number;
87
126
  /** Which summarization cycle this is (1-based, increments each time summarization fires) */
88
127
  summaryVersion: number;
128
+ /** Committed, source-valid semantic hints included in the request. */
129
+ semanticIndexEntryCount?: number;
130
+ /** Serialized semantic-index characters included in the request. */
131
+ semanticIndexCharCount?: number;
89
132
  }
90
133
  export interface SummarizeDeltaEvent {
91
134
  id: string;
@@ -99,3 +142,4 @@ export interface SummarizeCompleteEvent {
99
142
  summary?: SummaryContentBlock;
100
143
  error?: string;
101
144
  }
145
+ export {};
@@ -69,6 +69,15 @@ export declare function getTokenCountForMessage(message: BaseMessage, getTokenCo
69
69
  * computed as a single rounded product of the summed raw counts.
70
70
  */
71
71
  export declare function apportionTokenCounts(rawCounts: Record<string, number>, multiplier: number, targetTotal: number): Record<string, number>;
72
+ /**
73
+ * Encoding a counter measures in, for counters built here.
74
+ *
75
+ * `undefined` for a counter the host supplied itself: unknown, not wrong. A
76
+ * caller that needs a count in a specific encoding can therefore tell "counts
77
+ * in the encoding I need" from "counts in a different one" without treating
78
+ * every foreign counter as suspect.
79
+ */
80
+ export declare function encodingOfTokenCounter(tokenCounter: (message: BaseMessage) => number): EncodingName | undefined;
72
81
  /**
73
82
  * Creates a token counter function using the specified encoding.
74
83
  * Lazily loads the encoding data on first use via dynamic import.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@librechat/agents",
3
- "version": "3.7.7",
3
+ "version": "3.7.9",
4
4
  "reova": {
5
5
  "enabled": true,
6
6
  "endpoint": "https://telemetry.reo.dev/data"
@@ -221,6 +221,7 @@
221
221
  "bench:provider-derivation": "tsx ./src/scripts/bench-provider-derivation.ts",
222
222
  "bench:provider-projection": "tsx ./src/scripts/bench-provider-request-projection.ts",
223
223
  "bench:execution-world": "tsx ./src/scripts/bench-execution-world.ts",
224
+ "bench:compaction-semantic-index": "tsx ./src/scripts/bench-compaction-semantic-index.ts",
224
225
  "probe:overflow": "tsx -r dotenv/config ./src/scripts/context-overflow-probe.ts",
225
226
  "subagent": "tsx -r dotenv/config ./src/scripts/multi-agent-subagent.ts",
226
227
  "subagent:events": "tsx -r dotenv/config ./src/scripts/subagent-event-driven-debug.ts",