@librechat/agents 3.8.2 → 3.8.4

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 (45) hide show
  1. package/dist/cjs/hooks/index.cjs +2 -0
  2. package/dist/cjs/hooks/index.cjs.map +1 -1
  3. package/dist/cjs/hooks/types.cjs.map +1 -1
  4. package/dist/cjs/main.cjs +2 -0
  5. package/dist/cjs/tools/BashExecutor.cjs +2 -3
  6. package/dist/cjs/tools/BashExecutor.cjs.map +1 -1
  7. package/dist/cjs/tools/BashProgrammaticToolCalling.cjs +3 -2
  8. package/dist/cjs/tools/BashProgrammaticToolCalling.cjs.map +1 -1
  9. package/dist/cjs/tools/CodeExecutor.cjs +63 -11
  10. package/dist/cjs/tools/CodeExecutor.cjs.map +1 -1
  11. package/dist/cjs/tools/ProgrammaticToolCalling.cjs +8 -7
  12. package/dist/cjs/tools/ProgrammaticToolCalling.cjs.map +1 -1
  13. package/dist/cjs/tools/ToolNode.cjs +18 -3
  14. package/dist/cjs/tools/ToolNode.cjs.map +1 -1
  15. package/dist/cjs/tools/toolBatchReplay.cjs +15 -4
  16. package/dist/cjs/tools/toolBatchReplay.cjs.map +1 -1
  17. package/dist/esm/hooks/index.mjs +2 -1
  18. package/dist/esm/hooks/index.mjs.map +1 -1
  19. package/dist/esm/hooks/types.mjs.map +1 -1
  20. package/dist/esm/main.mjs +3 -3
  21. package/dist/esm/tools/BashExecutor.mjs +2 -3
  22. package/dist/esm/tools/BashExecutor.mjs.map +1 -1
  23. package/dist/esm/tools/BashProgrammaticToolCalling.mjs +3 -2
  24. package/dist/esm/tools/BashProgrammaticToolCalling.mjs.map +1 -1
  25. package/dist/esm/tools/CodeExecutor.mjs +63 -12
  26. package/dist/esm/tools/CodeExecutor.mjs.map +1 -1
  27. package/dist/esm/tools/ProgrammaticToolCalling.mjs +8 -7
  28. package/dist/esm/tools/ProgrammaticToolCalling.mjs.map +1 -1
  29. package/dist/esm/tools/ToolNode.mjs +19 -4
  30. package/dist/esm/tools/ToolNode.mjs.map +1 -1
  31. package/dist/esm/tools/toolBatchReplay.mjs +14 -4
  32. package/dist/esm/tools/toolBatchReplay.mjs.map +1 -1
  33. package/dist/types/hooks/index.d.ts +2 -0
  34. package/dist/types/hooks/types.d.ts +6 -0
  35. package/dist/types/tools/CodeExecutor.d.ts +2 -0
  36. package/dist/types/tools/toolBatchReplay.d.ts +11 -2
  37. package/package.json +1 -1
  38. package/src/hooks/index.ts +3 -0
  39. package/src/hooks/types.ts +6 -0
  40. package/src/tools/BashExecutor.ts +2 -2
  41. package/src/tools/BashProgrammaticToolCalling.ts +5 -3
  42. package/src/tools/CodeExecutor.ts +111 -17
  43. package/src/tools/ProgrammaticToolCalling.ts +10 -8
  44. package/src/tools/ToolNode.ts +27 -9
  45. package/src/tools/toolBatchReplay.ts +33 -3
@@ -20,9 +20,19 @@ function restoreToolReplayConfig(configurable, interruptId, payload) {
20
20
  const publicPayload = getPublicToolInterruptPayload(payload);
21
21
  const review = createToolApprovalReviewEvidence(interruptId, publicPayload, state?.approvalOwner);
22
22
  if (isToolApprovalInterrupt(publicPayload) && (review == null || state != null && state.approvalOwner == null)) throw new Error("Invalid tool approval checkpoint");
23
- if (state != null) configurable[TOOL_BATCH_REPLAY_KEY] = state;
23
+ if (state != null) configurable[TOOL_BATCH_REPLAY_KEY] = {
24
+ ...state,
25
+ interruptId
26
+ };
24
27
  if (review != null) configurable[TOOL_APPROVAL_REVIEW_CONFIG_KEY] = review;
25
28
  }
