@librechat/agents 3.4.0 → 3.4.1

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 (114) hide show
  1. package/dist/cjs/graphs/Graph.cjs +111 -27
  2. package/dist/cjs/graphs/Graph.cjs.map +1 -1
  3. package/dist/cjs/hooks/HookRegistry.cjs +83 -0
  4. package/dist/cjs/hooks/HookRegistry.cjs.map +1 -1
  5. package/dist/cjs/hooks/executeHooks.cjs +60 -14
  6. package/dist/cjs/hooks/executeHooks.cjs.map +1 -1
  7. package/dist/cjs/hooks/index.cjs.map +1 -1
  8. package/dist/cjs/hooks/types.cjs +2 -0
  9. package/dist/cjs/hooks/types.cjs.map +1 -1
  10. package/dist/cjs/llm/init.cjs +3 -3
  11. package/dist/cjs/llm/invoke.cjs +2 -2
  12. package/dist/cjs/main.cjs +14 -13
  13. package/dist/cjs/messages/format.cjs +40 -23
  14. package/dist/cjs/messages/format.cjs.map +1 -1
  15. package/dist/cjs/run.cjs +82 -15
  16. package/dist/cjs/run.cjs.map +1 -1
  17. package/dist/cjs/session/AgentSession.cjs +37 -2
  18. package/dist/cjs/session/AgentSession.cjs.map +1 -1
  19. package/dist/cjs/stream.cjs +1 -1
  20. package/dist/cjs/summarization/node.cjs +6 -3
  21. package/dist/cjs/summarization/node.cjs.map +1 -1
  22. package/dist/cjs/tools/BashExecutor.cjs +1 -1
  23. package/dist/cjs/tools/CodeExecutor.cjs +1 -1
  24. package/dist/cjs/tools/ProgrammaticToolCalling.cjs +1 -1
  25. package/dist/cjs/tools/ToolNode.cjs +316 -94
  26. package/dist/cjs/tools/ToolNode.cjs.map +1 -1
  27. package/dist/cjs/tools/ToolSearch.cjs +1 -1
  28. package/dist/cjs/tools/subagent/SubagentExecutor.cjs +630 -78
  29. package/dist/cjs/tools/subagent/SubagentExecutor.cjs.map +1 -1
  30. package/dist/cjs/tools/subagent/SubagentReplay.cjs +175 -0
  31. package/dist/cjs/tools/subagent/SubagentReplay.cjs.map +1 -0
  32. package/dist/cjs/tools/toolOutputReferences.cjs +20 -0
  33. package/dist/cjs/tools/toolOutputReferences.cjs.map +1 -1
  34. package/dist/cjs/utils/index.cjs +2 -2
  35. package/dist/esm/graphs/Graph.mjs +111 -27
  36. package/dist/esm/graphs/Graph.mjs.map +1 -1
  37. package/dist/esm/hooks/HookRegistry.mjs +83 -0
  38. package/dist/esm/hooks/HookRegistry.mjs.map +1 -1
  39. package/dist/esm/hooks/executeHooks.mjs +60 -14
  40. package/dist/esm/hooks/executeHooks.mjs.map +1 -1
  41. package/dist/esm/hooks/index.mjs.map +1 -1
  42. package/dist/esm/hooks/types.mjs +2 -1
  43. package/dist/esm/hooks/types.mjs.map +1 -1
  44. package/dist/esm/llm/init.mjs +1 -1
  45. package/dist/esm/llm/invoke.mjs +2 -2
  46. package/dist/esm/main.mjs +11 -11
  47. package/dist/esm/messages/format.mjs +40 -23
  48. package/dist/esm/messages/format.mjs.map +1 -1
  49. package/dist/esm/run.mjs +83 -16
  50. package/dist/esm/run.mjs.map +1 -1
  51. package/dist/esm/session/AgentSession.mjs +37 -2
  52. package/dist/esm/session/AgentSession.mjs.map +1 -1
  53. package/dist/esm/stream.mjs +1 -1
  54. package/dist/esm/summarization/node.mjs +6 -3
  55. package/dist/esm/summarization/node.mjs.map +1 -1
  56. package/dist/esm/tools/BashExecutor.mjs +1 -1
  57. package/dist/esm/tools/CodeExecutor.mjs +1 -1
  58. package/dist/esm/tools/ProgrammaticToolCalling.mjs +1 -1
  59. package/dist/esm/tools/ToolNode.mjs +317 -95
  60. package/dist/esm/tools/ToolNode.mjs.map +1 -1
  61. package/dist/esm/tools/ToolSearch.mjs +1 -1
  62. package/dist/esm/tools/subagent/SubagentExecutor.mjs +631 -79
  63. package/dist/esm/tools/subagent/SubagentExecutor.mjs.map +1 -1
  64. package/dist/esm/tools/subagent/SubagentReplay.mjs +168 -0
  65. package/dist/esm/tools/subagent/SubagentReplay.mjs.map +1 -0
  66. package/dist/esm/tools/toolOutputReferences.mjs +20 -0
  67. package/dist/esm/tools/toolOutputReferences.mjs.map +1 -1
  68. package/dist/esm/utils/index.mjs +2 -2
  69. package/dist/types/graphs/Graph.d.ts +16 -1
  70. package/dist/types/hooks/HookRegistry.d.ts +10 -1
  71. package/dist/types/hooks/executeHooks.d.ts +5 -1
  72. package/dist/types/hooks/index.d.ts +2 -2
  73. package/dist/types/hooks/types.d.ts +12 -0
  74. package/dist/types/run.d.ts +4 -1
  75. package/dist/types/session/AgentSession.d.ts +1 -0
  76. package/dist/types/tools/ToolNode.d.ts +23 -15
  77. package/dist/types/tools/subagent/SubagentExecutor.d.ts +46 -7
  78. package/dist/types/tools/subagent/SubagentReplay.d.ts +81 -0
  79. package/dist/types/tools/toolOutputReferences.d.ts +12 -0
  80. package/dist/types/types/graph.d.ts +5 -5
  81. package/dist/types/types/hitl.d.ts +15 -0
  82. package/package.json +6 -2
  83. package/src/graphs/Graph.ts +246 -52
  84. package/src/graphs/__tests__/Graph.breakerLifecycle.test.ts +48 -9
  85. package/src/graphs/__tests__/Graph.contextOverflow.test.ts +1 -1
  86. package/src/graphs/__tests__/Graph.subagentResumeState.test.ts +80 -0
  87. package/src/hooks/HookRegistry.ts +151 -1
  88. package/src/hooks/__tests__/HookRegistry.test.ts +96 -0
  89. package/src/hooks/__tests__/executeHooks.test.ts +160 -0
  90. package/src/hooks/executeHooks.ts +133 -15
  91. package/src/hooks/index.ts +3 -1
  92. package/src/hooks/types.ts +16 -0
  93. package/src/messages/format.ts +54 -26
  94. package/src/messages/formatAgentMessages.reducer.test.ts +162 -0
  95. package/src/messages/formatAgentMessages.steer.test.ts +16 -11
  96. package/src/messages/formatAgentMessages.test.ts +7 -2
  97. package/src/run.ts +164 -18
  98. package/src/session/AgentSession.ts +52 -2
  99. package/src/session/__tests__/JsonlSessionStore.test.ts +53 -0
  100. package/src/summarization/__tests__/node.test.ts +32 -14
  101. package/src/summarization/node.ts +15 -11
  102. package/src/tools/ToolNode.ts +631 -157
  103. package/src/tools/__tests__/SubagentExecutor.test.ts +542 -6
  104. package/src/tools/__tests__/SubagentReplay.test.ts +300 -0
  105. package/src/tools/__tests__/ToolNode.breakerSignal.test.ts +208 -2
  106. package/src/tools/__tests__/directToolHITLResumeScope.test.ts +486 -2
  107. package/src/tools/__tests__/hitl.test.ts +234 -5
  108. package/src/tools/__tests__/subagentHooks.test.ts +1040 -62
  109. package/src/tools/__tests__/toolOutputReferences.test.ts +19 -2
  110. package/src/tools/subagent/SubagentExecutor.ts +1369 -131
  111. package/src/tools/subagent/SubagentReplay.ts +575 -0
  112. package/src/tools/toolOutputReferences.ts +40 -1
  113. package/src/types/graph.ts +5 -5
  114. package/src/types/hitl.ts +16 -0
