@librechat/agents 3.1.79 → 3.1.80-dev.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/dist/cjs/tools/BashExecutor.cjs +13 -2
  2. package/dist/cjs/tools/BashExecutor.cjs.map +1 -1
  3. package/dist/cjs/tools/BashProgrammaticToolCalling.cjs +2 -1
  4. package/dist/cjs/tools/BashProgrammaticToolCalling.cjs.map +1 -1
  5. package/dist/cjs/tools/CodeExecutor.cjs +21 -5
  6. package/dist/cjs/tools/CodeExecutor.cjs.map +1 -1
  7. package/dist/cjs/tools/ProgrammaticToolCalling.cjs +12 -4
  8. package/dist/cjs/tools/ProgrammaticToolCalling.cjs.map +1 -1
  9. package/dist/cjs/tools/ToolNode.cjs +73 -40
  10. package/dist/cjs/tools/ToolNode.cjs.map +1 -1
  11. package/dist/cjs/tools/local/LocalExecutionTools.cjs.map +1 -1
  12. package/dist/cjs/tools/local/LocalProgrammaticToolCalling.cjs.map +1 -1
  13. package/dist/esm/tools/BashExecutor.mjs +13 -2
  14. package/dist/esm/tools/BashExecutor.mjs.map +1 -1
  15. package/dist/esm/tools/BashProgrammaticToolCalling.mjs +2 -1
  16. package/dist/esm/tools/BashProgrammaticToolCalling.mjs.map +1 -1
  17. package/dist/esm/tools/CodeExecutor.mjs +21 -5
  18. package/dist/esm/tools/CodeExecutor.mjs.map +1 -1
  19. package/dist/esm/tools/ProgrammaticToolCalling.mjs +12 -4
  20. package/dist/esm/tools/ProgrammaticToolCalling.mjs.map +1 -1
  21. package/dist/esm/tools/ToolNode.mjs +73 -40
  22. package/dist/esm/tools/ToolNode.mjs.map +1 -1
  23. package/dist/esm/tools/local/LocalExecutionTools.mjs.map +1 -1
  24. package/dist/esm/tools/local/LocalProgrammaticToolCalling.mjs.map +1 -1
  25. package/dist/types/types/tools.d.ts +106 -17
  26. package/package.json +1 -1
  27. package/src/scripts/code_exec_multi_session.ts +4 -4
  28. package/src/tools/BashExecutor.ts +14 -3
  29. package/src/tools/BashProgrammaticToolCalling.ts +6 -6
  30. package/src/tools/CodeExecutor.ts +22 -6
  31. package/src/tools/ProgrammaticToolCalling.ts +17 -10
  32. package/src/tools/ToolNode.ts +96 -48
  33. package/src/tools/__tests__/ProgrammaticToolCalling.test.ts +9 -2
  34. package/src/tools/__tests__/ToolNode.session.test.ts +152 -50
  35. package/src/tools/local/LocalExecutionTools.ts +2 -2
  36. package/src/tools/local/LocalProgrammaticToolCalling.ts +23 -6
  37. package/src/types/tools.ts +103 -17