29
+ function isToolReplayResume(config, interruptId, isChildReplay) {
30
+ const resumeMap = config.configurable?.__pregel_resume_map;
31
+ if (interruptId != null && resumeMap != null && !Object.prototype.hasOwnProperty.call(resumeMap, interruptId)) return false;
32
+ if (isChildReplay && resumeMap != null) return true;
33
+ const scratchpad = config.configurable?.__pregel_scratchpad;
34
+ return scratchpad == null || (scratchpad.resume?.length ?? 0) > 0 || scratchpad.nullResume !== void 0;
35
+ }
26
36
  function getToolBatchReplayOwner(config, agentId) {
27
37
  return JSON.stringify([
28
38
  getToolBatchReplayScope(config),
@@ -34,7 +44,7 @@ function getToolBatchReplayScope(config) {
34
44
  const scope = config.configurable?.["__librechat_tool_approval_execution_scope"] ?? config.configurable?.thread_id;
35
45
  return typeof scope === "string" && scope.length > 0 ? scope : void 0;
36
46
  }
37
- function isChildToolApprovalOwner(config, owner, trustedParentCallIds) {
47
+ function isChildToolReplayOwner(config, owner, trustedParentCallIds) {
38
48
  const manifest = requireValidSubagentResumeManifest(config.configurable);
39
49
  if (manifest == null || trustedParentCallIds.size === 0) return false;
40
50
  const identity = JSON.parse(owner);
@@ -88,7 +98,7 @@ function getToolBatchReplayState(payload) {
88
98
  const candidate = value[TOOL_BATCH_REPLAY_KEY];
89
99
  if (candidate === void 0) return;
90
100
  const state = candidate;
91
- if (state?.version !== 1 || state.wrappedPayload != null && typeof state.wrappedPayload !== "boolean" || state.approvalOwner != null && typeof state.approvalOwner !== "string" || !Array.isArray(state.records) || state.records.some((value) => {
101
+ if (state?.version !== 1 || state.wrappedPayload != null && typeof state.wrappedPayload !== "boolean" || state.approvalOwner != null && typeof state.approvalOwner !== "string" || state.interruptId != null && typeof state.interruptId !== "string" || !Array.isArray(state.records) || state.records.some((value) => {
92
102
  if (value == null || typeof value !== "object") return true;
93
103
  const record = value;
94
104
  return typeof record.owner !== "string" || typeof record.batch !== "string" || typeof record.encoding !== "string" || typeof record.data !== "string" || record.turnState != null && !isToolNodeResumeState(record.turnState) || record.referenceState != null && !isToolOutputReferenceState(record.referenceState);
@@ -171,6 +181,6 @@ async function restoreToolBatchReplayState(config, owner) {
171
181
  }));
172
182
  }
173
183
  //#endregion
174
- export { TOOL_BATCH_REPLAY_KEY, attachToolBatchReplayState, getPublicToolInterruptPayload, getToolBatchReplayOwner, getToolBatchReplayScope, getToolBatchReplayState, isChildToolApprovalOwner, rebindToolBatchReplayPayload, rebindToolBatchReplayScope, restoreToolBatchReplayState, restoreToolReplayConfig, stripToolBatchReplayState };
184
+ export { TOOL_BATCH_REPLAY_KEY, attachToolBatchReplayState, getPublicToolInterruptPayload, getToolBatchReplayOwner, getToolBatchReplayScope, getToolBatchReplayState, isChildToolReplayOwner, isToolReplayResume, rebindToolBatchReplayPayload, rebindToolBatchReplayScope, restoreToolBatchReplayState, restoreToolReplayConfig, stripToolBatchReplayState };
175
185
 
176
186
  //# sourceMappingURL=toolBatchReplay.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"toolBatchReplay.mjs","names":[],"sources":["../../../src/tools/toolBatchReplay.ts"],"mappings":"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"}
1
+ {"version":3,"file":"toolBatchReplay.mjs","names":[],"sources":["../../../src/tools/toolBatchReplay.ts"],"mappings":"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"}
@@ -47,3 +47,5 @@ export type { OutsideAccessPolicy, WorkspacePolicyConfig, PathExtractor, } from
47
47
  export { HOOK_EVENTS, TOOL_APPROVAL_EXECUTION_SCOPE_CONFIG_KEY } from './types';
48
48
  export type { HookEvent, HookInput, HookOutput, HookCallback, HookMatcher, HooksByEvent, HookInputByEvent, HookOutputByEvent, BaseHookInput, BaseHookOutput, ToolDecision, StopDecision, ToolApprovalReplayKey, ToolApprovalReplaySnapshot, AggregatedHookResult, RunStartHookInput, UserPromptSubmitHookInput, PreToolUseHookInput, PostToolUseHookInput, PostToolUseFailureHookInput, PostToolBatchHookInput, PostToolBatchEntry, PreemptBoundaryHookInput, PermissionDeniedHookInput, SubagentStartHookInput, SubagentStopHookInput, StopHookInput, StopFinalizeHookInput, StopFailureHookInput, PreCompactHookInput, PostCompactHookInput, RunStartHookOutput, UserPromptSubmitHookOutput, PreToolUseHookOutput, PostToolUseHookOutput, PostToolUseFailureHookOutput, PostToolBatchHookOutput, PreemptBoundaryHookOutput, PermissionDeniedHookOutput, SubagentStartHookOutput, SubagentStopHookOutput, StopHookOutput, StopFinalizeHookOutput, StopFailureHookOutput, PreCompactHookOutput, PostCompactHookOutput, } from './types';
49
49
  export type { ExecuteHooksOptions } from './executeHooks';
50
+ /** Hosts may opt into stable generation scopes across every ToolNode interrupt type. */
51
+ export declare const TOOL_APPROVAL_EXECUTION_SCOPE_CAPABLE = true;
@@ -9,6 +9,12 @@ 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", "StopFinalize", "StopFailure", "PreCompact", "PostCompact"];
12
+ /**
13
+ * Host-owned generation identity, stable across every resume and rebuilt Run.
14
+ * A new generation must use a new scope, even when it reuses a thread or response
15
+ * id. Explicit scoping keeps LangGraph task namespaces out of approval owners;
16
+ * the executing agent id remains part of the owner and cannot change on resume.
17
+ */
12
18
  export declare const TOOL_APPROVAL_EXECUTION_SCOPE_CONFIG_KEY = "__librechat_tool_approval_execution_scope";
13
19
  export type HookEvent = (typeof HOOK_EVENTS)[number];
14
20
  /** Tool-gating decision; executeHooks folds with `deny > ask > allow` precedence. */
@@ -50,7 +50,9 @@ export declare const CODE_API_AUTHORIZATION_ERROR_MESSAGE = "Code execution is n
50
50
  export declare const CODE_API_EXECUTION_FAILED_ERROR_MESSAGE = "Code execution failed.";
51
51
  export declare const CODE_API_INVALID_REQUEST_ERROR_MESSAGE = "The code execution request was rejected. Please check the tool input and try again.";
52
52
  export declare const CODE_API_RATE_LIMITED_ERROR_MESSAGE = "Code execution is temporarily rate-limited. Please retry shortly.";
53
+ export declare const CODE_API_CAPABILITY_ERROR_MESSAGE = "Code execution is not supported by the selected environment.";
53
54
  export declare class CodeApiRequestError extends Error {
55
+ readonly retryable: boolean;
54
56
  constructor(message?: string);
55
57
  }
56
58
  export declare function normalizeCodeApiRequestError(error: unknown): CodeApiRequestError;
@@ -27,15 +27,24 @@ export interface ToolBatchReplayRecord {
27
27
  interface ToolBatchReplayState {
28
28
  version: 1;
29
29
  approvalOwner?: string;
30
+ /** Populated from the pending interrupt by Run, including question pauses. */
31
+ interruptId?: string;
30
32
  records: ToolBatchReplayRecord[];
31
33
  wrappedPayload?: boolean;
32
34
  }
33
35
  /** The only host-to-runtime entry point for checkpoint-owned tool replay state. */
34
36
  export declare function restoreToolReplayConfig(configurable: Record<string, unknown>, interruptId: string | undefined, payload: unknown): void;
37
+ /**
38
+ * Resume maps identify the checkpointed interrupt, while the task scratchpad
39
+ * identifies the node that can consume it. Config survives into later graph
40
+ * steps, so the presence of replay evidence alone does not imply a resume.
41
+ * A parent replaying a checkpointed child has no local resume value.
42
+ */
43
+ export declare function isToolReplayResume(config: RunnableConfig, interruptId: string | undefined, isChildReplay: boolean): boolean;
35
44
  export declare function getToolBatchReplayOwner(config: RunnableConfig, agentId: string): string;
36
45
  export declare function getToolBatchReplayScope(config: RunnableConfig): string | undefined;
37
- /** Foreign approval evidence may only transit a checkpoint-proven child path. */
38
- export declare function isChildToolApprovalOwner(config: RunnableConfig, owner: string, trustedParentCallIds: ReadonlySet<string>): boolean;
46
+ /** Foreign replay evidence may only transit a checkpoint-proven child path. */
47
+ export declare function isChildToolReplayOwner(config: RunnableConfig, owner: string, trustedParentCallIds: ReadonlySet<string>): boolean;
39
48
  /** Rebind only the checkpoint-proven source execution when a child fork is created. */
40
49
  export declare function rebindToolBatchReplayScope(configurable: Record<string, unknown>, sourceScope: string, destinationScope: string): void;
41
50
  /** A fork may be paused again before it consumes its pending interrupt. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@librechat/agents",
3
- "version": "3.8.2",
3
+ "version": "3.8.4",
4
4
  "reova": {
5
5
  "enabled": true,
6
6
  "endpoint": "https://telemetry.reo.dev/data"
@@ -116,3 +116,6 @@ export type {
116
116
  PostCompactHookOutput,
117
117
  } from './types';
118
118
  export type { ExecuteHooksOptions } from './executeHooks';
119
+
120
+ /** Hosts may opt into stable generation scopes across every ToolNode interrupt type. */
121
+ export const TOOL_APPROVAL_EXECUTION_SCOPE_CAPABLE = true;
@@ -28,6 +28,12 @@ export const HOOK_EVENTS = [
28
28
  'PostCompact',
29
29
  ] as const;
30
30
 
31
+ /**
32
+ * Host-owned generation identity, stable across every resume and rebuilt Run.
33
+ * A new generation must use a new scope, even when it reuses a thread or response
34
+ * id. Explicit scoping keeps LangGraph task namespaces out of approval owners;
35
+ * the executing agent id remains part of the owner and cannot change on resume.
36
+ */
31
37
  export const TOOL_APPROVAL_EXECUTION_SCOPE_CONFIG_KEY =
32
38
  '__librechat_tool_approval_execution_scope';
33
39
 
@@ -279,7 +279,7 @@ function createBashExecutionTool(
279
279
  };
280
280
 
281
281
  const proxyAgent = resolveFetchProxyAgent(execEndpoint);
282
- if (proxyAgent) {
282
+ if (proxyAgent != null) {
283
283
  fetchOptions.agent = proxyAgent;
284
284
  }
285
285
  const response = await fetch(execEndpoint, fetchOptions);
@@ -343,7 +343,7 @@ function createBashExecutionTool(
343
343
  normalizeCodeApiRequestError(error).message,
344
344
  command
345
345
  );
346
- throw new Error(`Execution error:\n\n${messageWithReminder}`);
346
+ throw new CodeApiRequestError(`Execution error:\n\n${messageWithReminder}`);
347
347
  }
348
348
  },
349
349
  {
@@ -542,9 +542,11 @@ export function createBashProgrammaticToolCallingTool(
542
542
  (error as Error).message,
543
543
  code
544
544
  );
545
- throw new Error(
546
- `Bash programmatic execution failed: ${messageWithReminder}`
547
- );
545
+ const message = `Bash programmatic execution failed: ${messageWithReminder}`;
546
+ if (error instanceof CodeApiRequestError) {
547
+ throw new CodeApiRequestError(message);
548
+ }
549
+ throw new Error(message, { cause: error });
548
550
  }
549
551
  },
550
552
  {
@@ -70,6 +70,7 @@ export function appendFailedExecutionFileReminder(
70
70
  code: string
71
71
  ): string {
72
72
  if (
73
+ output.includes(CODE_API_CAPABILITY_ERROR_MESSAGE) ||
73
74
  !MNT_DATA_PATH_PATTERN.test(code) ||
74
75
  output.includes(FAILED_EXECUTION_FILE_REMINDER)
75
76
  ) {
@@ -157,10 +158,67 @@ const SAFE_CODE_API_EXECUTION_ERROR_DETAILS: Readonly<
157
158
  'stdout length exceeded': 'Execution output exceeded the size limit.',
158
159
  };
159
160
 
161
+ export const CODE_API_CAPABILITY_ERROR_MESSAGE =
162
+ 'Code execution is not supported by the selected environment.';
163
+ const CODE_API_CAPABILITY_ERROR_GUIDANCE =
164
+ 'This is a permanent capability mismatch. Do not retry this tool in this environment; select a compatible environment or use an available workspace tool.';
165
+
166
+ const CODE_API_CAPABILITY_LIMITATIONS: Readonly<
167
+ Partial<Record<string, string>>
168
+ > = {
169
+ bridge_worker_mismatch:
170
+ 'The selected worker does not support the requested execution capabilities (such as the stateful workspace required by programmatic tool calling).',
171
+ execution_profile_mismatch:
172
+ 'The selected environment does not provide the requested execution profile.',
173
+ capability_mismatch:
174
+ 'The selected environment lacks a required execution capability.',
175
+ unsupported_capability:
176
+ 'The selected environment lacks a required execution capability.',
177
+ stateful_workspace_unsupported:
178
+ 'The selected worker does not provide a stateful workspace.',
179
+ };
180
+
181
+ function getCodeApiCapabilityErrorMessage(
182
+ responseBody: string
183
+ ): string | undefined {
184
+ try {
185
+ const parsed = JSON.parse(responseBody) as {
186
+ error?: string;
187
+ code?: string;
188
+ message?: string;
189
+ } | null;
190
+ const code = parsed?.code ?? parsed?.error;
191
+ if (typeof code !== 'string') return undefined;
192
+ const normalizedCode = code.toLowerCase();
193
+ if (!Object.hasOwn(CODE_API_CAPABILITY_LIMITATIONS, normalizedCode))
194
+ return undefined;
195
+ let limitation = CODE_API_CAPABILITY_LIMITATIONS[normalizedCode];
196
+ if (limitation == null) return undefined;
197
+ if (
198
+ normalizedCode === 'bridge_worker_mismatch' &&
199
+ typeof parsed?.message === 'string' &&
200
+ /^Bridge worker [A-Za-z0-9._:-]+ does not provide a stateful workspace$/.test(
201
+ parsed.message
202
+ )
203
+ ) {
204
+ limitation =
205
+ CODE_API_CAPABILITY_LIMITATIONS.stateful_workspace_unsupported;
206
+ }
207
+ return `${CODE_API_CAPABILITY_ERROR_MESSAGE} ${limitation} ${CODE_API_CAPABILITY_ERROR_GUIDANCE}`;
208
+ } catch {
209
+ return undefined;
210
+ }
211
+ }
212
+
160
213
  export class CodeApiRequestError extends Error {
214
+ readonly retryable: boolean;
215
+
161
216
  constructor(message = CODE_API_UNAVAILABLE_ERROR_MESSAGE) {
162
217
  super(message);
163
218
  this.name = 'CodeApiRequestError';
219
+ this.retryable =
220
+ message.includes(CODE_API_UNAVAILABLE_ERROR_MESSAGE) ||
221
+ message.includes('Code execution is temporarily rate-limited.');
164
222
  }
165
223
  }
166
224
 
@@ -277,11 +335,7 @@ type CodeApiErrorResponse = {
277
335
  body?: NodeJS.ReadableStream | null;
278
336
  };
279
337
 
280
- /**
281
- * Only the 429 branch reads the body, and node-fetch keeps the stream and its
282
- * socket alive until something does. Repeated backend failures would otherwise
283
- * accumulate connections holding payloads this module deliberately discards.
284
- */
338
+ /** Releases unread or incomplete error responses after bounded classification. */
285
339
  function discardResponseBody(response: CodeApiErrorResponse): void {
286
340
  const body = response.body;
287
341
  if (body == null || !('destroy' in body)) {
@@ -298,6 +352,48 @@ function discardResponseBody(response: CodeApiErrorResponse): void {
298
352
  }
299
353
  }
300
354
 
355
+ const MAX_CODE_API_ERROR_BODY_BYTES = 64 * 1024;
356
+ const CODE_API_ERROR_BODY_TIMEOUT_MS = 1_000;
357
+
358
+ /** Reads only small error envelopes, with a deadline for stalled upstreams. */
359
+ async function readCodeApiErrorBody(
360
+ response: CodeApiErrorResponse
361
+ ): Promise<string> {
362
+ let timer: ReturnType<typeof setTimeout> | undefined;
363
+ try {
364
+ const read = async (): Promise<string> => {
365
+ if (response.body != null && Symbol.asyncIterator in response.body) {
366
+ let bytes = 0;
367
+ const chunks: Buffer[] = [];
368
+ for await (const chunk of response.body as AsyncIterable<
369
+ Buffer | string
370
+ >) {
371
+ const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
372
+ bytes += buffer.length;
373
+ if (bytes > MAX_CODE_API_ERROR_BODY_BYTES) return '';
374
+ chunks.push(buffer);
375
+ }
376
+ return Buffer.concat(chunks).toString('utf8');
377
+ }
378
+ const body = await response.text();
379
+ return Buffer.byteLength(body) <= MAX_CODE_API_ERROR_BODY_BYTES
380
+ ? body
381
+ : '';
382
+ };
383
+ return await Promise.race([
384
+ read(),
385
+ new Promise<string>((resolve) => {
386
+ timer = setTimeout(() => resolve(''), CODE_API_ERROR_BODY_TIMEOUT_MS);
387
+ }),
388
+ ]);
389
+ } catch {
390
+ return '';
391
+ } finally {
392
+ clearTimeout(timer);
393
+ discardResponseBody(response);
394
+ }
395
+ }
396
+
301
397
  export async function buildCodeApiHttpErrorMessage(
302
398
  method: CodeApiMethod,
303
399
  _endpoint: string,
@@ -305,8 +401,8 @@ export async function buildCodeApiHttpErrorMessage(
305
401
  options?: { recoverable?: boolean; profile?: t.CodeApiExecutionProfile }
306
402
  ): Promise<string> {
307
403
  /* Logged before the body is touched. A non-OK response can leave a chunked
308
- body open, and there is no read timeout here, so draining first would
309
- withhold the diagnostic indefinitely for exactly the backend it identifies.
404
+ body open, so logging first preserves the diagnostic even if the bounded
405
+ classification read times out.
310
406
  The body itself is never logged — it is upstream free text that can echo
311
407
  the header that was sent — and the endpoint is host-configured, so the
312
408
  backend is named by the profile this module chose rather than by its
@@ -325,16 +421,12 @@ export async function buildCodeApiHttpErrorMessage(
325
421
  }
326
422
  );
327
423
  }
328
- if (response.status !== 429) {
329
- discardResponseBody(response);
424
+ const responseBody = await readCodeApiErrorBody(response);
425
+ const capabilityError = getCodeApiCapabilityErrorMessage(responseBody);
426
+ if (capabilityError != null) {
427
+ return capabilityError;
330
428
  }
331
429
  if (response.status === 429) {
332
- let responseBody = '';
333
- try {
334
- responseBody = await response.text();
335
- } catch {
336
- responseBody = '';
337
- }
338
430
  const retryAfterSeconds = getRetryAfterSeconds(responseBody);
339
431
  return retryAfterSeconds != null
340
432
  ? `Code execution is temporarily rate-limited. Retry after ${retryAfterSeconds} seconds.`
@@ -543,7 +635,7 @@ function createCodeExecutionTool(
543
635
  };
544
636
 
545
637
  const proxyAgent = resolveFetchProxyAgent(execEndpoint);
546
- if (proxyAgent) {
638
+ if (proxyAgent != null) {
547
639
  fetchOptions.agent = proxyAgent;
548
640
  }
549
641
  const response = await fetch(execEndpoint, fetchOptions);
@@ -610,7 +702,9 @@ function createCodeExecutionTool(
610
702
  normalizeCodeApiRequestError(error).message,
611
703
  code
612
704
  );
613
- throw new Error(`Execution error:\n\n${messageWithReminder}`);
705
+ throw new CodeApiRequestError(
706
+ `Execution error:\n\n${messageWithReminder}`
707
+ );
614
708
  }
615
709
  },
616
710
  {
@@ -469,7 +469,7 @@ export async function fetchSessionFiles(
469
469
  };
470
470
 
471
471
  const proxyAgent = resolveFetchProxyAgent(filesEndpoint, proxy);
472
- if (proxyAgent) {
472
+ if (proxyAgent != null) {
473
473
  fetchOptions.agent = proxyAgent;
474
474
  }
475
475
 
@@ -531,7 +531,7 @@ export async function makeRequest(
531
531
  };
532
532
 
533
533
  const proxyAgent = resolveFetchProxyAgent(endpoint, proxy);
534
- if (proxyAgent) {
534
+ if (proxyAgent != null) {
535
535
  fetchOptions.agent = proxyAgent;
536
536
  }
537
537
 
@@ -689,7 +689,7 @@ type ToolInputSchemaKind = {
689
689
  function detectSchemaKind(schema: unknown): ToolInputSchemaKind {
690
690
  const kind: ToolInputSchemaKind = { object: false, string: false };
691
691
 
692
- if (!schema || typeof schema !== 'object') {
692
+ if (schema == null || typeof schema !== 'object') {
693
693
  return kind;
694
694
  }
695
695
 
@@ -704,7 +704,7 @@ function detectSchemaKind(schema: unknown): ToolInputSchemaKind {
704
704
  }
705
705
 
706
706
  const zodDef = (schema as { _def?: unknown })._def;
707
- if (!zodDef || typeof zodDef !== 'object') {
707
+ if (zodDef == null || typeof zodDef !== 'object') {
708
708
  return kind;
709
709
  }
710
710
 
@@ -726,7 +726,7 @@ function detectSchemaKind(schema: unknown): ToolInputSchemaKind {
726
726
  type?: unknown;
727
727
  }
728
728
  ).innerType ?? (zodDef as { schema?: unknown }).schema;
729
- if (innerSchema) {
729
+ if (innerSchema != null) {
730
730
  const innerKind = detectSchemaKind(innerSchema);
731
731
  kind.object ||= innerKind.object;
732
732
  kind.string ||= innerKind.string;
@@ -1221,9 +1221,11 @@ export function createProgrammaticToolCallingTool(
1221
1221
  (error as Error).message,
1222
1222
  code
1223
1223
  );
1224
- throw new Error(
1225
- `Programmatic execution failed: ${messageWithReminder}`
1226
- );
1224
+ const message = `Programmatic execution failed: ${messageWithReminder}`;
1225
+ if (error instanceof CodeApiRequestError) {
1226
+ throw new CodeApiRequestError(message);
1227
+ }
1228
+ throw new Error(message, { cause: error });
1227
1229
  }
1228
1230
  },
1229
1231
  {
@@ -128,7 +128,8 @@ import {
128
128
  attachToolBatchReplayState,
129
129
  restoreToolBatchReplayState,
130
130
  getToolBatchReplayState,
131
- isChildToolApprovalOwner,
131
+ isChildToolReplayOwner,
132
+ isToolReplayResume,
132
133
  } from '@/tools/toolBatchReplay';
133
134
 
134
135
  function stripToolApprovalReviewConfig(
@@ -908,19 +909,36 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
908
909
  config,
909
910
  this.executingAgentId ?? this.agentId ?? ''
910
911
  );
911
- const reviewEvidence = getToolApprovalReviewEvidence(config);
912
- if (reviewEvidence?.owner != null && reviewEvidence.owner !== replayOwner) {
912
+ let reviewEvidence = getToolApprovalReviewEvidence(config);
913
+ const replayState = getToolBatchReplayState(config.configurable);
914
+ const ownsParentBatch = replayState?.records.some(
915
+ (record) => record.owner === replayOwner && record.batch === activeReplayKey
916
+ ) === true;
917
+ let isChildReplay = false;
918
+ if (ownsParentBatch) {
913
919
  const calls = this.isSendInput(input) ? [input.lg_tool_call] : assistantBatch?.message.tool_calls ?? [];
914
920
  const trustedParentCallIds = new Set(calls.filter((call) =>
915
921
  call.name === Constants.SUBAGENT &&
916
922
  (this.toolMap.get(call.name) as ReplayableSubagentTool | undefined)?.[SUBAGENT_REPLAY_CONTROLLER] != null
917
923
  ).map((call) => call.id ?? ''));
918
- const ownsParentBatch = getToolBatchReplayState(config.configurable)?.records.some(
919
- (record) => record.owner === replayOwner && record.batch === activeReplayKey
920
- ) === true;
921
- if (!ownsParentBatch || !isChildToolApprovalOwner(config, reviewEvidence.owner, trustedParentCallIds)) {
922
- throw new Error('Tool approval execution owner changed before resume — failing closed');
923
- }
924
+ isChildReplay = reviewEvidence?.owner != null
925
+ ? isChildToolReplayOwner(config, reviewEvidence.owner, trustedParentCallIds)
926
+ : replayState.records.some((record) => isChildToolReplayOwner(config, record.owner, trustedParentCallIds));
927
+ }
928
+ if ((reviewEvidence?.owner != null || replayState != null) &&
929
+ !isToolReplayResume(config, reviewEvidence?.interruptId ?? replayState?.interruptId, isChildReplay)) {
930
+ config = {
931
+ ...config,
932
+ configurable: {
933
+ ...config.configurable,
934
+ [TOOL_APPROVAL_REVIEW_CONFIG_KEY]: undefined,
935
+ [TOOL_BATCH_REPLAY_KEY]: undefined,
936
+ },
937
+ };
938
+ reviewEvidence = undefined;
939
+ }
940
+ if (reviewEvidence?.owner != null && reviewEvidence.owner !== replayOwner && !isChildReplay) {
941
+ throw new Error('Tool approval execution owner changed before resume — failing closed');
924
942
  }
925
943
  const referenceReplay: { state?: ToolOutputReferenceState } = {};
926
944
  const restoredBatches = await restoreToolBatchReplayState(config, replayOwner);
@@ -49,6 +49,8 @@ export interface ToolBatchReplayRecord {
49
49
  interface ToolBatchReplayState {
50
50
  version: 1;
51
51
  approvalOwner?: string;
52
+ /** Populated from the pending interrupt by Run, including question pauses. */
53
+ interruptId?: string;
52
54
  records: ToolBatchReplayRecord[];
53
55
  wrappedPayload?: boolean;
54
56
  }
@@ -80,13 +82,40 @@ export function restoreToolReplayConfig(
80
82
  throw new Error('Invalid tool approval checkpoint');
81
83
  }
82
84
  if (state != null) {
83
- configurable[TOOL_BATCH_REPLAY_KEY] = state;
85
+ configurable[TOOL_BATCH_REPLAY_KEY] = { ...state, interruptId };
84
86
  }
85
87
  if (review != null) {
86
88
  configurable[TOOL_APPROVAL_REVIEW_CONFIG_KEY] = review;
87
89
  }
88
90
  }
89
91
 
92
+ /**
93
+ * Resume maps identify the checkpointed interrupt, while the task scratchpad
94
+ * identifies the node that can consume it. Config survives into later graph
95
+ * steps, so the presence of replay evidence alone does not imply a resume.
96
+ * A parent replaying a checkpointed child has no local resume value.
97
+ */
98
+ export function isToolReplayResume(
99
+ config: RunnableConfig,
100
+ interruptId: string | undefined,
101
+ isChildReplay: boolean
102
+ ): boolean {
103
+ const resumeMap: object | undefined = config.configurable?.__pregel_resume_map;
104
+ if (interruptId != null &&
105
+ resumeMap != null &&
106
+ !Object.prototype.hasOwnProperty.call(resumeMap, interruptId)) {
107
+ return false;
108
+ }
109
+ if (isChildReplay && resumeMap != null) {
110
+ return true;
111
+ }
112
+ const scratchpad: { resume?: { length: number }; nullResume?: unknown } | undefined =
113
+ config.configurable?.__pregel_scratchpad;
114
+ return scratchpad == null ||
115
+ (scratchpad.resume?.length ?? 0) > 0 ||
116
+ scratchpad.nullResume !== undefined;
117
+ }
118
+
90
119
  export function getToolBatchReplayOwner(
91
120
  config: RunnableConfig,
92
121
  agentId: string
@@ -109,8 +138,8 @@ export function getToolBatchReplayScope(
109
138
  return typeof scope === 'string' && scope.length > 0 ? scope : undefined;
110
139
  }
111
140
 
112
- /** Foreign approval evidence may only transit a checkpoint-proven child path. */
113
- export function isChildToolApprovalOwner(
141
+ /** Foreign replay evidence may only transit a checkpoint-proven child path. */
142
+ export function isChildToolReplayOwner(
114
143
  config: RunnableConfig,
115
144
  owner: string,
116
145
  trustedParentCallIds: ReadonlySet<string>
@@ -208,6 +237,7 @@ export function getToolBatchReplayState(
208
237
  (state.wrappedPayload != null &&
209
238
  typeof state.wrappedPayload !== 'boolean') ||
210
239
  (state.approvalOwner != null && typeof state.approvalOwner !== 'string') ||
240
+ (state.interruptId != null && typeof state.interruptId !== 'string') ||
211
241
  !Array.isArray(state.records) ||
212
242
  state.records.some((value: unknown) => {
213
243
  if (value == null || typeof value !== 'object') {