@librechat/agents 3.1.78 → 3.1.79
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/llm/anthropic/index.cjs +44 -55
- package/dist/cjs/llm/anthropic/index.cjs.map +1 -1
- package/dist/cjs/llm/anthropic/utils/message_inputs.cjs +33 -21
- package/dist/cjs/llm/anthropic/utils/message_inputs.cjs.map +1 -1
- package/dist/cjs/llm/anthropic/utils/message_outputs.cjs +0 -4
- package/dist/cjs/llm/anthropic/utils/message_outputs.cjs.map +1 -1
- package/dist/cjs/messages/anthropicToolCache.cjs +48 -15
- package/dist/cjs/messages/anthropicToolCache.cjs.map +1 -1
- package/dist/cjs/messages/format.cjs +97 -14
- package/dist/cjs/messages/format.cjs.map +1 -1
- package/dist/cjs/tools/local/LocalExecutionEngine.cjs +14 -16
- package/dist/cjs/tools/local/LocalExecutionEngine.cjs.map +1 -1
- package/dist/esm/llm/anthropic/index.mjs +43 -54
- package/dist/esm/llm/anthropic/index.mjs.map +1 -1
- package/dist/esm/llm/anthropic/utils/message_inputs.mjs +33 -21
- package/dist/esm/llm/anthropic/utils/message_inputs.mjs.map +1 -1
- package/dist/esm/llm/anthropic/utils/message_outputs.mjs +0 -4
- package/dist/esm/llm/anthropic/utils/message_outputs.mjs.map +1 -1
- package/dist/esm/messages/anthropicToolCache.mjs +48 -15
- package/dist/esm/messages/anthropicToolCache.mjs.map +1 -1
- package/dist/esm/messages/format.mjs +97 -14
- package/dist/esm/messages/format.mjs.map +1 -1
- package/dist/esm/tools/local/LocalExecutionEngine.mjs +14 -16
- package/dist/esm/tools/local/LocalExecutionEngine.mjs.map +1 -1
- package/dist/types/llm/anthropic/index.d.ts +1 -9
- package/dist/types/messages/anthropicToolCache.d.ts +5 -5
- package/package.json +1 -1
- package/src/llm/anthropic/index.ts +55 -64
- package/src/llm/anthropic/llm.spec.ts +585 -0
- package/src/llm/anthropic/utils/message_inputs.ts +36 -21
- package/src/llm/anthropic/utils/message_outputs.ts +0 -4
- package/src/llm/anthropic/utils/server-tool-inputs.test.ts +95 -13
- package/src/messages/__tests__/anthropicToolCache.test.ts +46 -0
- package/src/messages/anthropicToolCache.ts +70 -25
- package/src/messages/format.ts +117 -18
- package/src/messages/formatAgentMessages.test.ts +202 -1
- package/src/specs/summarization.test.ts +3 -3
- package/src/tools/__tests__/LocalExecutionRoots.test.ts +8 -0
- package/src/tools/local/LocalExecutionEngine.ts +55 -54
- package/src/types/diff.d.ts +15 -0
|
@@ -5,7 +5,6 @@ import { mkdir, realpath, rm, writeFile } from 'fs/promises';
|
|
|
5
5
|
import { createWriteStream } from 'fs';
|
|
6
6
|
import { spawn } from 'child_process';
|
|
7
7
|
import type { ChildProcess } from 'child_process';
|
|
8
|
-
import type { SandboxRuntimeConfig } from '@anthropic-ai/sandbox-runtime';
|
|
9
8
|
import { runBashAstChecks, bashAstFindingsToErrors } from './bashAst';
|
|
10
9
|
import { nodeWorkspaceFS } from './workspaceFS';
|
|
11
10
|
import type { WorkspaceFS } from './workspaceFS';
|
|
@@ -188,8 +187,17 @@ type RuntimeCommand = {
|
|
|
188
187
|
source?: string;
|
|
189
188
|
};
|
|
190
189
|
|
|
191
|
-
type
|
|
192
|
-
|
|
190
|
+
type SandboxManagerType = {
|
|
191
|
+
checkDependencies(): { errors: string[] };
|
|
192
|
+
initialize(config: BuiltSandboxRuntimeConfig): Promise<void>;
|
|
193
|
+
reset(): Promise<void>;
|
|
194
|
+
wrapWithSandbox(command: string): Promise<string>;
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
type SandboxRuntimeModule = {
|
|
198
|
+
getDefaultWritePaths(): string[];
|
|
199
|
+
SandboxManager: SandboxManagerType;
|
|
200
|
+
};
|
|
193
201
|
|
|
194
202
|
let sandboxConfigKey: string | undefined;
|
|
195
203
|
let sandboxInitialized = false;
|
|
@@ -229,9 +237,7 @@ export function getLocalCwd(config?: t.LocalExecutionConfig): string {
|
|
|
229
237
|
* Returns plain absolute paths — callers symlink-resolve when they
|
|
230
238
|
* need realpath equality (see `resolveWorkspacePathSafe`).
|
|
231
239
|
*/
|
|
232
|
-
export function getWorkspaceRoots(
|
|
233
|
-
config?: t.LocalExecutionConfig
|
|
234
|
-
): string[] {
|
|
240
|
+
export function getWorkspaceRoots(config?: t.LocalExecutionConfig): string[] {
|
|
235
241
|
const root = getLocalCwd(config);
|
|
236
242
|
const extras = config?.workspace?.additionalRoots ?? [];
|
|
237
243
|
if (extras.length === 0) return [root];
|
|
@@ -258,9 +264,7 @@ export function getWorkspaceRoots(
|
|
|
258
264
|
* back to the legacy top-level `local.spawn`, then to Node's
|
|
259
265
|
* `child_process.spawn`. Centralised so engine swapping is one knob.
|
|
260
266
|
*/
|
|
261
|
-
export function getSpawn(
|
|
262
|
-
config?: t.LocalExecutionConfig
|
|
263
|
-
): t.LocalSpawn {
|
|
267
|
+
export function getSpawn(config?: t.LocalExecutionConfig): t.LocalSpawn {
|
|
264
268
|
return (config?.exec?.spawn ?? config?.spawn ?? spawn) as t.LocalSpawn;
|
|
265
269
|
}
|
|
266
270
|
|
|
@@ -269,9 +273,7 @@ export function getSpawn(
|
|
|
269
273
|
* to the Node-host implementation. A future remote engine supplies
|
|
270
274
|
* its own implementation here and inherits every file-touching tool.
|
|
271
275
|
*/
|
|
272
|
-
export function getWorkspaceFS(
|
|
273
|
-
config?: t.LocalExecutionConfig
|
|
274
|
-
): WorkspaceFS {
|
|
276
|
+
export function getWorkspaceFS(config?: t.LocalExecutionConfig): WorkspaceFS {
|
|
275
277
|
return config?.exec?.fs ?? nodeWorkspaceFS;
|
|
276
278
|
}
|
|
277
279
|
|
|
@@ -282,17 +284,17 @@ export function getWorkspaceFS(
|
|
|
282
284
|
* helpers interpret as "skip the write clamp".
|
|
283
285
|
*/
|
|
284
286
|
export function getWriteRoots(
|
|
285
|
-
config
|
|
287
|
+
config: t.LocalExecutionConfig = {}
|
|
286
288
|
): string[] | null {
|
|
287
289
|
// Granular flag wins over the legacy one when explicitly set
|
|
288
290
|
// (true OR false) — otherwise a host tightening access during
|
|
289
291
|
// migration (`allowOutsideWorkspace: true, workspace.
|
|
290
292
|
// allowWriteOutside: false`) would still get the loose behavior
|
|
291
293
|
// because the legacy flag short-circuited the OR. Codex P1 #36.
|
|
292
|
-
const granular = config
|
|
294
|
+
const granular = config.workspace?.allowWriteOutside;
|
|
293
295
|
if (granular === true) return null;
|
|
294
296
|
if (granular === false) return getWorkspaceRoots(config);
|
|
295
|
-
if (config
|
|
297
|
+
if (config.allowOutsideWorkspace === true) return null;
|
|
296
298
|
return getWorkspaceRoots(config);
|
|
297
299
|
}
|
|
298
300
|
|
|
@@ -302,14 +304,14 @@ export function getWriteRoots(
|
|
|
302
304
|
* `allowOutsideWorkspace`) by returning `null`.
|
|
303
305
|
*/
|
|
304
306
|
export function getReadRoots(
|
|
305
|
-
config
|
|
307
|
+
config: t.LocalExecutionConfig = {}
|
|
306
308
|
): string[] | null {
|
|
307
309
|
// Same precedence as getWriteRoots: granular flag is authoritative
|
|
308
310
|
// when set, legacy flag is the fallback. Codex P1 #36.
|
|
309
|
-
const granular = config
|
|
311
|
+
const granular = config.workspace?.allowReadOutside;
|
|
310
312
|
if (granular === true) return null;
|
|
311
313
|
if (granular === false) return getWorkspaceRoots(config);
|
|
312
|
-
if (config
|
|
314
|
+
if (config.allowOutsideWorkspace === true) return null;
|
|
313
315
|
return getWorkspaceRoots(config);
|
|
314
316
|
}
|
|
315
317
|
|
|
@@ -323,10 +325,13 @@ const missingSandboxRuntimeMessage = [
|
|
|
323
325
|
'Local sandbox is enabled, but @anthropic-ai/sandbox-runtime is not installed.',
|
|
324
326
|
'Install it with `npm install @anthropic-ai/sandbox-runtime`, or disable local sandboxing with `local.sandbox.enabled: false`.',
|
|
325
327
|
].join(' ');
|
|
328
|
+
const sandboxRuntimePackage = '@anthropic-ai/sandbox-runtime';
|
|
326
329
|
|
|
327
330
|
/** Lazy-loads the ESM-only sandbox runtime only when sandboxing is enabled. */
|
|
328
331
|
function loadSandboxRuntime(): Promise<SandboxRuntimeModule> {
|
|
329
|
-
sandboxRuntimePromise ??= import(
|
|
332
|
+
sandboxRuntimePromise ??= import(
|
|
333
|
+
sandboxRuntimePackage
|
|
334
|
+
) as Promise<SandboxRuntimeModule>;
|
|
330
335
|
return sandboxRuntimePromise;
|
|
331
336
|
}
|
|
332
337
|
|
|
@@ -487,7 +492,9 @@ export async function validateBashCommand(
|
|
|
487
492
|
}
|
|
488
493
|
|
|
489
494
|
if (config.readOnly === true && mutatingCommandPattern.test(normalized)) {
|
|
490
|
-
errors.push(
|
|
495
|
+
errors.push(
|
|
496
|
+
'Command appears to mutate files or repository state in read-only local mode.'
|
|
497
|
+
);
|
|
491
498
|
}
|
|
492
499
|
|
|
493
500
|
// Use the same shell the actual execution path will use. Hard-coding
|
|
@@ -504,12 +511,14 @@ export async function validateBashCommand(
|
|
|
504
511
|
sandbox: { enabled: false },
|
|
505
512
|
},
|
|
506
513
|
{ internal: true }
|
|
507
|
-
).catch(
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
514
|
+
).catch(
|
|
515
|
+
(error: Error): SpawnResult => ({
|
|
516
|
+
stdout: '',
|
|
517
|
+
stderr: error.message,
|
|
518
|
+
exitCode: 1,
|
|
519
|
+
timedOut: false,
|
|
520
|
+
})
|
|
521
|
+
);
|
|
513
522
|
|
|
514
523
|
if (syntax.exitCode !== 0) {
|
|
515
524
|
errors.push(
|
|
@@ -567,14 +576,7 @@ async function ensureSandbox(
|
|
|
567
576
|
await runtime.SandboxManager.reset();
|
|
568
577
|
}
|
|
569
578
|
|
|
570
|
-
|
|
571
|
-
// is intentionally structural to keep the optional peer dep out of
|
|
572
|
-
// generated `.d.ts` (Codex P1 #22). It's a structural subset of the
|
|
573
|
-
// peer's `SandboxRuntimeConfig`, so the assignment is sound at the
|
|
574
|
-
// one site where the peer is actually loaded.
|
|
575
|
-
await runtime.SandboxManager.initialize(
|
|
576
|
-
runtimeConfig as unknown as SandboxRuntimeConfig
|
|
577
|
-
);
|
|
579
|
+
await runtime.SandboxManager.initialize(runtimeConfig);
|
|
578
580
|
sandboxInitialized = true;
|
|
579
581
|
sandboxConfigKey = nextKey;
|
|
580
582
|
return runtime.SandboxManager;
|
|
@@ -715,8 +717,7 @@ export async function spawnLocalProcess(
|
|
|
715
717
|
// so a process producing unbounded output gets stopped instead of
|
|
716
718
|
// letting the host OOM.
|
|
717
719
|
const inMemoryCapBytes = maxOutputChars * 2;
|
|
718
|
-
const hardKillBytes =
|
|
719
|
-
config.maxSpawnedBytes ?? DEFAULT_MAX_SPAWNED_BYTES;
|
|
720
|
+
const hardKillBytes = config.maxSpawnedBytes ?? DEFAULT_MAX_SPAWNED_BYTES;
|
|
720
721
|
const sandboxManager = await ensureSandbox(config, cwd);
|
|
721
722
|
// Internal probes (validateBashCommand syntax preflight,
|
|
722
723
|
// isRipgrepAvailable, syntax-check probe cache priming) pass
|
|
@@ -775,10 +776,7 @@ export async function spawnLocalProcess(
|
|
|
775
776
|
spillStream.write('\n===== overflow stream begins here =====\n');
|
|
776
777
|
};
|
|
777
778
|
|
|
778
|
-
const handleChunk = (
|
|
779
|
-
buf: Buffer,
|
|
780
|
-
kind: 'stdout' | 'stderr'
|
|
781
|
-
): void => {
|
|
779
|
+
const handleChunk = (buf: Buffer, kind: 'stdout' | 'stderr'): void => {
|
|
782
780
|
totalSpawnedBytes += buf.length;
|
|
783
781
|
// hardKillBytes <= 0 means "no cap" per the public config contract
|
|
784
782
|
// (see LocalExecutionConfig.maxSpawnedBytes). Skip the kill check
|
|
@@ -857,11 +855,11 @@ export async function spawnLocalProcess(
|
|
|
857
855
|
}, timeoutMs);
|
|
858
856
|
}
|
|
859
857
|
|
|
860
|
-
child.stdout
|
|
858
|
+
child.stdout.on('data', (chunk: Buffer) => {
|
|
861
859
|
handleChunk(chunk, 'stdout');
|
|
862
860
|
});
|
|
863
861
|
|
|
864
|
-
child.stderr
|
|
862
|
+
child.stderr.on('data', (chunk: Buffer) => {
|
|
865
863
|
handleChunk(chunk, 'stderr');
|
|
866
864
|
});
|
|
867
865
|
|
|
@@ -928,8 +926,7 @@ export async function executeLocalBash(
|
|
|
928
926
|
* Codex P1 [45], extended for dot-glob in Codex P1 [47] (mirrors the
|
|
929
927
|
* `DESTRUCTIVE_TARGET` suffix matrix exactly).
|
|
930
928
|
*/
|
|
931
|
-
const PROTECTED_TARGET_ARG_RE =
|
|
932
|
-
/^(?:\/|~|\$\{?HOME\}?|\.)(?:\/?\.?\*|\/)?$/;
|
|
929
|
+
const PROTECTED_TARGET_ARG_RE = /^(?:\/|~|\$\{?HOME\}?|\.)(?:\/?\.?\*|\/)?$/;
|
|
933
930
|
|
|
934
931
|
/**
|
|
935
932
|
* Mutating-op recognizer for the args check. Conservative: only the
|
|
@@ -971,11 +968,7 @@ export async function executeLocalBashWithArgs(
|
|
|
971
968
|
}
|
|
972
969
|
}
|
|
973
970
|
const shell = config.shell ?? DEFAULT_SHELL;
|
|
974
|
-
return spawnLocalProcess(
|
|
975
|
-
shell,
|
|
976
|
-
['-lc', command, '--', ...args],
|
|
977
|
-
config
|
|
978
|
-
);
|
|
971
|
+
return spawnLocalProcess(shell, ['-lc', command, '--', ...args], config);
|
|
979
972
|
}
|
|
980
973
|
|
|
981
974
|
export async function executeLocalCode(
|
|
@@ -1009,7 +1002,11 @@ export async function executeLocalCode(
|
|
|
1009
1002
|
config.shell
|
|
1010
1003
|
);
|
|
1011
1004
|
if (runtime.source != null) {
|
|
1012
|
-
await writeFile(
|
|
1005
|
+
await writeFile(
|
|
1006
|
+
resolve(tempDir, runtime.fileName),
|
|
1007
|
+
runtime.source,
|
|
1008
|
+
'utf8'
|
|
1009
|
+
);
|
|
1013
1010
|
}
|
|
1014
1011
|
return await spawnLocalProcess(runtime.command, runtime.args, config);
|
|
1015
1012
|
} finally {
|
|
@@ -1205,7 +1202,7 @@ function killProcessTree(child: ChildProcess): void {
|
|
|
1205
1202
|
// window. Use unref() so the timer doesn't keep the Node process
|
|
1206
1203
|
// alive past the parent's natural exit.
|
|
1207
1204
|
const escalation = setTimeout(() => sigkill(child), SIGKILL_ESCALATION_MS);
|
|
1208
|
-
escalation.unref
|
|
1205
|
+
escalation.unref();
|
|
1209
1206
|
child.once('close', () => clearTimeout(escalation));
|
|
1210
1207
|
}
|
|
1211
1208
|
|
|
@@ -1225,9 +1222,12 @@ export function resolveWorkspacePath(
|
|
|
1225
1222
|
intent: 'read' | 'write' = 'write'
|
|
1226
1223
|
): string {
|
|
1227
1224
|
const cwd = getLocalCwd(config);
|
|
1228
|
-
const absolutePath = isAbsolute(filePath)
|
|
1225
|
+
const absolutePath = isAbsolute(filePath)
|
|
1226
|
+
? resolve(filePath)
|
|
1227
|
+
: resolve(cwd, filePath);
|
|
1229
1228
|
|
|
1230
|
-
const roots =
|
|
1229
|
+
const roots =
|
|
1230
|
+
intent === 'write' ? getWriteRoots(config) : getReadRoots(config);
|
|
1231
1231
|
if (roots == null) return absolutePath; // explicit allow-outside
|
|
1232
1232
|
|
|
1233
1233
|
if (absolutePath === cwd || isInsideAnyRoot(absolutePath, roots)) {
|
|
@@ -1306,7 +1306,8 @@ export async function resolveWorkspacePathSafe(
|
|
|
1306
1306
|
intent: 'read' | 'write' = 'write'
|
|
1307
1307
|
): Promise<string> {
|
|
1308
1308
|
const lexical = resolveWorkspacePath(filePath, config, intent);
|
|
1309
|
-
const roots =
|
|
1309
|
+
const roots =
|
|
1310
|
+
intent === 'write' ? getWriteRoots(config) : getReadRoots(config);
|
|
1310
1311
|
if (roots == null) {
|
|
1311
1312
|
return lexical;
|
|
1312
1313
|
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
declare module 'diff' {
|
|
2
|
+
export type PatchOptions = {
|
|
3
|
+
context?: number;
|
|
4
|
+
};
|
|
5
|
+
|
|
6
|
+
export function createTwoFilesPatch(
|
|
7
|
+
oldFileName: string,
|
|
8
|
+
newFileName: string,
|
|
9
|
+
oldStr: string,
|
|
10
|
+
newStr: string,
|
|
11
|
+
oldHeader?: string,
|
|
12
|
+
newHeader?: string,
|
|
13
|
+
options?: PatchOptions
|
|
14
|
+
): string;
|
|
15
|
+
}
|