@librechat/agents 3.8.4 → 3.8.5
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/tools/ToolNode.cjs +7 -6
- package/dist/cjs/tools/ToolNode.cjs.map +1 -1
- package/dist/cjs/tools/subagent/SubagentExecutor.cjs +3 -3
- package/dist/cjs/tools/subagent/SubagentExecutor.cjs.map +1 -1
- package/dist/cjs/tools/toolBatchReplay.cjs +64 -12
- package/dist/cjs/tools/toolBatchReplay.cjs.map +1 -1
- package/dist/esm/tools/ToolNode.mjs +8 -7
- package/dist/esm/tools/ToolNode.mjs.map +1 -1
- package/dist/esm/tools/subagent/SubagentExecutor.mjs +3 -3
- package/dist/esm/tools/subagent/SubagentExecutor.mjs.map +1 -1
- package/dist/esm/tools/toolBatchReplay.mjs +63 -12
- package/dist/esm/tools/toolBatchReplay.mjs.map +1 -1
- package/dist/types/tools/toolBatchReplay.d.ts +6 -3
- package/package.json +1 -1
- package/src/tools/ToolNode.ts +18 -8
- package/src/tools/subagent/SubagentExecutor.ts +12 -3
- package/src/tools/toolBatchReplay.ts +121 -20
|
@@ -46,6 +46,8 @@ export interface ToolBatchReplayRecord {
|
|
|
46
46
|
referenceState?: ToolOutputReferenceState;
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
+
export type ToolReplayResumeStatus = 'active' | 'stale' | 'unverifiable';
|
|
50
|
+
|
|
49
51
|
interface ToolBatchReplayState {
|
|
50
52
|
version: 1;
|
|
51
53
|
approvalOwner?: string;
|
|
@@ -62,6 +64,44 @@ function isRecord(value: unknown): value is Record<string, unknown> {
|
|
|
62
64
|
return value != null && typeof value === 'object' && !Array.isArray(value);
|
|
63
65
|
}
|
|
64
66
|
|
|
67
|
+
function bindLegacyReplayOwner(
|
|
68
|
+
owner: string,
|
|
69
|
+
configurable: Record<string, unknown>
|
|
70
|
+
): string {
|
|
71
|
+
let identity: unknown;
|
|
72
|
+
try {
|
|
73
|
+
identity = JSON.parse(owner);
|
|
74
|
+
} catch {
|
|
75
|
+
return owner;
|
|
76
|
+
}
|
|
77
|
+
if (!Array.isArray(identity) || identity.length !== 3) {
|
|
78
|
+
return owner;
|
|
79
|
+
}
|
|
80
|
+
return JSON.stringify([
|
|
81
|
+
...identity,
|
|
82
|
+
typeof configurable.user_id === 'string' ? configurable.user_id : '',
|
|
83
|
+
typeof configurable.thread_id === 'string' ? configurable.thread_id : '',
|
|
84
|
+
]);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function bindLegacyReplayState(
|
|
88
|
+
state: ToolBatchReplayState | undefined,
|
|
89
|
+
configurable: Record<string, unknown>
|
|
90
|
+
): ToolBatchReplayState | undefined {
|
|
91
|
+
if (state == null) return undefined;
|
|
92
|
+
return {
|
|
93
|
+
...state,
|
|
94
|
+
approvalOwner:
|
|
95
|
+
state.approvalOwner == null
|
|
96
|
+
? undefined
|
|
97
|
+
: bindLegacyReplayOwner(state.approvalOwner, configurable),
|
|
98
|
+
records: state.records.map((record) => ({
|
|
99
|
+
...record,
|
|
100
|
+
owner: bindLegacyReplayOwner(record.owner, configurable),
|
|
101
|
+
})),
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
|
|
65
105
|
/** The only host-to-runtime entry point for checkpoint-owned tool replay state. */
|
|
66
106
|
export function restoreToolReplayConfig(
|
|
67
107
|
configurable: Record<string, unknown>,
|
|
@@ -70,7 +110,10 @@ export function restoreToolReplayConfig(
|
|
|
70
110
|
): void {
|
|
71
111
|
delete configurable[TOOL_BATCH_REPLAY_KEY];
|
|
72
112
|
delete configurable[TOOL_APPROVAL_REVIEW_CONFIG_KEY];
|
|
73
|
-
const state =
|
|
113
|
+
const state = bindLegacyReplayState(
|
|
114
|
+
getToolBatchReplayState(payload),
|
|
115
|
+
configurable
|
|
116
|
+
);
|
|
74
117
|
const publicPayload = getPublicToolInterruptPayload(payload);
|
|
75
118
|
const review = createToolApprovalReviewEvidence(
|
|
76
119
|
interruptId,
|
|
@@ -95,25 +138,40 @@ export function restoreToolReplayConfig(
|
|
|
95
138
|
* steps, so the presence of replay evidence alone does not imply a resume.
|
|
96
139
|
* A parent replaying a checkpointed child has no local resume value.
|
|
97
140
|
*/
|
|
98
|
-
export function
|
|
141
|
+
export function getToolReplayResumeStatus(
|
|
99
142
|
config: RunnableConfig,
|
|
100
143
|
interruptId: string | undefined,
|
|
101
144
|
isChildReplay: boolean
|
|
102
|
-
):
|
|
103
|
-
const resumeMap:
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
return false;
|
|
145
|
+
): ToolReplayResumeStatus {
|
|
146
|
+
const resumeMap: unknown = config.configurable?.__pregel_resume_map;
|
|
147
|
+
const scratchpad: unknown = config.configurable?.__pregel_scratchpad;
|
|
148
|
+
if (!isRecord(scratchpad) || !Array.isArray(scratchpad.resume)) {
|
|
149
|
+
return 'unverifiable';
|
|
108
150
|
}
|
|
109
|
-
|
|
110
|
-
|
|
151
|
+
/**
|
|
152
|
+
* LangGraph supports both an interrupt-id resume map and a task-local
|
|
153
|
+
* `nullResume` value. The latter is already scoped to the checkpoint task,
|
|
154
|
+
* so it remains a supported, verifiable resume without a map entry.
|
|
155
|
+
*/
|
|
156
|
+
if (scratchpad.nullResume !== undefined) {
|
|
157
|
+
return 'active';
|
|
111
158
|
}
|
|
112
|
-
const
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
159
|
+
const hasResume = scratchpad.resume.length > 0;
|
|
160
|
+
if (!isRecord(resumeMap)) {
|
|
161
|
+
return hasResume ? 'unverifiable' : 'stale';
|
|
162
|
+
}
|
|
163
|
+
if (interruptId == null) {
|
|
164
|
+
/** Legacy direct graph callers do not restore the ID into replay state. */
|
|
165
|
+
if (!hasResume) return 'stale';
|
|
166
|
+
return Object.keys(resumeMap).length === 1 ? 'active' : 'unverifiable';
|
|
167
|
+
}
|
|
168
|
+
if (!Object.prototype.hasOwnProperty.call(resumeMap, interruptId)) {
|
|
169
|
+
return hasResume ? 'unverifiable' : 'stale';
|
|
170
|
+
}
|
|
171
|
+
if (isChildReplay) {
|
|
172
|
+
return 'active';
|
|
173
|
+
}
|
|
174
|
+
return hasResume ? 'active' : 'stale';
|
|
117
175
|
}
|
|
118
176
|
|
|
119
177
|
export function getToolBatchReplayOwner(
|
|
@@ -126,9 +184,39 @@ export function getToolBatchReplayOwner(
|
|
|
126
184
|
? (config.configurable?.checkpoint_ns ?? '')
|
|
127
185
|
: '',
|
|
128
186
|
agentId,
|
|
187
|
+
typeof config.configurable?.user_id === 'string'
|
|
188
|
+
? config.configurable.user_id
|
|
189
|
+
: '',
|
|
190
|
+
typeof config.configurable?.thread_id === 'string'
|
|
191
|
+
? config.configurable.thread_id
|
|
192
|
+
: '',
|
|
129
193
|
]);
|
|
130
194
|
}
|
|
131
195
|
|
|
196
|
+
/** Remove stale authorization while retaining only settled results for this batch. */
|
|
197
|
+
export function clearStaleToolApprovalConfig(
|
|
198
|
+
configurable: Record<string, unknown>,
|
|
199
|
+
owner: string,
|
|
200
|
+
batch: string | undefined
|
|
201
|
+
): void {
|
|
202
|
+
delete configurable[TOOL_APPROVAL_REVIEW_CONFIG_KEY];
|
|
203
|
+
const state = getToolBatchReplayState(configurable);
|
|
204
|
+
const records = batch == null
|
|
205
|
+
? []
|
|
206
|
+
: (state?.records.filter(
|
|
207
|
+
(record) => record.owner === owner && record.batch === batch
|
|
208
|
+
) ?? []);
|
|
209
|
+
if (state == null || records.length === 0) {
|
|
210
|
+
delete configurable[TOOL_BATCH_REPLAY_KEY];
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
configurable[TOOL_BATCH_REPLAY_KEY] = {
|
|
214
|
+
version: 1,
|
|
215
|
+
records,
|
|
216
|
+
wrappedPayload: state.wrappedPayload,
|
|
217
|
+
} satisfies ToolBatchReplayState;
|
|
218
|
+
}
|
|
219
|
+
|
|
132
220
|
export function getToolBatchReplayScope(
|
|
133
221
|
config: RunnableConfig
|
|
134
222
|
): string | undefined {
|
|
@@ -147,7 +235,9 @@ export function isChildToolReplayOwner(
|
|
|
147
235
|
const manifest = requireValidSubagentResumeManifest(config.configurable);
|
|
148
236
|
if (manifest == null || trustedParentCallIds.size === 0) return false;
|
|
149
237
|
const identity: unknown = JSON.parse(owner);
|
|
150
|
-
if (!Array.isArray(identity) ||
|
|
238
|
+
if (!Array.isArray(identity) ||
|
|
239
|
+
(identity.length !== 3 && identity.length !== 5) ||
|
|
240
|
+
identity[1] !== '') return false;
|
|
151
241
|
const pending = manifest.executions.filter((execution) => trustedParentCallIds.has(execution.parentToolCallId));
|
|
152
242
|
while (pending.length > 0) {
|
|
153
243
|
const execution = pending.pop()!;
|
|
@@ -161,14 +251,19 @@ export function isChildToolReplayOwner(
|
|
|
161
251
|
export function rebindToolBatchReplayScope(
|
|
162
252
|
configurable: Record<string, unknown>,
|
|
163
253
|
sourceScope: string,
|
|
164
|
-
destinationScope: string
|
|
254
|
+
destinationScope: string,
|
|
255
|
+
destinationThreadId?: string
|
|
165
256
|
): void {
|
|
166
257
|
const rebind = (key: string): string => {
|
|
167
258
|
const parts: unknown = JSON.parse(key);
|
|
168
259
|
if (!Array.isArray(parts) || parts[0] !== sourceScope) {
|
|
169
260
|
return key;
|
|
170
261
|
}
|
|
171
|
-
|
|
262
|
+
const rebound = [destinationScope, ...parts.slice(1)];
|
|
263
|
+
if (rebound.length === 5 && destinationThreadId != null) {
|
|
264
|
+
rebound[4] = destinationThreadId;
|
|
265
|
+
}
|
|
266
|
+
return JSON.stringify(rebound);
|
|
172
267
|
};
|
|
173
268
|
const config = { configurable };
|
|
174
269
|
const review = getToolApprovalReviewEvidence(config);
|
|
@@ -197,7 +292,8 @@ export function rebindToolBatchReplayScope(
|
|
|
197
292
|
export function rebindToolBatchReplayPayload(
|
|
198
293
|
payload: unknown,
|
|
199
294
|
sourceScope: string,
|
|
200
|
-
destinationScope: string
|
|
295
|
+
destinationScope: string,
|
|
296
|
+
destinationThreadId?: string
|
|
201
297
|
): unknown {
|
|
202
298
|
const state = getToolBatchReplayState(payload);
|
|
203
299
|
const value = stripRunStepResumeState(payload);
|
|
@@ -205,7 +301,12 @@ export function rebindToolBatchReplayPayload(
|
|
|
205
301
|
return payload;
|
|
206
302
|
}
|
|
207
303
|
const configurable = { [TOOL_BATCH_REPLAY_KEY]: state };
|
|
208
|
-
rebindToolBatchReplayScope(
|
|
304
|
+
rebindToolBatchReplayScope(
|
|
305
|
+
configurable,
|
|
306
|
+
sourceScope,
|
|
307
|
+
destinationScope,
|
|
308
|
+
destinationThreadId
|
|
309
|
+
);
|
|
209
310
|
const rebound = {
|
|
210
311
|
...value,
|
|
211
312
|
[TOOL_BATCH_REPLAY_KEY]: configurable[TOOL_BATCH_REPLAY_KEY],
|