@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.
- package/dist/cjs/hooks/index.cjs +2 -0
- package/dist/cjs/hooks/index.cjs.map +1 -1
- package/dist/cjs/hooks/types.cjs.map +1 -1
- package/dist/cjs/main.cjs +2 -0
- package/dist/cjs/tools/BashExecutor.cjs +2 -3
- package/dist/cjs/tools/BashExecutor.cjs.map +1 -1
- package/dist/cjs/tools/BashProgrammaticToolCalling.cjs +3 -2
- package/dist/cjs/tools/BashProgrammaticToolCalling.cjs.map +1 -1
- package/dist/cjs/tools/CodeExecutor.cjs +63 -11
- package/dist/cjs/tools/CodeExecutor.cjs.map +1 -1
- package/dist/cjs/tools/ProgrammaticToolCalling.cjs +8 -7
- package/dist/cjs/tools/ProgrammaticToolCalling.cjs.map +1 -1
- package/dist/cjs/tools/ToolNode.cjs +18 -3
- package/dist/cjs/tools/ToolNode.cjs.map +1 -1
- package/dist/cjs/tools/toolBatchReplay.cjs +15 -4
- package/dist/cjs/tools/toolBatchReplay.cjs.map +1 -1
- package/dist/esm/hooks/index.mjs +2 -1
- package/dist/esm/hooks/index.mjs.map +1 -1
- package/dist/esm/hooks/types.mjs.map +1 -1
- package/dist/esm/main.mjs +3 -3
- package/dist/esm/tools/BashExecutor.mjs +2 -3
- package/dist/esm/tools/BashExecutor.mjs.map +1 -1
- package/dist/esm/tools/BashProgrammaticToolCalling.mjs +3 -2
- package/dist/esm/tools/BashProgrammaticToolCalling.mjs.map +1 -1
- package/dist/esm/tools/CodeExecutor.mjs +63 -12
- package/dist/esm/tools/CodeExecutor.mjs.map +1 -1
- package/dist/esm/tools/ProgrammaticToolCalling.mjs +8 -7
- package/dist/esm/tools/ProgrammaticToolCalling.mjs.map +1 -1
- package/dist/esm/tools/ToolNode.mjs +19 -4
- package/dist/esm/tools/ToolNode.mjs.map +1 -1
- package/dist/esm/tools/toolBatchReplay.mjs +14 -4
- package/dist/esm/tools/toolBatchReplay.mjs.map +1 -1
- package/dist/types/hooks/index.d.ts +2 -0
- package/dist/types/hooks/types.d.ts +6 -0
- package/dist/types/tools/CodeExecutor.d.ts +2 -0
- package/dist/types/tools/toolBatchReplay.d.ts +11 -2
- package/package.json +1 -1
- package/src/hooks/index.ts +3 -0
- package/src/hooks/types.ts +6 -0
- package/src/tools/BashExecutor.ts +2 -2
- package/src/tools/BashProgrammaticToolCalling.ts +5 -3
- package/src/tools/CodeExecutor.ts +111 -17
- package/src/tools/ProgrammaticToolCalling.ts +10 -8
- package/src/tools/ToolNode.ts +27 -9
- 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] =
|
|
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
|
|
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,
|
|
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":";;;;;;;;AAyBA,MAAa,wBAAwB;AACrC,MAAM,yBAAyB;AAC/B,MAAM,yBAAyB;
|
|
1
|
+
{"version":3,"file":"toolBatchReplay.mjs","names":[],"sources":["../../../src/tools/toolBatchReplay.ts"],"mappings":";;;;;;;;AAyBA,MAAa,wBAAwB;AACrC,MAAM,yBAAyB;AAC/B,MAAM,yBAAyB;AA+B/B,MAAM,aAAa,IAAI,YAAY,CAAC,CAAC;AAErC,SAAS,SAAS,OAAkD;CAClE,OAAO,SAAS,QAAQ,OAAO,UAAU,YAAY,CAAC,MAAM,QAAQ,KAAK;AAC3E;AAGA,SAAgB,wBACd,cACA,aACA,SACM;CACN,OAAO,aAAa;CACpB,OAAO,aAAa;CACpB,MAAM,QAAQ,wBAAwB,OAAO;CAC7C,MAAM,gBAAgB,8BAA8B,OAAO;CAC3D,MAAM,SAAS,iCACb,aACA,eACA,OAAO,aACT;CACA,IAAI,wBAAwB,aAAa,MACtC,UAAU,QAAS,SAAS,QAAQ,MAAM,iBAAiB,OAC5D,MAAM,IAAI,MAAM,kCAAkC;CAEpD,IAAI,SAAS,MACX,aAAa,yBAAyB;EAAE,GAAG;EAAO;CAAY;CAEhE,IAAI,UAAU,MACZ,aAAa,mCAAmC;AAEpD;AAQA,SAAgB,mBACd,QACA,aACA,eACS;CACT,MAAM,YAAgC,OAAO,cAAc;CAC3D,IAAI,eAAe,QACjB,aAAa,QACb,CAAC,OAAO,UAAU,eAAe,KAAK,WAAW,WAAW,GAC5D,OAAO;CAET,IAAI,iBAAiB,aAAa,MAChC,OAAO;CAET,MAAM,aACJ,OAAO,cAAc;CACvB,OAAO,cAAc,SAClB,WAAW,QAAQ,UAAU,KAAK,KACnC,WAAW,eAAe,KAAA;AAC9B;AAEA,SAAgB,wBACd,QACA,SACQ;CACR,OAAO,KAAK,UAAU;EACpB,wBAAwB,MAAM;EAC9B,OAAO,eAAA,gDAA4D,OAC9D,OAAO,cAAc,iBAAiB,KACvC;EACJ;CACF,CAAC;AACH;AAEA,SAAgB,wBACd,QACoB;CACpB,MAAM,QACJ,OAAO,eAAA,gDACP,OAAO,cAAc;CACvB,OAAO,OAAO,UAAU,YAAY,MAAM,SAAS,IAAI,QAAQ,KAAA;AACjE;AAGA,SAAgB,uBACd,QACA,OACA,sBACS;CACT,MAAM,WAAW,mCAAmC,OAAO,YAAY;CACvE,IAAI,YAAY,QAAQ,qBAAqB,SAAS,GAAG,OAAO;CAChE,MAAM,WAAoB,KAAK,MAAM,KAAK;CAC1C,IAAI,CAAC,MAAM,QAAQ,QAAQ,KAAK,SAAS,WAAW,KAAK,SAAS,OAAO,IAAI,OAAO;CACpF,MAAM,UAAU,SAAS,WAAW,QAAQ,cAAc,qBAAqB,IAAI,UAAU,gBAAgB,CAAC;CAC9G,OAAO,QAAQ,SAAS,GAAG;EACzB,MAAM,YAAY,QAAQ,IAAI;EAC9B,IAAI,UAAU,2BAA2B,SAAS,IAAI,OAAO;EAC7D,QAAQ,KAAK,GAAI,UAAU,YAAY,cAAc,CAAC,CAAE;CAC1D;CACA,OAAO;AACT;AAGA,SAAgB,2BACd,cACA,aACA,kBACM;CACN,MAAM,UAAU,QAAwB;EACtC,MAAM,QAAiB,KAAK,MAAM,GAAG;EACrC,IAAI,CAAC,MAAM,QAAQ,KAAK,KAAK,MAAM,OAAO,aACxC,OAAO;EAET,OAAO,KAAK,UAAU,CAAC,kBAAkB,GAAG,MAAM,MAAM,CAAC,CAAC,CAAC;CAC7D;CAEA,MAAM,SAAS,8BAA8B,EAD5B,aACiC,CAAC;CACnD,IAAI,QAAQ,SAAS,MACnB,aAAa,mCAAmC;EAC9C,GAAG;EACH,OAAO,OAAO,OAAO,KAAK;CAC5B;CAEF,MAAM,QAAQ,wBAAwB,YAAY;CAClD,IAAI,SAAS,MACX,aAAa,yBAAyB;EACpC,GAAG;EACH,eACE,MAAM,iBAAiB,OAAO,KAAA,IAAY,OAAO,MAAM,aAAa;EACtE,SAAS,MAAM,QAAQ,KAAK,YAAY;GACtC,GAAG;GACH,OAAO,OAAO,OAAO,KAAK;GAC1B,OAAO,OAAO,OAAO,KAAK;EAC5B,EAAE;CACJ;AAEJ;AAGA,SAAgB,6BACd,SACA,aACA,kBACS;CACT,MAAM,QAAQ,wBAAwB,OAAO;CAC7C,MAAM,QAAQ,wBAAwB,OAAO;CAC7C,IAAI,SAAS,QAAQ,CAAC,SAAS,KAAK,GAClC,OAAO;CAET,MAAM,eAAe,GAAG,wBAAwB,MAAM;CACtD,2BAA2B,cAAc,aAAa,gBAAgB;CACtE,MAAM,UAAU;EACd,GAAG;GACF,wBAAwB,aAAa;CACxC;CACA,MAAM,eAAe,sBAAsB,OAAO;CAClD,OAAO,gBAAgB,OACnB,UACA,yBAAyB,SAAS,YAAY;AACpD;AAEA,SAAgB,wBACd,SACkC;CAClC,MAAM,QAAQ,wBAAwB,OAAO;CAC7C,IACE,SAAS,QACT,OAAO,UAAU,YACjB,EAAA,mCAA2B,QAE3B;CAEF,MAAM,YAAqB,MAAM;CACjC,IAAI,cAAc,KAAA,GAChB;CAEF,MAAM,QAAQ;CACd,IACE,OAAO,YAAY,KAClB,MAAM,kBAAkB,QACvB,OAAO,MAAM,mBAAmB,aACjC,MAAM,iBAAiB,QAAQ,OAAO,MAAM,kBAAkB,YAC9D,MAAM,eAAe,QAAQ,OAAO,MAAM,gBAAgB,YAC3D,CAAC,MAAM,QAAQ,MAAM,OAAO,KAC5B,MAAM,QAAQ,MAAM,UAAmB;EACrC,IAAI,SAAS,QAAQ,OAAO,UAAU,UACpC,OAAO;EAET,MAAM,SAAS;EACf,OACE,OAAO,OAAO,UAAU,YACxB,OAAO,OAAO,UAAU,YACxB,OAAO,OAAO,aAAa,YAC3B,OAAO,OAAO,SAAS,YACtB,OAAO,aAAa,QACnB,CAAC,sBAAsB,OAAO,SAAS,KACxC,OAAO,kBAAkB,QACxB,CAAC,2BAA2B,OAAO,cAAc;CAEvD,CAAC,GAED,MAAM,IAAI,MAAM,sCAAsC;CAExD,OAAO,gBAAgB,KAA6B;AACtD;AAEA,SAAgB,0BAA0B,SAA2B;CACnE,IACE,WAAW,QACX,OAAO,YAAY,YACnB,EAAA,mCAA2B,UAE3B,OAAO;CAET,MAAM,QAAQ,wBAAwB,OAAO;CAC7C,MAAM,GAAG,wBAAwB,QAAQ,GAAG,kBAAkB;CAC9D,IACE,OAAO,mBAAmB,QAC1B,0BAA0B,eAE1B,OAAO,cAAc;CAEvB,OAAO;AACT;AAGA,SAAgB,8BAA8B,SAA2B;CACvE,OAAO,4BACL,0BAA0B,wBAAwB,OAAO,CAAC,CAC5D;AACF;AAEA,eAAsB,2BACpB,SACA,OACA,SACA,WACA,gBACkB;CAClB,MAAM,gBAAgB,wBAAwB,OAAO;CACrD,MAAM,WAAW,wBAAwB,aAAa;CACtD,MAAM,UACJ,UAAU,QAAQ,QAAQ,WAAW,OAAO,UAAU,KAAK,KAAK,CAAC;CACnE,KAAK,MAAM,CAAC,OAAO,YAAY,SAAS;EACtC,MAAM,CAAC,UAAU,SAAS,MAAM,WAAW,WAAW,CAAC,GAAG,OAAO,CAAC;EAClE,QAAQ,KAAK;GACX;GACA;GACA;GACA,MAAM,OAAO,KAAK,KAAK,CAAC,CAAC,SAAS,QAAQ;GAC1C,GAAI,aAAa,OAAO,CAAC,IAAI,EAAE,UAAU;GACzC,GAAI,kBAAkB,OAAO,CAAC,IAAI,EAAE,eAAe;EACrD,CAAC;CACH;CACA,MAAM,gBACJ,UAAU,kBACT,wBAAwB,aAAa,IAAI,QAAQ,KAAA;CACpD,IAAI,QAAQ,WAAW,KAAK,iBAAiB,MAC3C,OAAO;CAET,MAAM,SAAS;EACb,GAAI,SAAS,aAAa,IACtB,gBACA;IACC,yBAAyB;IACzB,yBAAyB;EAC5B;GACD,wBAAwB;GACvB,SAAS;GACT;GACA;GACA,gBAAgB,UAAU,kBAAkB,CAAC,SAAS,aAAa;EACrE;CACF;CACA,MAAM,eAAe,sBAAsB,OAAO;CAClD,OAAO,gBAAgB,OACnB,SACA,yBAAyB,QAAQ,YAAY;AACnD;AAEA,eAAsB,4BACpB,QACA,OAQA;CACA,MAAM,QAAQ,wBAAwB,OAAO,YAAY;CACzD,IAAI,SAAS,MACX,OAAO,CAAC;CAEV,OAAO,QAAQ,IACb,MAAM,QACH,QAAQ,WAAW,OAAO,UAAU,KAAK,CAAC,CAC1C,IAAI,OAAO,WAAW;EACrB,MAAM,SAAkB,MAAM,WAAW,WACvC,OAAO,UACP,OAAO,KAAK,OAAO,MAAM,QAAQ,CACnC;EACA,IAAI,CAAC,MAAM,QAAQ,MAAM,GACvB,MAAM,IAAI,MAAM,oCAAoC;EAEtD,MAAM,uBAAO,IAAI,IAAY;EAC7B,MAAM,UAAU,OAAO,KACpB,UAAqD;GACpD,IACE,CAAC,MAAM,QAAQ,KAAK,KACpB,MAAM,WAAW,KACjB,OAAO,MAAM,OAAO,YACpB,KAAK,IAAI,MAAM,EAAE,GAEjB,MAAM,IAAI,MAAM,oCAAoC;GAEtD,MAAM,SAAS,MAAM;GACrB,IACE,UAAU,QACT,CAAC,cAAc,OAAO,MAAM,KAAK,CAAC,UAAU,OAAO,MAAM,KAC1D,CAAC,MAAM,QAAQ,OAAO,kBAAkB,KACxC,OAAO,mBAAmB,MACvB,YAAY,OAAO,YAAY,QAClC,KACC,OAAO,qBAAqB,QAC3B,OAAO,OAAO,sBAAsB,aACrC,OAAO,oBAAoB,QAC1B,OAAO,OAAO,qBAAqB,YACpC,OAAO,QAAQ,SACb,CAAC,OAAO,cAAc,OAAO,IAAI,KAAK,OAAO,OAAO,MACvD,CAAC,SAAS,OAAO,QAAQ,KACzB,OAAO,OAAO,SAAS,SAAS,YAChC,OAAO,SAAS,KAAK,WAAW,KAChC,CAAC,SAAS,OAAO,SAAS,IAAI,KAC7B,OAAO,gBAAgB,QAAQ,CAAC,SAAS,OAAO,YAAY,GAE7D,MAAM,IAAI,MAAM,oCAAoC;GAEtD,KAAK,IAAI,MAAM,EAAE;GACjB,MAAM,SAAS,UAAU,OAAO,MAAM,IAClC,IAAI,QAAQ;IACZ,QAAQ,OAAO,OAAO;IACtB,MAAM,OAAO,OAAO;IACpB,OAAO,OAAO,OAAO;IACrB,QAAQ,OAAO,OAAO;GACxB,CAAC,IACC,OAAO;GACX,OAAO,CACL,MAAM,IACN;IACE,GAAG;IACH,UAAU,OAAO;IACjB;IACA,oBAAoB,OAAO;GAC7B,CACF;EACF,CACF;EACA,OAAO;GACL,OAAO,OAAO;GACd;GACA,WAAW,OAAO;GAClB,gBAAgB,OAAO;EACzB;CACF,CAAC,CACL;AACF"}
|
|
@@ -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
|
|
38
|
-
export declare function
|
|
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
package/src/hooks/index.ts
CHANGED
|
@@ -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;
|
package/src/hooks/types.ts
CHANGED
|
@@ -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
|
|
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
|
-
|
|
546
|
-
|
|
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,
|
|
309
|
-
|
|
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
|
-
|
|
329
|
-
|
|
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
|
|
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 (
|
|
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 (
|
|
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
|
-
|
|
1225
|
-
|
|
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
|
{
|
package/src/tools/ToolNode.ts
CHANGED
|
@@ -128,7 +128,8 @@ import {
|
|
|
128
128
|
attachToolBatchReplayState,
|
|
129
129
|
restoreToolBatchReplayState,
|
|
130
130
|
getToolBatchReplayState,
|
|
131
|
-
|
|
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
|
-
|
|
912
|
-
|
|
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
|
-
|
|
919
|
-
(
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
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
|
|
113
|
-
export function
|
|
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') {
|