@librechat/agents 3.8.5 → 3.8.6

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 (77) hide show
  1. package/dist/cjs/agents/AgentContext.cjs +1 -1
  2. package/dist/cjs/common/constants.cjs +2 -0
  3. package/dist/cjs/common/constants.cjs.map +1 -1
  4. package/dist/cjs/graphs/Graph.cjs +18 -9
  5. package/dist/cjs/graphs/Graph.cjs.map +1 -1
  6. package/dist/cjs/hitl/approvalReview.cjs +1 -1
  7. package/dist/cjs/hooks/types.cjs.map +1 -1
  8. package/dist/cjs/main.cjs +7 -6
  9. package/dist/cjs/messages/index.cjs +1 -1
  10. package/dist/cjs/messages/prune.cjs +1 -1
  11. package/dist/cjs/run.cjs +7 -3
  12. package/dist/cjs/run.cjs.map +1 -1
  13. package/dist/cjs/stream.cjs +13 -3
  14. package/dist/cjs/stream.cjs.map +1 -1
  15. package/dist/cjs/summarization/node.cjs +3 -1
  16. package/dist/cjs/summarization/node.cjs.map +1 -1
  17. package/dist/cjs/tools/ToolNode.cjs +127 -26
  18. package/dist/cjs/tools/ToolNode.cjs.map +1 -1
  19. package/dist/cjs/tools/local/LocalProgrammaticToolCalling.cjs +1 -0
  20. package/dist/cjs/tools/local/LocalProgrammaticToolCalling.cjs.map +1 -1
  21. package/dist/cjs/tools/subagent/SubagentExecutionRegistry.cjs +0 -1
  22. package/dist/cjs/tools/subagent/SubagentExecutionRegistry.cjs.map +1 -1
  23. package/dist/cjs/tools/subagent/SubagentExecutor.cjs +120 -26
  24. package/dist/cjs/tools/subagent/SubagentExecutor.cjs.map +1 -1
  25. package/dist/cjs/tools/toolBatchReplay.cjs +1 -1
  26. package/dist/cjs/utils/index.cjs +1 -1
  27. package/dist/esm/agents/AgentContext.mjs +1 -1
  28. package/dist/esm/common/constants.mjs +2 -1
  29. package/dist/esm/common/constants.mjs.map +1 -1
  30. package/dist/esm/graphs/Graph.mjs +17 -8
  31. package/dist/esm/graphs/Graph.mjs.map +1 -1
  32. package/dist/esm/hitl/approvalReview.mjs +1 -1
  33. package/dist/esm/hooks/types.mjs.map +1 -1
  34. package/dist/esm/main.mjs +6 -6
  35. package/dist/esm/messages/index.mjs +1 -1
  36. package/dist/esm/messages/prune.mjs +1 -1
  37. package/dist/esm/run.mjs +7 -3
  38. package/dist/esm/run.mjs.map +1 -1
  39. package/dist/esm/stream.mjs +13 -3
  40. package/dist/esm/stream.mjs.map +1 -1
  41. package/dist/esm/summarization/node.mjs +3 -1
  42. package/dist/esm/summarization/node.mjs.map +1 -1
  43. package/dist/esm/tools/ToolNode.mjs +127 -26
  44. package/dist/esm/tools/ToolNode.mjs.map +1 -1
  45. package/dist/esm/tools/local/LocalProgrammaticToolCalling.mjs +1 -0
  46. package/dist/esm/tools/local/LocalProgrammaticToolCalling.mjs.map +1 -1
  47. package/dist/esm/tools/subagent/SubagentExecutionRegistry.mjs +0 -1
  48. package/dist/esm/tools/subagent/SubagentExecutionRegistry.mjs.map +1 -1
  49. package/dist/esm/tools/subagent/SubagentExecutor.mjs +120 -26
  50. package/dist/esm/tools/subagent/SubagentExecutor.mjs.map +1 -1
  51. package/dist/esm/tools/toolBatchReplay.mjs +1 -1
  52. package/dist/esm/utils/index.mjs +1 -1
  53. package/dist/types/common/constants.d.ts +2 -0
  54. package/dist/types/graphs/Graph.d.ts +3 -2
  55. package/dist/types/hooks/types.d.ts +3 -0
  56. package/dist/types/run.d.ts +1 -0
  57. package/dist/types/summarization/node.d.ts +1 -0
  58. package/dist/types/tools/ToolNode.d.ts +4 -2
  59. package/dist/types/tools/subagent/SubagentExecutor.d.ts +7 -4
  60. package/dist/types/types/graph.d.ts +28 -0
  61. package/dist/types/types/run.d.ts +2 -0
  62. package/dist/types/types/tools.d.ts +11 -1
  63. package/package.json +1 -1
  64. package/src/common/constants.ts +3 -0
  65. package/src/graphs/Graph.ts +32 -18
  66. package/src/hooks/types.ts +3 -0
  67. package/src/run.ts +30 -15
  68. package/src/stream.ts +27 -5
  69. package/src/summarization/node.ts +3 -0
  70. package/src/tools/ToolNode.ts +521 -152
  71. package/src/tools/local/LocalProgrammaticToolCalling.ts +1 -0
  72. package/src/tools/subagent/SubagentExecutionRegistry.ts +0 -1
  73. package/src/tools/subagent/SubagentExecutor.ts +214 -55
  74. package/src/types/graph.ts +37 -0
  75. package/src/types/run.ts +2 -0
  76. package/src/types/tools.ts +11 -1
  77. package/src/utils/llmConfig.ts +0 -1