@@ -9,6 +9,7 @@ import type { InjectedMessage } from '@/types/tools';
9
9
  * `@librechat/agents` emission points.
10
10
  */
11
11
  export declare const HOOK_EVENTS: readonly ["RunStart", "UserPromptSubmit", "PreToolUse", "PostToolUse", "PostToolUseFailure", "PostToolBatch", "PreemptBoundary", "PermissionDenied", "SubagentStart", "SubagentStop", "Stop", "StopFailure", "PreCompact", "PostCompact"];
12
+ export declare const TOOL_APPROVAL_EXECUTION_SCOPE_CONFIG_KEY = "__librechat_tool_approval_execution_scope";
12
13
  export type HookEvent = (typeof HOOK_EVENTS)[number];
13
14
  /** Tool-gating decision; executeHooks folds with `deny > ask > allow` precedence. */
14
15
  export type ToolDecision = 'allow' | 'deny' | 'ask';
@@ -331,6 +332,17 @@ export interface PreToolUseHookOutput extends BaseHookOutput {
331
332
  */
332
333
  allowedDecisions?: ReadonlyArray<'approve' | 'reject' | 'edit' | 'respond'>;
333
334
  }
335
+ /** Stable identity for replaying a consumed one-shot tool approval. */
336
+ export interface ToolApprovalReplayKey {
337
+ executionScope: string;
338
+ agentId: string;
339
+ toolUseId: string;
340
+ }
341
+ /** Checkpoint-safe copy of a consumed one-shot approval contribution. */
342
+ export interface ToolApprovalReplaySnapshot {
343
+ key: ToolApprovalReplayKey;
344
+ result: AggregatedHookResult;
345
+ }
334
346
  export interface PostToolUseHookOutput extends BaseHookOutput {
335
347
  /**
336
348
  * Replacement tool output. Flows through the aggregated result so the
@@ -1,5 +1,6 @@
1
+ import { BaseMessage } from '@langchain/core/messages';
1
2
  import { Command } from '@langchain/langgraph';
2
- import type { MessageContentComplex, BaseMessage } from '@langchain/core/messages';
3
+ import type { MessageContentComplex } from '@langchain/core/messages';
3
4
  import type * as t from '@/types';
4
5
  import { MultiAgentGraph } from '@/graphs/MultiAgentGraph';
5
6
  import { StandardGraph } from '@/graphs/Graph';
@@ -103,6 +104,7 @@ export declare class Run<_T extends t.BaseGraphState> {
103
104
  private runPreStreamHooks;
104
105
  static create<T extends t.BaseGraphState>(config: t.RunConfig): Promise<Run<T>>;
105
106
  getRunMessages(): BaseMessage[] | undefined;
107
+ getChildCheckpointThreadIds(): string[];
106
108
  /**
107
109
  * Returns a defensive snapshot of tools discovered by the current run.
108
110
  * Pass an agent id for that context, or omit it for the ordered union across
@@ -220,6 +222,7 @@ export declare class Run<_T extends t.BaseGraphState> {
220
222
  */
221
223
  resume<TResume = t.ToolApprovalDecision[] | t.ToolApprovalDecisionMap>(resumeValue: TResume, callerConfig: t.RunStreamConfig, streamOptions?: t.EventStreamOptions, commandOptions?: Pick<ConstructorParameters<typeof Command>[0], 'update' | 'goto'>): Promise<MessageContentComplex[] | undefined>;
222
224
  private resolveInterruptResumeConfig;
225
+ private restoreInterruptFromCheckpoint;
223
226
  private createSystemCallback;
224
227
  getCallbacks(clientCallbacks: t.ClientCallbacks): t.SystemCallbacks;
225
228
  generateTitle({ provider, inputText, contentParts, titlePrompt, clientOptions, chainOptions, skipLanguage, titleMethod, titlePromptTemplate, }: t.RunTitleOptions): Promise<{
@@ -16,6 +16,7 @@ export declare class AgentSession {
16
16
  getLatestCheckpoint(options?: AgentSessionCheckpointLookupOptions): Promise<AgentSessionCheckpointReference | undefined>;
17
17
  private hasCheckpointState;
18
18
  private recordCheckpoint;
19
+ private recordChildCheckpointThreads;
19
20
  private getCheckpointThreadIds;
20
21
  private resetCheckpointThreads;
21
22
  private runInternal;
@@ -3,6 +3,7 @@ import { END, Command, MessagesAnnotation } from '@langchain/langgraph';
3
3
  import type { RunnableConfig } from '@langchain/core/runnables';
4
4
  import type { BaseMessage } from '@langchain/core/messages';
5
5
  import type { ToolOutputResolveView, ResolvedArgsByCallId } from '@/tools/toolOutputReferences';
6
+ import type { SubagentToolNodeResumeState } from '@/tools/subagent/SubagentReplay';
6
7
  import type * as t from '@/types';
7
8
  import { ToolOutputReferenceRegistry } from '@/tools/toolOutputReferences';
8
9
  import { RunnableCallable } from '@/utils';
@@ -50,6 +51,8 @@ type RunToolBatchContext<T = unknown> = {
50
51
  * contract for hosts relying on it for policy / recovery guidance.
51
52
  */
52
53
  additionalContextsSink?: string[];
54
+ /** Stable identity of the assistant tool-call batch across HITL replay. */
55
+ replayBatchKey?: string;
53
56
  /**
54
57
  * Graph state the ToolNode was invoked with, threaded from `run()`
55
58
  * so `tool.invoke` can forward it as langgraph 1.4's `runtime.state`
@@ -113,6 +116,10 @@ export declare class ToolNode<T = any> extends RunnableCallable<T, T> {
113
116
  * the Run ends.
114
117
  */
115
118
  private directPathTurns;
119
+ /** Terminal results from interrupting siblings that must survive a
120
+ * LangGraph replay of the containing ToolNode. Includes the sidecar data
121
+ * the fresh batch needs for hook-context injection and completion events. */
122
+ private settledInterruptingResults;
116
123
  /** Tool registry for filtering (lazy computation of programmatic maps) */
117
124
  private toolRegistry?;
118
125
  /** Cached programmatic tools (computed once on first PTC call) */
@@ -240,12 +247,11 @@ export declare class ToolNode<T = any> extends RunnableCallable<T, T> {
240
247
  private shouldHandleUnknownHandoffLocally;
241
248
  private getUnknownToolErrorMessage;
242
249
  /**
243
- * Flush the per-Run direct-path turn cache. Called by the Graph at
244
- * end-of-Run via `clearHeavyState`. The map intentionally survives
245
- * `run()` re-entry so an interrupt + resume reuses the original
246
- * slot (Codex P2 #30), but it would otherwise grow linearly with
247
- * tool calls and could collide across Runs if a provider reused
248
- * call IDs (Codex P2 #33). Hosts can also call this directly if
250
+ * Flush per-Run direct replay state. Called by the Graph at end-of-Run via
251
+ * `clearHeavyState`. The state intentionally survives `run()` re-entry so
252
+ * interrupt + resume keeps both original turn slots and terminal sibling
253
+ * outputs, but it would otherwise grow linearly and could collide across
254
+ * Runs if a provider reused call IDs. Hosts can also call this directly if
249
255
  * they reuse a ToolNode across batches outside of a Graph.
250
256
  */
251
257
  clearDirectPathTurns(): void;
@@ -259,6 +265,8 @@ export declare class ToolNode<T = any> extends RunnableCallable<T, T> {
259
265
  * @returns A ReadonlyMap where keys are tool names and values are their usage counts.
260
266
  */
261
267
  getToolUsageCounts(): ReadonlyMap<string, number>;
268
+ createSubagentResumeState(): SubagentToolNodeResumeState;
269
+ restoreSubagentResumeState(state: SubagentToolNodeResumeState): void;
262
270
  private recordToolUsageTurn;
263
271
  private recordEventToolPlanningTurn;
264
272
  /**
@@ -296,9 +304,9 @@ export declare class ToolNode<T = any> extends RunnableCallable<T, T> {
296
304
  * `blockDirectCall`, `respond` returns the host-supplied
297
305
  * `responseText` as a synthetic success ToolMessage,
298
306
  * `edit` re-runs with edited args. LangGraph re-enters
299
- * ToolNode.run from the start on resume; the hook fires
300
- * again and the resume value distinguishes "first ask" from
301
- * "second pass with decision".
307
+ * ToolNode.run from the start on resume. Reusable hooks fire
308
+ * again; a consumed one-shot hook replays its pending approval
309
+ * result. In both cases `interrupt()` consumes the resume value.
302
310
  * • When HITL is off: collapses to a fail-closed deny (matches
303
311
  * the rest of the SDK's HITL-disabled default). One-time
304
312
  * warning logged so hosts notice the gap.
@@ -465,16 +473,16 @@ export declare class ToolNode<T = any> extends RunnableCallable<T, T> {
465
473
  * call throws a `GraphInterrupt`, the `await` below rejects and unwinds the
466
474
  * whole ToolNode *before* any non-interrupting sibling has started — so a
467
475
  * sibling with real side effects (send_email, billing) never executes on
468
- * the first pass. On the resume pass LangGraph re-runs the batch from the
469
- * top; the interrupting tool resolves with the host's answer instead of
470
- * throwing, and the siblings execute for the FIRST time, exactly once.
476
+ * the first pass. Terminal interrupting siblings are cached by call id, so
477
+ * LangGraph replay reuses their complete lifecycle output instead of
478
+ * repeating model calls, hooks, or side effects.
471
479
  *
472
480
  * Without this ordering, a flat `Promise.all` starts every sibling
473
481
  * concurrently, so a non-idempotent sibling can complete its side effect
474
482
  * before the interrupt unwinds and then run a SECOND time on resume — the
475
- * duplicate side effect this method exists to prevent. Interrupting tools
476
- * are expected to be side-effect-free (they only suspend), so running them
477
- * as a group and re-running them on resume is harmless.
483
+ * duplicate side effect this method exists to prevent. A tool that actually
484
+ * suspends re-enters until it reaches a terminal result; siblings that
485
+ * already settled do not re-enter.
478
486
  *
479
487
  * `batchIndices[i]` is `directCalls[i]`'s position within the parent
480
488
  * ToolNode batch (used for `{{tool<i>turn<n>}}` registration); it is
@@ -1,5 +1,8 @@
1
- import type { BaseMessage } from '@langchain/core/messages';
2
- import type { AgentInputs, StandardGraphInput, ResolvedSubagentConfig, SubagentConfig, SubagentUsageSink, TokenCounter } from '@/types';
1
+ import { BaseMessage } from '@langchain/core/messages';
2
+ import type { RunnableConfig } from '@langchain/core/runnables';
3
+ import type { ToolCall } from '@langchain/core/messages/tool';
4
+ import type { AgentInputs, HumanInTheLoopConfig, StandardGraphInput, ResolvedSubagentConfig, SubagentConfig, SubagentUsageSink, TokenCounter } from '@/types';
5
+ import type { SubagentResumeManifest, SettledSubagentToolOutput } from './SubagentReplay';
3
6
  import type { HookRegistry } from '@/hooks';
4
7
  import type { AgentContext } from '@/agents/AgentContext';
5
8
  import type { StandardGraph } from '@/graphs/Graph';
@@ -34,11 +37,12 @@ export type SubagentExecuteParams = {
34
37
  *
35
38
  * Inheritance details (verified empirically against LangGraph):
36
39
  * - host-set keys propagate as-is into the child's tool dispatches;
37
- * - `thread_id` propagates (with `childRunId` as a fallback when
38
- * parent did not supply one) — matches the "subagent is part of
39
- * the same conversation" mental model and aligns with the
40
- * `sessionId: this.parentRunId` convention this executor already
41
- * uses for `SubagentStart` / `SubagentStop` hooks;
40
+ * - with nested HITL enabled, `thread_id` is replaced with a stable
41
+ * child checkpoint id derived from the parent's durable thread id,
42
+ * checkpoint fork, parent agent id, and spawning tool call id so parent
43
+ * and child checkpoints cannot collide, sibling parent forks stay
44
+ * isolated, and reconstruction returns to the same child checkpoint;
45
+ * parent-scoped hook lookup remains keyed by the inherited `run_id`;
42
46
  * - `parent_run_id` propagates when the host put it on parent's
43
47
  * configurable;
44
48
  * - `run_id` is *overwritten by the LangGraph runtime* at child
@@ -95,6 +99,11 @@ export type SubagentExecutorOptions = {
95
99
  * revert to the defaults.
96
100
  */
97
101
  streamLimits?: StandardGraphInput['streamLimits'];
102
+ humanInTheLoop?: HumanInTheLoopConfig;
103
+ /** Shared durable saver used to recover outer tool lifecycle results before
104
+ * parent hooks re-enter after a process rebuild. Narrowed structurally at
105
+ * construction because graph compile options also permit framework flags. */
106
+ checkpointer?: unknown;
98
107
  /** Remaining nesting budget. 0 or negative blocks execution. */
99
108
  maxDepth?: number;
100
109
  /**
@@ -143,9 +152,18 @@ export declare class SubagentExecutor {
143
152
  private readonly langfuse?;
144
153
  private readonly tokenCounter?;
145
154
  private readonly streamLimits?;
155
+ private readonly humanInTheLoop?;
156
+ private readonly checkpointer?;
146
157
  private readonly maxDepth;
147
158
  private readonly createChildGraph;
148
159
  private readonly usageSink?;
160
+ private readonly checkpointThreadIds;
161
+ private readonly startedChildRuns;
162
+ private readonly completedChildRuns;
163
+ private readonly completedChildResults;
164
+ private readonly childExecutionIdentities;
165
+ private readonly activeChildRuns;
166
+ private replayCheckpointWorkflow?;
149
167
  private readonly resolveParentHandlerRegistry?;
150
168
  constructor(options: SubagentExecutorOptions);
151
169
  /** The breaker controller current for this execution — read per spawn
@@ -157,6 +175,27 @@ export declare class SubagentExecutor {
157
175
  private composeChildSignal;
158
176
  /** Snapshot of the parent's registry at the moment a subagent is dispatched. */
159
177
  private getParentHandlerRegistry;
178
+ /**
179
+ * Keeps the original child thread as an immutable resume source once a
180
+ * different parent Run reconstructs it. Each rebuilt parent gets a private
181
+ * checkpoint fork, while the persisted child run ID remains stable for
182
+ * activity and usage correlation across the interrupt boundary.
183
+ */
184
+ private resolveChildExecutionIdentity;
185
+ /** Captures one exact checkpoint head per namespace for a child thread. */
186
+ private getLatestCheckpointSnapshot;
187
+ /** Copies exact checkpoint lineages, including pending task writes. */
188
+ private forkCheckpointSnapshot;
189
+ private createResumeManifest;
190
+ getResumeManifest(parentToolCallIds?: ReadonlySet<string>): Promise<SubagentResumeManifest | undefined>;
191
+ getChildCheckpointThreadIds(): string[];
192
+ resetCheckpointThreadIds(): void;
193
+ private getGraphChildCheckpointThreadIds;
194
+ private clearChildGraph;
195
+ clearHeavyState(): void;
196
+ getSettledToolOutput(call: ToolCall, config: RunnableConfig): Promise<SettledSubagentToolOutput | undefined>;
197
+ persistSettledToolOutput(call: ToolCall, config: RunnableConfig, settled: SettledSubagentToolOutput): Promise<void>;
198
+ private persistChildCheckpointMarker;
160
199
  execute(params: SubagentExecuteParams): Promise<SubagentExecuteResult>;
161
200
  /**
162
201
  * Emits a single {@link GraphEvents.ON_SUBAGENT_UPDATE} envelope through the
@@ -0,0 +1,81 @@
1
+ import type { ToolCall, ToolMessage } from '@langchain/core/messages/tool';
2
+ import type { RunnableConfig } from '@langchain/core/runnables';
3
+ import type { ToolOutputReferenceState } from '@/tools/toolOutputReferences';
4
+ import type { ToolApprovalReplaySnapshot } from '@/hooks';
5
+ import type { ToolSessionContext } from '@/types';
6
+ export declare const SUBAGENT_RESUME_MANIFEST_CONFIG_KEY = "__librechat_subagent_resume_manifest";
7
+ export declare const SUBAGENT_RESUME_ATTEMPT_CONFIG_KEY = "__librechat_subagent_resume_attempt";
8
+ export declare const SUBAGENT_PARENT_BATCH_CONFIG_KEY = "__librechat_subagent_parent_batch";
9
+ export interface SubagentCheckpointReference {
10
+ threadId: string;
11
+ checkpointId: string;
12
+ checkpointNs: string;
13
+ }
14
+ export interface SubagentToolCallStepReference {
15
+ toolCallId: string;
16
+ stepId: string;
17
+ }
18
+ export interface SubagentToolSessionReference {
19
+ toolName: string;
20
+ context: ToolSessionContext;
21
+ }
22
+ export interface SubagentToolNodeResumeState {
23
+ stateKey: string;
24
+ toolUsageCounts: Array<{
25
+ toolName: string;
26
+ count: number;
27
+ }>;
28
+ directPathTurns: Array<{
29
+ toolCallId: string;
30
+ turn: number;
31
+ }>;
32
+ }
33
+ export interface SubagentEagerToolUsageState {
34
+ agentId: string;
35
+ toolUsageCounts: Array<{
36
+ toolName: string;
37
+ count: number;
38
+ }>;
39
+ }
40
+ export interface SubagentGraphResumeState {
41
+ toolCallSteps: SubagentToolCallStepReference[];
42
+ toolSessions: SubagentToolSessionReference[];
43
+ toolNodes: SubagentToolNodeResumeState[];
44
+ eagerToolUsage: SubagentEagerToolUsageState[];
45
+ eagerToolSuppressions: string[];
46
+ toolOutputReferences?: ToolOutputReferenceState;
47
+ }
48
+ /** Private checkpoint payload linking a parent pause to an exact child state. */
49
+ export interface SubagentResumeExecution {
50
+ parentToolCallId: string;
51
+ childRunId: string;
52
+ approvalExecutionScope: string;
53
+ checkpoints: SubagentCheckpointReference[];
54
+ graphState: SubagentGraphResumeState;
55
+ approvalReplays: ToolApprovalReplaySnapshot[];
56
+ descendant?: SubagentResumeManifest;
57
+ }
58
+ /** Private checkpoint payload linking a parent pause to every child state. */
59
+ export interface SubagentResumeManifest {
60
+ version: 1;
61
+ executions: SubagentResumeExecution[];
62
+ }
63
+ export declare function getSubagentResumeManifest(payload: unknown): SubagentResumeManifest | undefined;
64
+ export declare function requireValidSubagentResumeManifest(payload: unknown): SubagentResumeManifest | undefined;
65
+ export declare function attachSubagentResumeManifest(payload: unknown, manifest: SubagentResumeManifest): object;
66
+ export declare function stripSubagentResumeManifest(payload: unknown): unknown;
67
+ export declare const SUBAGENT_REPLAY_CONTROLLER: unique symbol;
68
+ export type SettledSubagentToolOutput = {
69
+ output: ToolMessage;
70
+ additionalContexts: string[];
71
+ resolvedArgs?: Record<string, unknown>;
72
+ referenceContent?: string;
73
+ };
74
+ export interface SubagentReplayController {
75
+ getResumeManifest?(parentToolCallIds?: ReadonlySet<string>): Promise<SubagentResumeManifest | undefined>;
76
+ getSettledOutput(call: ToolCall, config: RunnableConfig): Promise<SettledSubagentToolOutput | undefined>;
77
+ persistSettledOutput(call: ToolCall, config: RunnableConfig, settled: SettledSubagentToolOutput): Promise<void>;
78
+ }
79
+ export type ReplayableSubagentTool = {
80
+ [SUBAGENT_REPLAY_CONTROLLER]?: SubagentReplayController;
81
+ };
@@ -53,6 +53,14 @@ export type ToolOutputReferenceRegistryOptions = {
53
53
  */
54
54
  maxActiveRuns?: number;
55
55
  };
56
+ export interface ToolOutputReferenceState {
57
+ entries: Array<{
58
+ key: string;
59
+ value: string;
60
+ }>;
61
+ turnCounter: number;
62
+ warnedNonStringTools: string[];
63
+ }
56
64
  /**
57
65
  * Result of resolving placeholders in tool args.
58
66
  */
@@ -189,6 +197,10 @@ export declare class ToolOutputReferenceRegistry {
189
197
  * completed.
190
198
  */
191
199
  snapshot(runId: string | undefined): ToolOutputResolveView;
200
+ /** Captures the checkpoint-safe state needed to resume one run bucket. */
201
+ snapshotState(runId: string | undefined): ToolOutputReferenceState;
202
+ /** Restores a checkpointed run bucket under the current resume scope. */
203
+ restoreState(runId: string | undefined, state: ToolOutputReferenceState): void;
192
204
  private resolveAgainst;
193
205
  private transform;
194
206
  private replaceInString;
@@ -524,11 +524,11 @@ export interface AgentInputs {
524
524
  * raise a LangGraph `interrupt()` (e.g. a tool built on `askUserQuestion()`) —
525
525
  * the host-side event handler runs outside the graph task, where `interrupt()`
526
526
  * throws. Do NOT also list these tools in `toolDefinitions` (they would be bound
527
- * twice). NOT inherited by SELF-SPAWNED subagent children (their config is a
528
- * shallow spread of the parent's inputs, and child graphs compile without a
529
- * checkpointer, so an interrupt-capable tool could never pause there)
530
- * `buildChildInputs` scrubs the inherited copy; an EXPLICIT child config that
531
- * lists its own `graphTools` keeps them.
527
+ * twice). NOT inherited by SELF-SPAWNED subagent children: `buildChildInputs`
528
+ * scrubs the shallow-spread parent copy so parent-scoped direct tools are not
529
+ * exposed to a child implicitly. An EXPLICIT child config that lists its own
530
+ * `graphTools` keeps them; with HITL enabled, those tools share the parent's
531
+ * checkpointer and may pause and resume inside the child graph.
532
532
  *
533
533
  * Deliberately `GenericTool[]`, not `GraphTools`: the wider union admits
534
534
  * schema-only shapes (OpenAI `BindToolsInput`, Google tool objects) that
@@ -97,6 +97,17 @@ export type ToolApprovalDecisionMap = Record<string, ToolApprovalDecision>;
97
97
  * below; this type alias is primarily an integration-layer contract.
98
98
  */
99
99
  export type HumanInterruptType = 'tool_approval' | 'ask_user_question';
100
+ /** Identifies an interrupt that originated inside a checkpointed subagent. */
101
+ export interface SubagentInterruptScope {
102
+ /** Child execution run id used by subagent update and usage events. */
103
+ run_id: string;
104
+ /** Child agent id that owns the interrupted tool call. */
105
+ agent_id: string;
106
+ /** Configured subagent type selected by the parent tool call. */
107
+ subagent_type: string;
108
+ /** Parent `subagent` tool call that launched this child. */
109
+ parent_tool_call_id?: string;
110
+ }
100
111
  /**
101
112
  * Structured payload the SDK passes to `interrupt()` when one or more
102
113
  * pending tool calls require host approval. All `ask`-decision tool calls
@@ -110,6 +121,10 @@ export interface ToolApprovalInterruptPayload {
110
121
  type: 'tool_approval';
111
122
  action_requests: ToolApprovalRequest[];
112
123
  review_configs: ToolApprovalReviewConfig[];
124
+ /** Hook-registry session whose policy raised this interrupt. */
125
+ hook_session_id?: string;
126
+ /** Present when the approval request was bridged from a child graph. */
127
+ subagent?: SubagentInterruptScope;
113
128
  }
114
129
  /**
115
130
  * Pre-defined option the user can pick when answering an
package/package.json CHANGED
@@ -1,6 +1,10 @@
1
1
  {
2
2
  "name": "@librechat/agents",
3
- "version": "3.4.0",
3
+ "version": "3.4.1",
4
+ "reova": {
5
+ "enabled": true,
6
+ "endpoint": "https://telemetry.reo.dev/data"
7
+ },
4
8
  "main": "./dist/cjs/main.cjs",
5
9
  "module": "./dist/esm/main.mjs",
6
10
  "types": "./dist/types/index.d.ts",
@@ -248,7 +252,7 @@
248
252
  "nanoid": "^3.3.7",
249
253
  "okapibm25": "^1.4.1",
250
254
  "openai": "^6.46.0",
251
- "reo-census": "^1.2.10",
255
+ "reova": "^0.4.1",
252
256
  "socks-proxy-agent": "^8.0.5",
253
257
  "uuid": "^11.1.1"
254
258
  },