@@ -123,39 +123,79 @@ function normalizeApprovalDecisions(callIds, resumeValue) {
123
123
  * - `artifact.session_id` (the `sessionId` arg here) is the EXEC session
124
124
  * — the sandbox VM that ran the code. It's transient and torn down
125
125
  * post-execution; subsequent calls cannot reuse it as a sandbox.
126
- * - `file.session_id` on each `artifact.files[i]` is the STORAGE
126
+ * - `file.storage_session_id` on each `artifact.files[i]` is the STORAGE
127
127
  * session — the file-server bucket prefix where the artifact actually
128
128
  * lives and is served from.
129
129
  *
130
- * Per-file `session_id` is preserved (not overwritten with the exec id)
131
- * because `_injected_files` are looked up against the file-server's
132
- * storage path on subsequent tool calls. Stomping the storage id with
133
- * the exec id silently 404s every follow-up tool call within the same
134
- * run — `cat /mnt/data/foo.txt` reports "No such file or directory"
135
- * because the worker can't mount a file at a path the storage doesn't
136
- * know about. Fall back to `sessionId` only when the per-file id is
137
- * absent (older worker payloads).
130
+ * Per-file `storage_session_id` is preserved (not overwritten with the
131
+ * exec id) because `_injected_files` are looked up against the
132
+ * file-server's storage path on subsequent tool calls. Stomping the
133
+ * storage id with the exec id silently 404s every follow-up tool call
134
+ * within the same run — `cat /mnt/data/foo.txt` reports "No such file
135
+ * or directory" because the worker can't mount a file at a path the
136
+ * storage doesn't know about. Fall back to the exec id only when the
137
+ * per-file id is absent (e.g. inline `content` files have no persistent
138
+ * storage location).
138
139
  */
139
- function updateCodeSession(sessions, sessionId, files) {
140
+ /**
141
+ * Builds a `CodeEnvFile` ref from an arbitrary `FileRef`-like input,
142
+ * narrowing onto the discriminated union: `kind: 'skill'` requires
143
+ * `version`, other kinds forbid it.
144
+ *
145
+ * Defaults `kind` to `'user'` when unset — most ad-hoc files are
146
+ * user-private; shared resources (skills/agents) populate their kind
147
+ * upstream. A skill ref missing `version` falls back to `'user'` so
148
+ * the upstream contract bug surfaces as a degraded sessionKey rather
149
+ * than a runtime crash; primeSkillFiles is the only writer, and it
150
+ * always sets `version` — see LC packages/api/src/agents/skillFiles.ts.
151
+ *
152
+ * `resource_id` carries the entity-that-owns-this-file's-session
153
+ * identity (skill `_id` etc.); falls back to `id` (the storage
154
+ * file_id) for inputs that haven't been updated to send the field
155
+ * explicitly. The fallback degrades sessionKey resolution on the
156
+ * codeapi side for shared kinds (it'll match the storage nanoid
157
+ * against a skill _id and 403) — but won't crash, so an unmigrated
158
+ * client still produces a diagnosable error instead of a stack
159
+ * trace.
160
+ */
161
+ function toInjectedFileRef(file, execSessionId) {
162
+ const base = {
163
+ id: file.id,
164
+ resource_id: file.resource_id ?? file.id,
165
+ name: file.name,
166
+ /* Inline `content` files have no persistent storage location;
167
+ * fall back to the execution session id for those entries. */
168
+ storage_session_id: file.storage_session_id ?? execSessionId,
169
+ };
170
+ const kind = file.kind ?? 'user';
171
+ if (kind === 'skill' && file.version != null) {
172
+ return { ...base, kind: 'skill', version: file.version };
173
+ }
174
+ if (kind === 'agent') {
175
+ return { ...base, kind: 'agent' };
176
+ }
177
+ return { ...base, kind: 'user' };
178
+ }
179
+ function updateCodeSession(sessions, execSessionId, files) {
140
180
  const newFiles = files ?? [];
141
181
  const existingSession = sessions.get(_enum.Constants.EXECUTE_CODE);
142
182
  const existingFiles = existingSession?.files ?? [];
143
183
  if (newFiles.length > 0) {
144
184
  const filesWithSession = newFiles.map((file) => ({
145
185
  ...file,
146
- session_id: file.session_id ?? sessionId,
186
+ storage_session_id: file.storage_session_id ?? execSessionId,
147
187
  }));
148
188
  const newFileNames = new Set(filesWithSession.map((f) => f.name));
149
189
  const filteredExisting = existingFiles.filter((f) => !newFileNames.has(f.name));
150
190
  sessions.set(_enum.Constants.EXECUTE_CODE, {
151
- session_id: sessionId,
191
+ session_id: execSessionId,
152
192
  files: [...filteredExisting, ...filesWithSession],
153
193
  lastUpdated: Date.now(),
154
194
  });
155
195
  }
156
196
  else {
157
197
  sessions.set(_enum.Constants.EXECUTE_CODE, {
158
- session_id: sessionId,
198
+ session_id: execSessionId,
159
199
  files: existingFiles,
160
200
  lastUpdated: Date.now(),
161
201
  });
@@ -242,7 +282,10 @@ class ToolNode extends run.RunnableCallable {
242
282
  this.loadRuntimeTools = loadRuntimeTools;
243
283
  this.errorHandler = errorHandler;
244
284
  this.toolUsageCount = new Map();
245
- this.toolRegistry = resolveLocalExecutionTools.resolveLocalToolRegistry({ toolRegistry, toolExecution });
285
+ this.toolRegistry = resolveLocalExecutionTools.resolveLocalToolRegistry({
286
+ toolRegistry,
287
+ toolExecution,
288
+ });
246
289
  this.sessions = sessions;
247
290
  this.eventDrivenMode = eventDrivenMode ?? false;
248
291
  this.agentId = agentId;
@@ -509,19 +552,14 @@ class ToolNode extends run.RunnableCallable {
509
552
  */
510
553
  if (_enum.CODE_EXECUTION_TOOLS.has(call.name)) {
511
554
  const codeSession = this.sessions?.get(_enum.Constants.EXECUTE_CODE);
512
- if (codeSession?.session_id != null && codeSession.session_id !== '') {
555
+ const execSessionId = codeSession?.session_id;
556
+ if (execSessionId != null && execSessionId !== '') {
513
557
  invokeParams = {
514
558
  ...invokeParams,
515
- session_id: codeSession.session_id,
559
+ session_id: execSessionId,
516
560
  };
517
- if (codeSession.files != null && codeSession.files.length > 0) {
518
- const fileRefs = codeSession.files.map((file) => ({
519
- session_id: file.session_id ?? codeSession.session_id,
520
- id: file.id,
521
- name: file.name,
522
- ...(file.entity_id != null ? { entity_id: file.entity_id } : {}),
523
- }));
524
- invokeParams._injected_files = fileRefs;
561
+ if (codeSession?.files != null && codeSession.files.length > 0) {
562
+ invokeParams._injected_files = codeSession.files.map((file) => toInjectedFileRef(file, execSessionId));
525
563
  }
526
564
  }
527
565
  }
@@ -737,8 +775,7 @@ class ToolNode extends run.RunnableCallable {
737
775
  // survives `run()`'s clear, so a resume sees the original
738
776
  // assignment.)
739
777
  const cachedTurn = call.id != null && call.id !== ''
740
- ? this.directPathTurns.get(call.id) ??
741
- this.toolCallTurns.get(call.id)
778
+ ? (this.directPathTurns.get(call.id) ?? this.toolCallTurns.get(call.id))
742
779
  : undefined;
743
780
  if (cachedTurn != null) {
744
781
  usageCount = cachedTurn;
@@ -874,9 +911,7 @@ class ToolNode extends run.RunnableCallable {
874
911
  return this.blockDirectCall({
875
912
  call,
876
913
  resolvedArgs,
877
- reason: decision.reason ??
878
- preResult.reason ??
879
- 'Rejected by user',
914
+ reason: decision.reason ?? preResult.reason ?? 'Rejected by user',
880
915
  hookRegistry,
881
916
  runId,
882
917
  threadId,
@@ -1135,16 +1170,12 @@ class ToolNode extends run.RunnableCallable {
1135
1170
  if (!codeSession) {
1136
1171
  return undefined;
1137
1172
  }
1173
+ const execSessionId = codeSession.session_id;
1138
1174
  const context = {
1139
- session_id: codeSession.session_id,
1175
+ session_id: execSessionId,
1140
1176
  };
1141
1177
  if (codeSession.files && codeSession.files.length > 0) {
1142
- context.files = codeSession.files.map((file) => ({
1143
- session_id: file.session_id ?? codeSession.session_id,
1144
- id: file.id,
1145
- name: file.name,
1146
- ...(file.entity_id != null ? { entity_id: file.entity_id } : {}),
1147
- }));
1178
+ context.files = codeSession.files.map((file) => toInjectedFileRef(file, execSessionId));
1148
1179
  }
1149
1180
  return context;
1150
1181
  }
@@ -1169,10 +1200,11 @@ class ToolNode extends run.RunnableCallable {
1169
1200
  continue;
1170
1201
  }
1171
1202
  const artifact = result.artifact;
1172
- if (artifact?.session_id == null || artifact.session_id === '') {
1203
+ const execSessionId = artifact?.session_id;
1204
+ if (execSessionId == null || execSessionId === '') {
1173
1205
  continue;
1174
1206
  }
1175
- updateCodeSession(this.sessions, artifact.session_id, artifact.files);
1207
+ updateCodeSession(this.sessions, execSessionId, artifact?.files);
1176
1208
  }
1177
1209
  }
1178
1210
  /**
@@ -1205,8 +1237,9 @@ class ToolNode extends run.RunnableCallable {
1205
1237
  }
1206
1238
  if (this.sessions && _enum.CODE_EXECUTION_TOOLS.has(call.name)) {
1207
1239
  const artifact = toolMessage.artifact;
1208
- if (artifact?.session_id != null && artifact.session_id !== '') {
1209
- updateCodeSession(this.sessions, artifact.session_id, artifact.files);
1240
+ const execSessionId = artifact?.session_id;
1241
+ if (execSessionId != null && execSessionId !== '') {
1242
+ updateCodeSession(this.sessions, execSessionId, artifact?.files);
1210
1243
  }
1211
1244
  }
1212
1245
  // Dispatch ON_RUN_STEP_COMPLETED via custom event (same path as dispatchToolEvents)