@@ -214,6 +214,7 @@ export async function applyPreToolUseHooksForBridge(
214
214
  runId: hookContext.runId,
215
215
  threadId: hookContext.threadId,
216
216
  agentId: hookContext.agentId,
217
+ executionContext: hookContext.executionContext,
217
218
  executingAgentId: hookContext.executingAgentId,
218
219
  toolName,
219
220
  toolInput,
@@ -783,7 +783,6 @@ export class SubagentExecutionRecord<
783
783
  this.transitionTo('completed', ['active']);
784
784
  this.completedValue = true;
785
785
  this.completedResultValue = result;
786
- this.resolvedConfigValue = undefined;
787
786
  }
788
787
 
789
788
  releaseActiveRun(): TActiveRun | undefined {
@@ -52,6 +52,10 @@ import type {
52
52
  ResolvedSubagentConfig,
53
53
  ResolvedSubagentConfigEntry,
54
54
  SubagentExecutionContext,
55
+ SubagentContextAdapter,
56
+ SubagentContextInput,
57
+ SubagentContextResult,
58
+ PreparedSubagentContext,
55
59
  SubagentTaskConfig,
56
60
  SubagentTaskRuntime,
57
61
  SubagentResolveConfigurable,
@@ -88,11 +92,6 @@ import type {
88
92
  ToolApprovalReplaySnapshot,
89
93
  } from '@/hooks';
90
94
  import type { GraphFactory } from '@/graphs/graphFactory';
91
- import {
92
- rebindToolBatchReplayScope,
93
- rebindToolBatchReplayPayload,
94
- restoreToolReplayConfig,
95
- } from '@/tools/toolBatchReplay';
96
95
  import type { StandardGraph } from '@/graphs/Graph';
97
96
  import {
98
97
  getSubagentApprovalExecutionScope,
@@ -108,6 +107,11 @@ import {
108
107
  SUBAGENT_RESUME_ATTEMPT_CONFIG_KEY,
109
108
  SUBAGENT_RESUME_MANIFEST_CONFIG_KEY,
110
109
  } from './SubagentReplay';
110
+ import {
111
+ rebindToolBatchReplayScope,
112
+ rebindToolBatchReplayPayload,
113
+ restoreToolReplayConfig,
114
+ } from '@/tools/toolBatchReplay';
111
115
  import {
112
116
  executeHooks,
113
117
  HookRegistry,
@@ -138,6 +142,7 @@ import { stableStringify } from '@/tools/eagerEventExecution';
138
142
  import { convertInjectedMessages } from '@/messages/injected';
139
143
  import { resolveClientOptionsModel } from '@/llm/request';
140
144
  import { composeAbortSignals } from '@/utils/misc';
145
+ import { sleep } from '@/utils/run';
141
146
  import { HandlerRegistry } from '@/events';
142
147
 
143
148
  export {
@@ -168,6 +173,31 @@ function seedChildGraphSessions(
168
173
  seedAgentInitialSessions(childGraph.sessions, agents);
169
174
  }
170
175
 
176
+ async function awaitWithAbort<T>(
177
+ promise: Promise<T>,
178
+ signal: AbortSignal
179
+ ): Promise<T> {
180
+ signal.throwIfAborted();
181
+ let onAbort: (() => void) | undefined;
182
+ const aborted = new Promise<never>((_resolve, reject) => {
183
+ onAbort = (): void => {
184
+ reject(
185
+ signal.reason instanceof Error
186
+ ? signal.reason
187
+ : new Error('Detached subagent task cancelled.')
188
+ );
189
+ };
190
+ signal.addEventListener('abort', onAbort, { once: true });
191
+ });
192
+ try {
193
+ return await Promise.race([promise, aborted]);
194
+ } finally {
195
+ if (onAbort != null) {
196
+ signal.removeEventListener('abort', onAbort);
197
+ }
198
+ }
199
+ }
200
+
171
201
  async function dispatchObservationalSubagentUpdate(
172
202
  handler: EventHandler,
173
203
  event: SubagentUpdateEvent
@@ -831,11 +861,11 @@ export type SubagentExecuteParams = {
831
861
  initialMessages?: BaseMessage[];
832
862
  };
833
863
 
834
- export type SubagentExecuteResult = {
835
- content: string;
836
- messages: BaseMessage[];
864
+ export type SubagentExecuteResult = SubagentContextResult & {
837
865
  /** Tagged internal failure; foreground callers retain the legacy content. */
838
866
  error?: string;
867
+ /** Completed child work whose host projection must be retried without re-execution. */
868
+ retryableDelivery?: true;
839
869
  };
840
870
 
841
871
  function createSubagentFailure(
@@ -931,6 +961,7 @@ export type SubagentExecutorOptions = {
931
961
  usageSink?: SubagentUsageSink;
932
962
  /** Host-owned process-local task namespace for detached execution. */
933
963
  taskConfig?: SubagentTaskConfig;
964
+ subagentContext?: SubagentContextAdapter;
934
965
  };
935
966
 
936
967
  type DurableExecutionRecord = SubagentExecutionRecord<
@@ -968,6 +999,7 @@ export class SubagentExecutor {
968
999
  ) => GraphFactory;
969
1000
  private readonly usageSink?: SubagentUsageSink;
970
1001
  private readonly taskConfig?: SubagentTaskConfig;
1002
+ private readonly subagentContext?: SubagentContextAdapter;
971
1003
  private readonly executions: SubagentExecutionRegistry<
972
1004
  SubagentExecuteResult,
973
1005
  ResolvedSubagentConfig,
@@ -1012,6 +1044,7 @@ export class SubagentExecutor {
1012
1044
  options.createDetachedChildGraphFactory;
1013
1045
  this.usageSink = options.usageSink;
1014
1046
  this.taskConfig = options.taskConfig;
1047
+ this.subagentContext = options.subagentContext;
1015
1048
  const rawRegistry = options.parentHandlerRegistry;
1016
1049
  if (typeof rawRegistry === 'function') {
1017
1050
  this.resolveParentHandlerRegistry = rawRegistry;
@@ -1257,6 +1290,7 @@ export class SubagentExecutor {
1257
1290
  langfuse: this.langfuse,
1258
1291
  tokenCounter: this.tokenCounter,
1259
1292
  usageSink: this.usageSink,
1293
+ subagentContext: this.subagentContext,
1260
1294
  streamLimits: this.streamLimits,
1261
1295
  maxDepth: this.maxDepth,
1262
1296
  createChildGraph:
@@ -1268,21 +1302,38 @@ export class SubagentExecutor {
1268
1302
  detachedGraphFactory ?? this.createChildGraphByKind,
1269
1303
  });
1270
1304
  try {
1271
- const result = await detached.execute({
1272
- ...params,
1273
- signal: undefined,
1274
- breaker: undefined,
1275
- hookSessionId: taskHookSessionId,
1276
- taskRuntime: runtime,
1277
- parentConfigurable: {
1278
- ...params.parentConfigurable,
1279
- run_id: taskHookSessionId,
1280
- },
1281
- });
1282
- if (runtime.signal.aborted) {
1283
- throw runtime.signal.reason instanceof Error
1284
- ? runtime.signal.reason
1285
- : new Error('Detached subagent task cancelled.');
1305
+ const executeAttempt = async (): Promise<SubagentExecuteResult> => {
1306
+ const result = await detached.execute({
1307
+ ...params,
1308
+ signal: undefined,
1309
+ breaker: undefined,
1310
+ hookSessionId: taskHookSessionId,
1311
+ taskRuntime: runtime,
1312
+ parentConfigurable: {
1313
+ ...params.parentConfigurable,
1314
+ run_id: taskHookSessionId,
1315
+ },
1316
+ });
1317
+ if (runtime.signal.aborted) {
1318
+ throw runtime.signal.reason instanceof Error
1319
+ ? runtime.signal.reason
1320
+ : new Error('Detached subagent task cancelled.');
1321
+ }
1322
+ return result;
1323
+ };
1324
+ let deliveryAttempts = 1;
1325
+ let result = await executeAttempt();
1326
+ while (
1327
+ result.retryableDelivery === true &&
1328
+ deliveryAttempts < 3
1329
+ ) {
1330
+ if (deliveryAttempts > 1) {
1331
+ await sleep(
1332
+ Math.min(100 * 2 ** Math.min(deliveryAttempts - 2, 6), 5_000)
1333
+ );
1334
+ }
1335
+ deliveryAttempts += 1;
1336
+ result = await executeAttempt();
1286
1337
  }
1287
1338
  if (result.error != null) {
1288
1339
  throw new Error(result.error);
@@ -1454,7 +1505,10 @@ export class SubagentExecutor {
1454
1505
  resumeExecution.checkpoints,
1455
1506
  branchChildThreadId,
1456
1507
  lease,
1457
- { source: resumeExecution.approvalExecutionScope, target: approvalExecutionScope }
1508
+ {
1509
+ source: resumeExecution.approvalExecutionScope,
1510
+ target: approvalExecutionScope,
1511
+ }
1458
1512
  );
1459
1513
  let previousApprovals: ToolApprovalReplaySnapshot[] = [];
1460
1514
  let approvalsMutated = false;
@@ -1765,14 +1819,20 @@ export class SubagentExecutor {
1765
1819
  }
1766
1820
  /** Repeated unconsumed forks must persist their current owner. */
1767
1821
  const reboundValue =
1768
- channel === INTERRUPT && approvalScope != null &&
1769
- value != null && typeof value === 'object' && 'value' in value
1770
- ? { ...value, value: rebindToolBatchReplayPayload(
1771
- value.value,
1772
- approvalScope.source,
1773
- approvalScope.target,
1774
- targetThreadId
1775
- ) }
1822
+ channel === INTERRUPT &&
1823
+ approvalScope != null &&
1824
+ value != null &&
1825
+ typeof value === 'object' &&
1826
+ 'value' in value
1827
+ ? {
1828
+ ...value,
1829
+ value: rebindToolBatchReplayPayload(
1830
+ value.value,
1831
+ approvalScope.source,
1832
+ approvalScope.target,
1833
+ targetThreadId
1834
+ ),
1835
+ }
1776
1836
  : value;
1777
1837
  writes.push([channel, reboundValue]);
1778
1838
  }
@@ -2117,6 +2177,9 @@ export class SubagentExecutor {
2117
2177
  ) {
2118
2178
  return;
2119
2179
  }
2180
+ if (settled.output.status === 'error' && config.signal?.aborted === true) {
2181
+ return;
2182
+ }
2120
2183
  const parentConfigurable = config.configurable as
2121
2184
  | Record<string, unknown>
2122
2185
  | undefined;
@@ -2126,6 +2189,9 @@ export class SubagentExecutor {
2126
2189
  parentToolCallId,
2127
2190
  parentConfigurable,
2128
2191
  });
2192
+ if (execution.completedResult != null && settled.output.status === 'error') {
2193
+ return;
2194
+ }
2129
2195
  const { resumeExecution } = execution;
2130
2196
  const resolvedSubagentType = getSubagentTypeFromArgs(settled.resolvedArgs);
2131
2197
  const resolvedDescription = getSubagentDescriptionFromArgs(
@@ -2386,11 +2452,6 @@ export class SubagentExecutor {
2386
2452
  if (!bound) {
2387
2453
  return createSubagentFailure(SUBAGENT_CONFIG_CHANGED_MESSAGE);
2388
2454
  }
2389
- const completedChildResult = execution.completedResult;
2390
- if (completedChildResult != null) {
2391
- return completedChildResult;
2392
- }
2393
-
2394
2455
  let config: ResolvedSubagentConfigEntry;
2395
2456
  try {
2396
2457
  config = await this.resolveExecutionConfig(executableConfig, execution, {
@@ -2450,6 +2511,63 @@ export class SubagentExecutor {
2450
2511
  },
2451
2512
  ],
2452
2513
  };
2514
+ const hostContextInput: SubagentContextInput = {
2515
+ executionContext: childExecutionContext,
2516
+ parentThreadId: threadId,
2517
+ memberAgentIds: childPlan.agents.map((agent) => agent.agentId),
2518
+ signal: childSignal,
2519
+ resumed: execution.started || resumeExecution != null,
2520
+ };
2521
+ let preparedContext: PreparedSubagentContext | undefined;
2522
+ try {
2523
+ preparedContext =
2524
+ this.subagentContext?.prepare == null
2525
+ ? undefined
2526
+ : await awaitWithAbort(
2527
+ Promise.resolve(this.subagentContext.prepare(hostContextInput)),
2528
+ childSignal
2529
+ );
2530
+ execution.assertUsable(childSignal);
2531
+ for (const [agentId, sessions] of Object.entries(
2532
+ preparedContext?.agentSessions ?? {}
2533
+ )) {
2534
+ const member = childPlan.memberInputs.get(agentId);
2535
+ if (member == null) throw new Error('Unknown subagent context member.');
2536
+ const activeMember =
2537
+ execution.activeRun?.graph.agentContexts.get(agentId);
2538
+ if (
2539
+ activeMember != null &&
2540
+ activeMember.codeSessionKey !== sessions.codeSessionKey
2541
+ ) {
2542
+ throw new Error(
2543
+ 'Subagent session partition changed during execution.'
2544
+ );
2545
+ }
2546
+ member.codeSessionKey = sessions.codeSessionKey;
2547
+ member.initialSessions = sessions.initialSessions;
2548
+ }
2549
+ } catch (error) {
2550
+ if (childSignal.aborted) {
2551
+ throw childSignal.reason instanceof Error
2552
+ ? childSignal.reason
2553
+ : error;
2554
+ }
2555
+ if (execution.completedResult != null) {
2556
+ return {
2557
+ ...createSubagentFailure(
2558
+ 'Subagent context is unavailable or access was denied.'
2559
+ ),
2560
+ retryableDelivery: true,
2561
+ };
2562
+ }
2563
+ return createSubagentFailure(
2564
+ 'Subagent context is unavailable or access was denied.'
2565
+ );
2566
+ }
2567
+ const completedChildResult = execution.completedResult;
2568
+ if (completedChildResult != null) {
2569
+ return this.completeHostContext(hostContextInput, completedChildResult);
2570
+ }
2453
2571
  const maxTurns = config.maxTurns ?? DEFAULT_SUBAGENT_MAX_TURNS;
2454
2572
  const memberRecursionLimit = maxTurns * SUBAGENT_RECURSION_MULTIPLIER;
2455
2573
  const recursionLimit =
@@ -2476,6 +2594,7 @@ export class SubagentExecutor {
2476
2594
  streamLimits: this.streamLimits,
2477
2595
  subagentScope: true,
2478
2596
  subagentExecutionContext: childExecutionContext,
2597
+ subagentContext: this.subagentContext,
2479
2598
  ...(resumeExecution?.graphState.fadingTier == null
2480
2599
  ? {}
2481
2600
  : { fadingTier: resumeExecution.graphState.fadingTier }),
@@ -2522,9 +2641,7 @@ export class SubagentExecutor {
2522
2641
  if (params.taskRuntime != null) {
2523
2642
  childGraph.hookRegistry = this.hookRegistry;
2524
2643
  }
2525
- if (cachedChildRun == null) {
2526
- seedChildGraphSessions(childGraph, childPlan.agents);
2527
- }
2644
+ seedChildGraphSessions(childGraph, childPlan.agents);
2528
2645
  let forwarding: ForwarderCallback | undefined;
2529
2646
  if (forwardingEnabled) {
2530
2647
  forwarding = this.createForwarderCallback({
@@ -2626,6 +2743,8 @@ export class SubagentExecutor {
2626
2743
  }
2627
2744
  const childConfigurable: Record<string, unknown> = {
2628
2745
  ...inheritedConfigurable,
2746
+ ...sanitizePreparedConfigurable(preparedContext?.configurable),
2747
+ executionContext: childExecutionContext,
2629
2748
  };
2630
2749
  if (this.humanInTheLoop?.enabled === true) {
2631
2750
  childConfigurable[TOOL_APPROVAL_EXECUTION_SCOPE_CONFIG_KEY] =
@@ -2666,16 +2785,18 @@ export class SubagentExecutor {
2666
2785
  }
2667
2786
  const persistedInterrupts = getPersistedInterrupts(persistedState);
2668
2787
  if (persistedInterrupts.length > 0) {
2669
- activeChildRun.pendingInterrupts = resumeExecution == null ? persistedInterrupts :
2670
- persistedInterrupts.map((pending) => ({
2671
- ...pending,
2672
- value: rebindToolBatchReplayPayload(
2673
- pending.value,
2674
- resumeExecution.approvalExecutionScope,
2675
- approvalExecutionScope,
2676
- childThreadId
2677
- ),
2678
- }));
2788
+ activeChildRun.pendingInterrupts =
2789
+ resumeExecution == null
2790
+ ? persistedInterrupts
2791
+ : persistedInterrupts.map((pending) => ({
2792
+ ...pending,
2793
+ value: rebindToolBatchReplayPayload(
2794
+ pending.value,
2795
+ resumeExecution.approvalExecutionScope,
2796
+ approvalExecutionScope,
2797
+ childThreadId
2798
+ ),
2799
+ }));
2679
2800
  execution.markStarted();
2680
2801
  childAlreadyStarted = true;
2681
2802
  } else if (persistedState.next.length > 0) {
@@ -2710,9 +2831,12 @@ export class SubagentExecutor {
2710
2831
  }
2711
2832
  let childInput: BaseGraphState | Command | null;
2712
2833
  if (childResumeMap != null) {
2713
- const pending = activeChildRun.pendingInterrupts.find((entry) =>
2714
- entry.id != null && Object.prototype.hasOwnProperty.call(childResumeMap, entry.id)
2715
- ) ?? activeChildRun.pendingInterrupts[0];
2834
+ const pending =
2835
+ activeChildRun.pendingInterrupts.find(
2836
+ (entry) =>
2837
+ entry.id != null &&
2838
+ Object.prototype.hasOwnProperty.call(childResumeMap, entry.id)
2839
+ ) ?? activeChildRun.pendingInterrupts[0];
2716
2840
  restoreToolReplayConfig(childConfigurable, pending.id, pending.value);
2717
2841
  rebindToolBatchReplayScope(
2718
2842
  childConfigurable,
@@ -2734,6 +2858,7 @@ export class SubagentExecutor {
2734
2858
  [SUBAGENT_RUN_ID_KEY]: childRunId,
2735
2859
  },
2736
2860
  }),
2861
+ ...(preparedContext?.messages ?? []),
2737
2862
  ],
2738
2863
  };
2739
2864
  }
@@ -2749,6 +2874,7 @@ export class SubagentExecutor {
2749
2874
  registry: this.hookRegistry,
2750
2875
  input: {
2751
2876
  hook_event_name: 'SubagentStart',
2877
+ executionContext: childExecutionContext,
2752
2878
  runId: currentHookSessionId,
2753
2879
  threadId,
2754
2880
  parentAgentId: this.parentAgentId,
@@ -2955,6 +3081,7 @@ export class SubagentExecutor {
2955
3081
  registry: this.hookRegistry,
2956
3082
  input: {
2957
3083
  hook_event_name: 'SubagentStop',
3084
+ executionContext: childExecutionContext,
2958
3085
  runId: currentHookSessionId,
2959
3086
  threadId,
2960
3087
  agentId: childAgentId,
@@ -2997,12 +3124,33 @@ export class SubagentExecutor {
2997
3124
 
2998
3125
  this.clearChildGraph(childGraph);
2999
3126
 
3000
- const completedResult = {
3127
+ const completedResult: SubagentExecuteResult = {
3001
3128
  content: filteredContent,
3002
3129
  messages: result.messages,
3003
3130
  };
3004
3131
  execution.markCompleted(completedResult);
3005
- return completedResult;
3132
+ return this.completeHostContext(hostContextInput, completedResult);
3133
+ }
3134
+
3135
+ private async completeHostContext(
3136
+ input: SubagentContextInput,
3137
+ result: SubagentExecuteResult
3138
+ ): Promise<SubagentExecuteResult> {
3139
+ if (this.subagentContext?.complete == null) return result;
3140
+ try {
3141
+ input.signal.throwIfAborted();
3142
+ const completed = await awaitWithAbort(
3143
+ this.subagentContext.complete(input, result),
3144
+ input.signal
3145
+ );
3146
+ input.signal.throwIfAborted();
3147
+ return { ...result, content: completed.content };
3148
+ } catch {
3149
+ return {
3150
+ ...createSubagentFailure('Subagent result delivery failed.'),
3151
+ retryableDelivery: true,
3152
+ };
3153
+ }
3006
3154
  }
3007
3155
 
3008
3156
  /**
@@ -3535,6 +3683,17 @@ function sanitizeChildConfigurable(
3535
3683
  );
3536
3684
  }
3537
3685
 
3686
+ function sanitizePreparedConfigurable(
3687
+ configurable: RunnableConfig['configurable']
3688
+ ): Record<string, unknown> {
3689
+ const prepared = sanitizeChildConfigurable(configurable);
3690
+ delete prepared.run_id;
3691
+ delete prepared.parent_run_id;
3692
+ delete prepared.thread_id;
3693
+ delete prepared.executionContext;
3694
+ return prepared;
3695
+ }
3696
+
3538
3697
  function isLangGraphRuntimeConfigKey(key: string): boolean {
3539
3698
  return (
3540
3699
  key.startsWith(LANGGRAPH_RUNTIME_CONFIG_PREFIX) ||
@@ -395,6 +395,8 @@ export type StandardGraphInput = {
395
395
  * do not inherit it, keeping background nesting disabled for the MVP.
396
396
  */
397
397
  subagentTasks?: SubagentTaskConfig;
398
+ /** Host-owned context and result projection for each isolated child execution. */
399
+ subagentContext?: SubagentContextAdapter;
398
400
  /**
399
401
  * True when this graph IS a subagent child run (set by `SubagentExecutor`
400
402
  * when it constructs the child graph). Drives the hook-input `agentId`
@@ -669,6 +671,41 @@ export interface SubagentExecutionContext {
669
671
  readonly ancestry: readonly SubagentAncestryEntry[];
670
672
  }
671
673
 
674
+ /** Trusted identity supplied before a child can access host-owned resources. */
675
+ export interface SubagentContextInput {
676
+ executionContext: SubagentExecutionContext;
677
+ parentThreadId?: string;
678
+ memberAgentIds: readonly string[];
679
+ signal: AbortSignal;
680
+ resumed: boolean;
681
+ }
682
+
683
+ export interface PreparedSubagentContext {
684
+ /** Added to the initial child input; checkpoint recovery does not add them again. */
685
+ messages?: BaseMessage[];
686
+ /** Host configuration; SDK execution and checkpoint identities remain authoritative. */
687
+ configurable?: RunnableConfig['configurable'];
688
+ /** Each entry replaces that member's inherited code partition and initial sessions. */
689
+ agentSessions?: Readonly<
690
+ Record<string, Pick<AgentInputs, 'codeSessionKey' | 'initialSessions'>>
691
+ >;
692
+ }
693
+
694
+ export interface SubagentContextResult {
695
+ content: string;
696
+ messages: BaseMessage[];
697
+ }
698
+
699
+ export interface SubagentContextAdapter {
700
+ /** Reauthorizes reconstructed executions; durable work must be idempotent by child run ID. */
701
+ prepare(input: SubagentContextInput): Promise<PreparedSubagentContext>;
702
+ /** Projects durable references into the result returned to the parent. */
703
+ complete?(
704
+ input: SubagentContextInput,
705
+ result: SubagentContextResult
706
+ ): Promise<Pick<SubagentContextResult, 'content'>>;
707
+ }
708
+
672
709
  /**
673
710
  * Wrapper event emitted when a subagent's child graph dispatches activity.
674
711
  * Lets hosts show subagent progress in a UI surface separate from the parent
package/src/types/run.ts CHANGED
@@ -291,6 +291,8 @@ export type RunConfig = {
291
291
  * Omit to preserve foreground-only subagent behavior.
292
292
  */
293
293
  subagentTasks?: SubagentTaskConfig;
294
+ /** Authorizes and supplies host-owned context to isolated child runs. */
295
+ subagentContext?: g.SubagentContextAdapter;
294
296
  /**
295
297
  * Pre-constructed hook registry for this run. Hooks fire at lifecycle
296
298
  * points in `processStream` (RunStart, UserPromptSubmit, Stop,
@@ -11,10 +11,10 @@ import type {
11
11
  ToolErrorData,
12
12
  } from './stream';
13
13
  import type { ToolOutputReferenceRegistry } from '@/tools/toolOutputReferences';
14
+ import type { LangfuseConfig, SubagentExecutionContext } from './graph';
14
15
  import type { PreparedSubagents } from '@/tools/preparedSubagents';
15
16
  import type { RunBreakerScope } from '@/llm/streamLimits';
16
17
  import type { HumanInTheLoopConfig } from './hitl';
17
- import type { LangfuseConfig } from './graph';
18
18
  import type { HookRegistry } from '@/hooks';
19
19
 
20
20
  /** Replacement type for `import type { ToolCall } from '@langchain/core/messages/tool'` in order to have stringified args typed */
@@ -75,6 +75,7 @@ export type EagerEventToolExecution = {
75
75
  toolName: string;
76
76
  args: Record<string, unknown>;
77
77
  request: ToolCallRequest;
78
+ codeSessionBaselineByName?: ReadonlyMap<string, string>;
78
79
  promise: Promise<EagerEventToolExecutionOutcome>;
79
80
  /**
80
81
  * True when the streaming eager path already emitted the user-visible
@@ -156,6 +157,8 @@ export type ToolNodeOptions = {
156
157
  /** ID of the agent that owns this tool node, surfaced to hooks as `executingAgentId`
157
158
  * so a batch can be attributed to a specific agent even where `agentId` is undefined. */
158
159
  executingAgentId?: string;
160
+ /** SDK-owned identity of the child execution, independent of reusable agent IDs. */
161
+ executionContext?: SubagentExecutionContext;
159
162
  /** Name of the agent that owns this tool node, used for active Langfuse attribution. */
160
163
  executingAgentName?: string;
161
164
  /** Root graph-agent identity retained alongside the currently executing tool owner. */
@@ -573,6 +576,10 @@ export type ToolCallRequest = {
573
576
  session_id: string;
574
577
  files?: CodeEnvFile[];
575
578
  };
579
+ /** Request-time execution session used to detect host session refreshes. */
580
+ codeSessionBaselineId?: string;
581
+ /** Preserve request-provisioned code inputs after an eager identity mismatch. */
582
+ retainCodeSessionInputs?: true;
576
583
  /**
577
584
  * Stable runtime session hint for stateful sandbox sessions. Orthogonal to
578
585
  * `codeSessionContext` (which threads the transient exec-session for file
@@ -596,6 +603,8 @@ export type CallerCapabilityProjectionSnapshot = {
596
603
 
597
604
  /** Batch request containing ALL tool calls for a graph step */
598
605
  export type ToolExecuteBatchRequest = {
606
+ /** SDK-owned child lineage. Unset for tools in the root graph. */
607
+ executionContext?: SubagentExecutionContext;
599
608
  /** All tool calls from the AIMessage */
600
609
  toolCalls: ToolCallRequest[];
601
610
  /** User ID for context */
@@ -1227,6 +1236,7 @@ export type ProgrammaticHookContext = {
1227
1236
  runId: string;
1228
1237
  threadId?: string;
1229
1238
  agentId?: string;
1239
+ executionContext?: SubagentExecutionContext;
1230
1240
  executingAgentId?: string;
1231
1241
  };
1232
1242
 
@@ -71,7 +71,6 @@ export const llmConfigs: Record<string, t.BuiltInLLMConfig | undefined> = {
71
71
  } as or.ChatOpenRouterCallOptions & t.BuiltInLLMConfig,
72
72
  [Providers.AZURE]: {
73
73
  provider: Providers.AZURE,
74
- temperature: 0.7,
75
74
  streaming: true,
76
75
  streamUsage: true,
77
76
  azureOpenAIApiKey: process.env.AZURE_OPENAI_API_KEY,