@librechat/agents 3.8.6 → 3.8.7
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/main.cjs +1 -0
- package/dist/cjs/tools/BashExecutor.cjs +79 -27
- package/dist/cjs/tools/BashExecutor.cjs.map +1 -1
- package/dist/cjs/tools/BashProgrammaticToolCalling.cjs +47 -7
- package/dist/cjs/tools/BashProgrammaticToolCalling.cjs.map +1 -1
- package/dist/cjs/tools/CodeExecutor.cjs +5 -2
- package/dist/cjs/tools/CodeExecutor.cjs.map +1 -1
- package/dist/cjs/tools/CodeSessionFileSummary.cjs +11 -1
- package/dist/cjs/tools/CodeSessionFileSummary.cjs.map +1 -1
- package/dist/cjs/tools/ProgrammaticToolCalling.cjs +54 -8
- package/dist/cjs/tools/ProgrammaticToolCalling.cjs.map +1 -1
- package/dist/cjs/tools/ToolNode.cjs +52 -20
- package/dist/cjs/tools/ToolNode.cjs.map +1 -1
- package/dist/cjs/tools/toolBatchReplay.cjs +3 -1
- package/dist/cjs/tools/toolBatchReplay.cjs.map +1 -1
- package/dist/esm/main.mjs +2 -2
- package/dist/esm/tools/BashExecutor.mjs +81 -29
- package/dist/esm/tools/BashExecutor.mjs.map +1 -1
- package/dist/esm/tools/BashProgrammaticToolCalling.mjs +48 -9
- package/dist/esm/tools/BashProgrammaticToolCalling.mjs.map +1 -1
- package/dist/esm/tools/CodeExecutor.mjs +5 -2
- package/dist/esm/tools/CodeExecutor.mjs.map +1 -1
- package/dist/esm/tools/CodeSessionFileSummary.mjs +11 -2
- package/dist/esm/tools/CodeSessionFileSummary.mjs.map +1 -1
- package/dist/esm/tools/ProgrammaticToolCalling.mjs +55 -9
- package/dist/esm/tools/ProgrammaticToolCalling.mjs.map +1 -1
- package/dist/esm/tools/ToolNode.mjs +52 -20
- package/dist/esm/tools/ToolNode.mjs.map +1 -1
- package/dist/esm/tools/toolBatchReplay.mjs +3 -1
- package/dist/esm/tools/toolBatchReplay.mjs.map +1 -1
- package/dist/types/tools/BashExecutor.d.ts +2 -0
- package/dist/types/tools/BashProgrammaticToolCalling.d.ts +1 -0
- package/dist/types/tools/CodeExecutor.d.ts +1 -1
- package/dist/types/tools/CodeSessionFileSummary.d.ts +3 -0
- package/dist/types/tools/ProgrammaticToolCalling.d.ts +2 -2
- package/dist/types/tools/ToolNode.d.ts +2 -0
- package/dist/types/tools/toolBatchReplay.d.ts +2 -0
- package/dist/types/types/tools.d.ts +16 -2
- package/package.json +2 -2
- package/src/tools/BashExecutor.ts +143 -40
- package/src/tools/BashProgrammaticToolCalling.ts +82 -10
- package/src/tools/CodeExecutor.ts +12 -2
- package/src/tools/CodeSessionFileSummary.ts +25 -1
- package/src/tools/ProgrammaticToolCalling.ts +77 -6
- package/src/tools/ToolNode.ts +152 -26
- package/src/tools/toolBatchReplay.ts +23 -1
- package/src/types/tools.ts +19 -2
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// src/tools/ProgrammaticToolCalling.ts
|
|
2
2
|
import { config } from 'dotenv';
|
|
3
|
+
import { randomUUID } from 'node:crypto';
|
|
3
4
|
import fetch, { RequestInit } from 'node-fetch';
|
|
4
5
|
import { tool, DynamicStructuredTool } from '@langchain/core/tools';
|
|
5
6
|
import type { ToolCall } from '@langchain/core/messages/tool';
|
|
@@ -21,6 +22,7 @@ import {
|
|
|
21
22
|
resolveCodeApiAuthHeaders,
|
|
22
23
|
selectRuntimeSessionHint,
|
|
23
24
|
} from './CodeExecutor';
|
|
25
|
+
import { appendExecutionArtifactFileSummary } from './CodeSessionFileSummary';
|
|
24
26
|
import {
|
|
25
27
|
assertUnambiguousIdentifiers,
|
|
26
28
|
projectProgrammaticToolMap,
|
|
@@ -51,6 +53,9 @@ config();
|
|
|
51
53
|
const DEFAULT_MAX_ROUND_TRIPS = 20;
|
|
52
54
|
|
|
53
55
|
const DEFAULT_RUN_TIMEOUT_MS = resolveCodeApiRunTimeoutMs();
|
|
56
|
+
const CODE_API_PROGRAMMATIC_REQUEST_HEADER =
|
|
57
|
+
'X-LibreChat-Code-Request-ID';
|
|
58
|
+
const CANCELLATION_REQUEST_TIMEOUT_MS = 2_000;
|
|
54
59
|
|
|
55
60
|
// ============================================================================
|
|
56
61
|
// Description Components (Single Source of Truth)
|
|
@@ -513,10 +518,12 @@ export async function makeRequest(
|
|
|
513
518
|
body: Record<string, unknown>,
|
|
514
519
|
proxy?: string,
|
|
515
520
|
authHeaders?: t.CodeApiAuthHeaders,
|
|
516
|
-
executionProfile?: t.CodeApiExecutionProfile
|
|
521
|
+
executionProfile?: t.CodeApiExecutionProfile,
|
|
522
|
+
signal?: AbortSignal
|
|
517
523
|
): Promise<t.ProgrammaticExecutionResponse> {
|
|
518
524
|
try {
|
|
519
525
|
const resolvedAuthHeaders = await resolveCodeApiAuthHeaders(authHeaders);
|
|
526
|
+
const requestId = signal != null ? randomUUID() : undefined;
|
|
520
527
|
const fetchOptions: RequestInit = {
|
|
521
528
|
method: 'POST',
|
|
522
529
|
headers: {
|
|
@@ -526,8 +533,12 @@ export async function makeRequest(
|
|
|
526
533
|
resolvedAuthHeaders,
|
|
527
534
|
executionProfile
|
|
528
535
|
),
|
|
536
|
+
...(requestId != null
|
|
537
|
+
? { [CODE_API_PROGRAMMATIC_REQUEST_HEADER]: requestId }
|
|
538
|
+
: {}),
|
|
529
539
|
},
|
|
530
540
|
body: JSON.stringify(body),
|
|
541
|
+
signal: signal as RequestInit['signal'],
|
|
531
542
|
};
|
|
532
543
|
|
|
533
544
|
const proxyAgent = resolveFetchProxyAgent(endpoint, proxy);
|
|
@@ -535,7 +546,59 @@ export async function makeRequest(
|
|
|
535
546
|
fetchOptions.agent = proxyAgent;
|
|
536
547
|
}
|
|
537
548
|
|
|
538
|
-
|
|
549
|
+
let cancellationRequest: Promise<void> | undefined;
|
|
550
|
+
const requestCancellation = (): void => {
|
|
551
|
+
if (requestId == null || cancellationRequest != null) {
|
|
552
|
+
return;
|
|
553
|
+
}
|
|
554
|
+
cancellationRequest = (async (): Promise<void> => {
|
|
555
|
+
const cancellationController = new AbortController();
|
|
556
|
+
const timeout = setTimeout(
|
|
557
|
+
() => cancellationController.abort(),
|
|
558
|
+
CANCELLATION_REQUEST_TIMEOUT_MS
|
|
559
|
+
);
|
|
560
|
+
try {
|
|
561
|
+
const cancelOptions: RequestInit = {
|
|
562
|
+
method: 'POST',
|
|
563
|
+
headers: {
|
|
564
|
+
'Content-Type': 'application/json',
|
|
565
|
+
'User-Agent': 'LibreChat/1.0',
|
|
566
|
+
...addCodeApiExecutionProfileHeader(
|
|
567
|
+
resolvedAuthHeaders,
|
|
568
|
+
executionProfile
|
|
569
|
+
),
|
|
570
|
+
},
|
|
571
|
+
body: JSON.stringify({ request_id: requestId }),
|
|
572
|
+
signal: cancellationController.signal as RequestInit['signal'],
|
|
573
|
+
};
|
|
574
|
+
if (proxyAgent != null) {
|
|
575
|
+
cancelOptions.agent = proxyAgent;
|
|
576
|
+
}
|
|
577
|
+
await fetch(`${endpoint}/cancel`, cancelOptions);
|
|
578
|
+
} catch {
|
|
579
|
+
// Cancellation is best effort for older Code API versions and
|
|
580
|
+
// transient network failures. The original abort remains the public
|
|
581
|
+
// result while the server-side execution deadline is the fallback.
|
|
582
|
+
} finally {
|
|
583
|
+
clearTimeout(timeout);
|
|
584
|
+
}
|
|
585
|
+
})();
|
|
586
|
+
};
|
|
587
|
+
signal?.addEventListener('abort', requestCancellation, { once: true });
|
|
588
|
+
if (signal?.aborted === true) requestCancellation();
|
|
589
|
+
|
|
590
|
+
let response;
|
|
591
|
+
try {
|
|
592
|
+
response = await fetch(endpoint, fetchOptions);
|
|
593
|
+
} catch (error) {
|
|
594
|
+
if (signal?.aborted === true) {
|
|
595
|
+
requestCancellation();
|
|
596
|
+
await cancellationRequest;
|
|
597
|
+
}
|
|
598
|
+
throw error;
|
|
599
|
+
} finally {
|
|
600
|
+
signal?.removeEventListener('abort', requestCancellation);
|
|
601
|
+
}
|
|
539
602
|
|
|
540
603
|
if (!response.ok) {
|
|
541
604
|
throw new CodeApiRequestError(
|
|
@@ -860,7 +923,8 @@ export async function executeTools(
|
|
|
860
923
|
*/
|
|
861
924
|
export function formatCompletedResponse(
|
|
862
925
|
response: t.ProgrammaticExecutionResponse,
|
|
863
|
-
sourceCode = ''
|
|
926
|
+
sourceCode = '',
|
|
927
|
+
filePersistence: 'session' | 'execution' = 'session'
|
|
864
928
|
): [string, t.ProgrammaticExecutionArtifact] {
|
|
865
929
|
let formatted = '';
|
|
866
930
|
|
|
@@ -884,10 +948,15 @@ export function formatCompletedResponse(
|
|
|
884
948
|
);
|
|
885
949
|
|
|
886
950
|
return [
|
|
887
|
-
|
|
951
|
+
filePersistence === 'execution'
|
|
952
|
+
? appendExecutionArtifactFileSummary(outputWithDeliveryWarning, response.files)
|
|
953
|
+
: appendCodeSessionFileSummary(outputWithDeliveryWarning, response.files),
|
|
888
954
|
{
|
|
889
955
|
session_id: response.session_id,
|
|
890
956
|
files: response.files,
|
|
957
|
+
...(response.deleted_files != null
|
|
958
|
+
? { deleted_files: response.deleted_files }
|
|
959
|
+
: {}),
|
|
891
960
|
...(artifactDelivery != null
|
|
892
961
|
? { artifact_delivery: artifactDelivery }
|
|
893
962
|
: {}),
|
|
@@ -1161,7 +1230,8 @@ export function createProgrammaticToolCallingTool(
|
|
|
1161
1230
|
},
|
|
1162
1231
|
proxy,
|
|
1163
1232
|
initParams.authHeaders,
|
|
1164
|
-
initParams.executionProfile
|
|
1233
|
+
initParams.executionProfile,
|
|
1234
|
+
config.signal
|
|
1165
1235
|
);
|
|
1166
1236
|
|
|
1167
1237
|
// ====================================================================
|
|
@@ -1199,7 +1269,8 @@ export function createProgrammaticToolCallingTool(
|
|
|
1199
1269
|
},
|
|
1200
1270
|
proxy,
|
|
1201
1271
|
initParams.authHeaders,
|
|
1202
|
-
initParams.executionProfile
|
|
1272
|
+
initParams.executionProfile,
|
|
1273
|
+
config.signal
|
|
1203
1274
|
);
|
|
1204
1275
|
}
|
|
1205
1276
|
|
package/src/tools/ToolNode.ts
CHANGED
|
@@ -221,6 +221,8 @@ type RunToolBatchContext<T = unknown> = {
|
|
|
221
221
|
batchScopeId?: string;
|
|
222
222
|
/** Batch-local sink for post-substitution args. */
|
|
223
223
|
resolvedArgsByCallId?: ResolvedArgsByCallId;
|
|
224
|
+
/** File identities actually injected into each direct code call. */
|
|
225
|
+
codeSessionBaselineByCallId?: Map<string, ReadonlyMap<string, string>>;
|
|
224
226
|
/**
|
|
225
227
|
* Frozen pre-batch view of the tool-output registry. When supplied,
|
|
226
228
|
* `runTool` resolves `{{tool…turn…}}` placeholders against this
|
|
@@ -651,29 +653,78 @@ function toInjectedFileRef(
|
|
|
651
653
|
|
|
652
654
|
/* Stable file identity = `(storage_session_id, id)`. Same name in
|
|
653
655
|
* different storage sessions are distinct files. */
|
|
654
|
-
function fileIdentityKey(
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
656
|
+
function fileIdentityKey(
|
|
657
|
+
file: {
|
|
658
|
+
storage_session_id?: string;
|
|
659
|
+
id: string;
|
|
660
|
+
},
|
|
661
|
+
fallbackSessionId = ''
|
|
662
|
+
): string {
|
|
663
|
+
return `${file.storage_session_id ?? fallbackSessionId}\0${file.id}`;
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
function codeSessionIdentityByName(
|
|
667
|
+
context:
|
|
668
|
+
| t.ToolCallRequest['codeSessionContext']
|
|
669
|
+
| t.CodeSessionContext
|
|
670
|
+
| undefined
|
|
671
|
+
): ReadonlyMap<string, string> {
|
|
672
|
+
return new Map(
|
|
673
|
+
(context?.files ?? []).map((file) => [
|
|
674
|
+
file.name,
|
|
675
|
+
fileIdentityKey(file, context?.session_id),
|
|
676
|
+
])
|
|
677
|
+
);
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
function storageSessionIdFromIdentity(
|
|
681
|
+
identity: string | undefined,
|
|
682
|
+
fileId: string
|
|
683
|
+
): string | undefined {
|
|
684
|
+
const suffix = `\0${fileId}`;
|
|
685
|
+
return identity != null && identity.endsWith(suffix)
|
|
686
|
+
? identity.slice(0, identity.length - suffix.length)
|
|
687
|
+
: undefined;
|
|
659
688
|
}
|
|
660
689
|
|
|
661
690
|
function updateCodeSession(
|
|
662
691
|
sessions: t.ToolSessionMap,
|
|
663
692
|
sessionKey: string,
|
|
664
693
|
execSessionId: string,
|
|
665
|
-
files: t.FileRefs | undefined
|
|
694
|
+
files: t.FileRefs | undefined,
|
|
695
|
+
deletedFiles: readonly string[] | undefined,
|
|
696
|
+
baselineIdentityByName: ReadonlyMap<string, string> | undefined
|
|
666
697
|
): void {
|
|
667
698
|
const newFiles = files ?? [];
|
|
668
699
|
const existingSession = sessions.get(sessionKey) as
|
|
669
700
|
| t.CodeSessionContext
|
|
670
701
|
| undefined;
|
|
671
|
-
const
|
|
702
|
+
const existingSessionId = existingSession?.session_id ?? '';
|
|
703
|
+
const existingFiles = (existingSession?.files ?? []).map((file) =>
|
|
704
|
+
file.storage_session_id == null
|
|
705
|
+
? { ...file, storage_session_id: existingSessionId }
|
|
706
|
+
: file
|
|
707
|
+
);
|
|
708
|
+
const deletedIdentityByName = new Map<string, string>();
|
|
709
|
+
for (const name of deletedFiles ?? []) {
|
|
710
|
+
const identity = baselineIdentityByName?.get(name);
|
|
711
|
+
if (identity != null) {
|
|
712
|
+
deletedIdentityByName.set(name, identity);
|
|
713
|
+
}
|
|
714
|
+
}
|
|
672
715
|
|
|
716
|
+
/* Absence from `files` is not a deletion signal: older Code API versions
|
|
717
|
+
* and truncated responses can omit otherwise-live refs. Remove only paths
|
|
718
|
+
* explicitly attested by `deleted_files`, and only while the current ref
|
|
719
|
+
* still has the identity injected into that request. */
|
|
673
720
|
if (newFiles.length === 0) {
|
|
674
721
|
sessions.set(sessionKey, {
|
|
675
722
|
session_id: execSessionId,
|
|
676
|
-
files: existingFiles
|
|
723
|
+
files: existingFiles.filter(
|
|
724
|
+
(file) =>
|
|
725
|
+
deletedIdentityByName.get(file.name) !==
|
|
726
|
+
fileIdentityKey(file, existingSessionId)
|
|
727
|
+
),
|
|
677
728
|
lastUpdated: Date.now(),
|
|
678
729
|
});
|
|
679
730
|
return;
|
|
@@ -685,26 +736,46 @@ function updateCodeSession(
|
|
|
685
736
|
const filesWithSession: t.FileRefs = [];
|
|
686
737
|
const newFileNames = new Set<string>();
|
|
687
738
|
const incomingByIdentity = new Map<string, number>();
|
|
739
|
+
const existingIdentities = new Set(
|
|
740
|
+
existingFiles.map((file) => fileIdentityKey(file, existingSessionId))
|
|
741
|
+
);
|
|
688
742
|
for (const file of newFiles) {
|
|
743
|
+
const baselineIdentity = baselineIdentityByName?.get(file.name);
|
|
689
744
|
const withSession = {
|
|
690
745
|
...file,
|
|
691
|
-
storage_session_id:
|
|
746
|
+
storage_session_id:
|
|
747
|
+
file.storage_session_id ??
|
|
748
|
+
storageSessionIdFromIdentity(baselineIdentity, file.id) ??
|
|
749
|
+
execSessionId,
|
|
692
750
|
};
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
751
|
+
const identity = fileIdentityKey(withSession);
|
|
752
|
+
const wasTrackedByRequest = baselineIdentity === identity;
|
|
753
|
+
/* An inherited echo is evidence that a request observed an existing ref,
|
|
754
|
+
* not authority to resurrect one removed by a concurrent sibling. Fresh
|
|
755
|
+
* and modified outputs remain eligible to replace by path below. */
|
|
756
|
+
if (
|
|
757
|
+
file.inherited === true &&
|
|
758
|
+
wasTrackedByRequest &&
|
|
759
|
+
(!existingIdentities.has(identity) ||
|
|
760
|
+
deletedIdentityByName.get(file.name) === identity)
|
|
761
|
+
) {
|
|
762
|
+
continue;
|
|
763
|
+
}
|
|
764
|
+
incomingByIdentity.set(identity, filesWithSession.length);
|
|
697
765
|
newFileNames.add(withSession.name);
|
|
698
766
|
filesWithSession.push(withSession);
|
|
699
767
|
}
|
|
700
768
|
|
|
701
769
|
const filteredExisting: t.FileRefs = [];
|
|
702
770
|
for (const e of existingFiles) {
|
|
703
|
-
const idx = incomingByIdentity.get(fileIdentityKey(e));
|
|
771
|
+
const idx = incomingByIdentity.get(fileIdentityKey(e, existingSessionId));
|
|
704
772
|
if (idx !== undefined) {
|
|
705
773
|
filesWithSession[idx] = { ...e, ...filesWithSession[idx] };
|
|
706
774
|
}
|
|
707
|
-
|
|
775
|
+
const explicitlyDeleted =
|
|
776
|
+
deletedIdentityByName.get(e.name) ===
|
|
777
|
+
fileIdentityKey(e, existingSessionId);
|
|
778
|
+
if (!newFileNames.has(e.name) && !explicitlyDeleted) {
|
|
708
779
|
filteredExisting.push(e);
|
|
709
780
|
}
|
|
710
781
|
}
|
|
@@ -1898,6 +1969,12 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
1898
1969
|
| t.CodeSessionContext
|
|
1899
1970
|
| undefined;
|
|
1900
1971
|
const execSessionId = codeSession?.session_id;
|
|
1972
|
+
if (batchContext.codeSessionBaselineByCallId != null) {
|
|
1973
|
+
batchContext.codeSessionBaselineByCallId.set(
|
|
1974
|
+
call.id ?? '',
|
|
1975
|
+
codeSessionIdentityByName(codeSession)
|
|
1976
|
+
);
|
|
1977
|
+
}
|
|
1901
1978
|
if (execSessionId != null && execSessionId !== '') {
|
|
1902
1979
|
invokeParams = {
|
|
1903
1980
|
...invokeParams,
|
|
@@ -3095,7 +3172,11 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
3095
3172
|
baselineByRequestId: ReadonlyMap<
|
|
3096
3173
|
string,
|
|
3097
3174
|
ReadonlyMap<string, string>
|
|
3098
|
-
>
|
|
3175
|
+
> = new Map(),
|
|
3176
|
+
executionBaselineByRequestId: ReadonlyMap<
|
|
3177
|
+
string,
|
|
3178
|
+
ReadonlyMap<string, string>
|
|
3179
|
+
> = baselineByRequestId
|
|
3099
3180
|
): void {
|
|
3100
3181
|
if (!this.sessions) {
|
|
3101
3182
|
return;
|
|
@@ -3131,7 +3212,9 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
3131
3212
|
this.sessions,
|
|
3132
3213
|
this.codeSessionKey,
|
|
3133
3214
|
execSessionId,
|
|
3134
|
-
artifact?.files
|
|
3215
|
+
artifact?.files,
|
|
3216
|
+
artifact?.deleted_files,
|
|
3217
|
+
executionBaselineByRequestId.get(request.id)
|
|
3135
3218
|
);
|
|
3136
3219
|
}
|
|
3137
3220
|
}
|
|
@@ -3200,6 +3283,10 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
3200
3283
|
outputs: (BaseMessage | Command)[],
|
|
3201
3284
|
config: RunnableConfig,
|
|
3202
3285
|
resolvedArgsByCallId?: ResolvedArgsByCallId,
|
|
3286
|
+
codeSessionBaselineByCallId?: ReadonlyMap<
|
|
3287
|
+
string,
|
|
3288
|
+
ReadonlyMap<string, string>
|
|
3289
|
+
>,
|
|
3203
3290
|
errorOwnership?: ToolErrorOwnership
|
|
3204
3291
|
): Promise<void> {
|
|
3205
3292
|
const ownership = errorOwnership ?? this.looseErrorOwnership;
|
|
@@ -3235,7 +3322,11 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
3235
3322
|
ownership.undispatched.delete(toolCallId);
|
|
3236
3323
|
}
|
|
3237
3324
|
|
|
3238
|
-
if (
|
|
3325
|
+
if (
|
|
3326
|
+
toolMessage.status !== 'error' &&
|
|
3327
|
+
this.sessions &&
|
|
3328
|
+
this.participatesInCodeSession(call.name)
|
|
3329
|
+
) {
|
|
3239
3330
|
const artifact = toolMessage.artifact as
|
|
3240
3331
|
| t.CodeExecutionArtifact
|
|
3241
3332
|
| undefined;
|
|
@@ -3245,7 +3336,9 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
3245
3336
|
this.sessions,
|
|
3246
3337
|
this.codeSessionKey,
|
|
3247
3338
|
execSessionId,
|
|
3248
|
-
artifact?.files
|
|
3339
|
+
artifact?.files,
|
|
3340
|
+
artifact?.deleted_files,
|
|
3341
|
+
codeSessionBaselineByCallId?.get(toolCallId)
|
|
3249
3342
|
);
|
|
3250
3343
|
}
|
|
3251
3344
|
}
|
|
@@ -4112,12 +4205,7 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
4112
4205
|
>(
|
|
4113
4206
|
plan.allRequests.map((request) => [
|
|
4114
4207
|
request.id,
|
|
4115
|
-
|
|
4116
|
-
(request.codeSessionContext?.files ?? []).map((file) => [
|
|
4117
|
-
file.name,
|
|
4118
|
-
fileIdentityKey(file),
|
|
4119
|
-
])
|
|
4120
|
-
),
|
|
4208
|
+
codeSessionIdentityByName(request.codeSessionContext),
|
|
4121
4209
|
])
|
|
4122
4210
|
);
|
|
4123
4211
|
const eagerExecutions: Array<{
|
|
@@ -4319,11 +4407,25 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
4319
4407
|
...flattenedEagerResults,
|
|
4320
4408
|
...dispatchedResults,
|
|
4321
4409
|
];
|
|
4410
|
+
/* Hosts may lazily refresh codeSessionContext while dispatching. The
|
|
4411
|
+
* pre-dispatch snapshot above remains the authority for detecting those
|
|
4412
|
+
* additions, while deletion reconciliation must use the identities the
|
|
4413
|
+
* execution actually received. */
|
|
4414
|
+
const executionBaselineByRequestId = new Map<
|
|
4415
|
+
string,
|
|
4416
|
+
ReadonlyMap<string, string>
|
|
4417
|
+
>(
|
|
4418
|
+
plan.allRequests.map((request) => [
|
|
4419
|
+
request.id,
|
|
4420
|
+
codeSessionIdentityByName(request.codeSessionContext),
|
|
4421
|
+
])
|
|
4422
|
+
);
|
|
4322
4423
|
|
|
4323
4424
|
this.storeCodeSessionFromResults(
|
|
4324
4425
|
results,
|
|
4325
4426
|
requestMap,
|
|
4326
|
-
codeSessionBaselineByRequestId
|
|
4427
|
+
codeSessionBaselineByRequestId,
|
|
4428
|
+
executionBaselineByRequestId
|
|
4327
4429
|
);
|
|
4328
4430
|
|
|
4329
4431
|
for (const result of results) {
|
|
@@ -4993,6 +5095,15 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
4993
5095
|
) {
|
|
4994
5096
|
baseContext.resolvedArgsByCallId.set(call.id, result.resolvedArgs);
|
|
4995
5097
|
}
|
|
5098
|
+
if (
|
|
5099
|
+
result.codeSessionBaseline != null &&
|
|
5100
|
+
baseContext.codeSessionBaselineByCallId != null
|
|
5101
|
+
) {
|
|
5102
|
+
baseContext.codeSessionBaselineByCallId.set(
|
|
5103
|
+
call.id ?? '',
|
|
5104
|
+
new Map(result.codeSessionBaseline)
|
|
5105
|
+
);
|
|
5106
|
+
}
|
|
4996
5107
|
return result;
|
|
4997
5108
|
};
|
|
4998
5109
|
const runOne = async (
|
|
@@ -5029,6 +5140,8 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
5029
5140
|
call.id == null
|
|
5030
5141
|
? undefined
|
|
5031
5142
|
: baseContext.resolvedArgsByCallId?.get(call.id);
|
|
5143
|
+
const codeSessionBaseline =
|
|
5144
|
+
baseContext.codeSessionBaselineByCallId?.get(call.id ?? '');
|
|
5032
5145
|
const result: SettledDirectToolResult = {
|
|
5033
5146
|
proposal: structuredClone({
|
|
5034
5147
|
name: call.name,
|
|
@@ -5042,6 +5155,9 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
5042
5155
|
output,
|
|
5043
5156
|
additionalContexts,
|
|
5044
5157
|
...(resolvedArgs == null ? {} : { resolvedArgs }),
|
|
5158
|
+
...(codeSessionBaseline == null
|
|
5159
|
+
? {}
|
|
5160
|
+
: { codeSessionBaseline: [...codeSessionBaseline.entries()] }),
|
|
5045
5161
|
};
|
|
5046
5162
|
const refMeta = isBaseMessage(output)
|
|
5047
5163
|
? (output.additional_kwargs as t.ToolMessageRefMetadata)
|
|
@@ -5260,6 +5376,10 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
5260
5376
|
* ToolNode cannot read or wipe each other's entries.
|
|
5261
5377
|
*/
|
|
5262
5378
|
const resolvedArgsByCallId = new Map<string, Record<string, unknown>>();
|
|
5379
|
+
const codeSessionBaselineByCallId = new Map<
|
|
5380
|
+
string,
|
|
5381
|
+
ReadonlyMap<string, string>
|
|
5382
|
+
>();
|
|
5263
5383
|
/** Per-invocation error-completion ownership — see `ToolErrorOwnership`. */
|
|
5264
5384
|
const errorOwnership = createToolErrorOwnership();
|
|
5265
5385
|
/**
|
|
@@ -5340,6 +5460,7 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
5340
5460
|
batchScopeId,
|
|
5341
5461
|
preBatchSnapshot: replayInputSnapshot,
|
|
5342
5462
|
resolvedArgsByCallId,
|
|
5463
|
+
codeSessionBaselineByCallId,
|
|
5343
5464
|
errorOwnership,
|
|
5344
5465
|
additionalContextsSink: directAdditionalContexts,
|
|
5345
5466
|
replayBatchKey: sendReplayBatchKey,
|
|
@@ -5368,6 +5489,7 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
5368
5489
|
[sendOutput],
|
|
5369
5490
|
config,
|
|
5370
5491
|
resolvedArgsByCallId,
|
|
5492
|
+
codeSessionBaselineByCallId,
|
|
5371
5493
|
errorOwnership
|
|
5372
5494
|
);
|
|
5373
5495
|
} else {
|
|
@@ -5625,6 +5747,7 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
5625
5747
|
turn,
|
|
5626
5748
|
batchScopeId,
|
|
5627
5749
|
resolvedArgsByCallId,
|
|
5750
|
+
codeSessionBaselineByCallId,
|
|
5628
5751
|
errorOwnership,
|
|
5629
5752
|
preBatchSnapshot,
|
|
5630
5753
|
additionalContextsSink: directAdditionalContexts,
|
|
@@ -5662,6 +5785,7 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
5662
5785
|
unhandledDirectOutputs,
|
|
5663
5786
|
config,
|
|
5664
5787
|
resolvedArgsByCallId,
|
|
5788
|
+
codeSessionBaselineByCallId,
|
|
5665
5789
|
errorOwnership
|
|
5666
5790
|
);
|
|
5667
5791
|
for (let i = 0; i < directCalls.length; i++) {
|
|
@@ -5737,6 +5861,7 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
5737
5861
|
turn,
|
|
5738
5862
|
batchScopeId,
|
|
5739
5863
|
resolvedArgsByCallId,
|
|
5864
|
+
codeSessionBaselineByCallId,
|
|
5740
5865
|
errorOwnership,
|
|
5741
5866
|
preBatchSnapshot,
|
|
5742
5867
|
additionalContextsSink: directAdditionalContexts,
|
|
@@ -5751,6 +5876,7 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
5751
5876
|
toolOutputs,
|
|
5752
5877
|
config,
|
|
5753
5878
|
resolvedArgsByCallId,
|
|
5879
|
+
codeSessionBaselineByCallId,
|
|
5754
5880
|
errorOwnership
|
|
5755
5881
|
);
|
|
5756
5882
|
// Append accumulated additionalContexts as a single
|
|
@@ -32,6 +32,8 @@ export interface SettledToolBatchResult {
|
|
|
32
32
|
output: BaseMessage | Command;
|
|
33
33
|
additionalContexts: string[];
|
|
34
34
|
resolvedArgs?: Record<string, unknown>;
|
|
35
|
+
/** Code-session identities injected into this completed direct call. */
|
|
36
|
+
codeSessionBaseline?: Array<[string, string]>;
|
|
35
37
|
completionHandled?: boolean;
|
|
36
38
|
referenceContent?: string;
|
|
37
39
|
turn?: number;
|
|
@@ -474,6 +476,14 @@ export async function restoreToolBatchReplayState(
|
|
|
474
476
|
throw new Error('Invalid settled tool batch results');
|
|
475
477
|
}
|
|
476
478
|
const result = entry[1] as Partial<SettledToolBatchResult> | null;
|
|
479
|
+
const codeSessionBaseline: unknown = (
|
|
480
|
+
result as { codeSessionBaseline?: unknown } | null
|
|
481
|
+
)?.codeSessionBaseline;
|
|
482
|
+
const baselineNames = Array.isArray(codeSessionBaseline)
|
|
483
|
+
? codeSessionBaseline.map((pair) =>
|
|
484
|
+
Array.isArray(pair) ? pair[0] : undefined
|
|
485
|
+
)
|
|
486
|
+
: [];
|
|
477
487
|
if (
|
|
478
488
|
result == null ||
|
|
479
489
|
(!isBaseMessage(result.output) && !isCommand(result.output)) ||
|
|
@@ -491,7 +501,19 @@ export async function restoreToolBatchReplayState(
|
|
|
491
501
|
typeof result.proposal.name !== 'string' ||
|
|
492
502
|
result.proposal.name.length === 0 ||
|
|
493
503
|
!isRecord(result.proposal.args) ||
|
|
494
|
-
(result.resolvedArgs != null && !isRecord(result.resolvedArgs))
|
|
504
|
+
(result.resolvedArgs != null && !isRecord(result.resolvedArgs)) ||
|
|
505
|
+
(codeSessionBaseline != null &&
|
|
506
|
+
(!Array.isArray(codeSessionBaseline) ||
|
|
507
|
+
codeSessionBaseline.some(
|
|
508
|
+
(pair) =>
|
|
509
|
+
!Array.isArray(pair) ||
|
|
510
|
+
pair.length !== 2 ||
|
|
511
|
+
typeof pair[0] !== 'string' ||
|
|
512
|
+
pair[0].length === 0 ||
|
|
513
|
+
typeof pair[1] !== 'string' ||
|
|
514
|
+
pair[1].length === 0
|
|
515
|
+
) ||
|
|
516
|
+
new Set(baselineNames).size !== baselineNames.length))
|
|
495
517
|
) {
|
|
496
518
|
throw new Error('Invalid settled tool batch results');
|
|
497
519
|
}
|
package/src/types/tools.ts
CHANGED
|
@@ -502,6 +502,8 @@ export type ExecuteResult = {
|
|
|
502
502
|
stdout: string;
|
|
503
503
|
stderr: string;
|
|
504
504
|
files?: FileRefs;
|
|
505
|
+
/** Persisted input paths explicitly removed during this execution. */
|
|
506
|
+
deleted_files?: string[];
|
|
505
507
|
artifact_delivery?: ArtifactDeliveryFailure;
|
|
506
508
|
/**
|
|
507
509
|
* Durable runtime session id echoed by a stateful Code API backend
|
|
@@ -1346,6 +1348,8 @@ export type ProgrammaticExecutionResponse = {
|
|
|
1346
1348
|
stdout?: string;
|
|
1347
1349
|
stderr?: string;
|
|
1348
1350
|
files?: FileRefs;
|
|
1351
|
+
/** Persisted input paths explicitly removed during this execution. */
|
|
1352
|
+
deleted_files?: string[];
|
|
1349
1353
|
artifact_delivery?: ArtifactDeliveryFailure;
|
|
1350
1354
|
|
|
1351
1355
|
/** Durable runtime session echo from a stateful backend (additive). */
|
|
@@ -1363,6 +1367,8 @@ export type ProgrammaticExecutionArtifact = {
|
|
|
1363
1367
|
/** Execution session — see `CodeSessionContext.session_id`. */
|
|
1364
1368
|
session_id?: string;
|
|
1365
1369
|
files?: FileRefs;
|
|
1370
|
+
/** Persisted input paths explicitly removed during this execution. */
|
|
1371
|
+
deleted_files?: string[];
|
|
1366
1372
|
artifact_delivery?: ArtifactDeliveryFailure;
|
|
1367
1373
|
/** Durable runtime session echo from a stateful backend (additive). */
|
|
1368
1374
|
runtime_session_id?: string;
|
|
@@ -1370,10 +1376,19 @@ export type ProgrammaticExecutionArtifact = {
|
|
|
1370
1376
|
};
|
|
1371
1377
|
|
|
1372
1378
|
/** Parameters for creating a bash execution tool (same API as CodeExecutor, bash-only) */
|
|
1373
|
-
export type BashExecutionToolParams =
|
|
1379
|
+
export type BashExecutionToolParams =
|
|
1380
|
+
| undefined
|
|
1381
|
+
| (Exclude<CodeExecutionToolParams, undefined> & {
|
|
1382
|
+
/** Trusted attached-workspace identifier selected by the host. */
|
|
1383
|
+
workspaceId?: string;
|
|
1384
|
+
});
|
|
1374
1385
|
|
|
1375
1386
|
/** Parameters for creating a bash programmatic tool calling tool (same API as PTC, bash-only) */
|
|
1376
|
-
export type BashProgrammaticToolCallingParams =
|
|
1387
|
+
export type BashProgrammaticToolCallingParams =
|
|
1388
|
+
ProgrammaticToolCallingParams & {
|
|
1389
|
+
/** Trusted attached-workspace identifier selected by the host. */
|
|
1390
|
+
workspaceId?: string;
|
|
1391
|
+
};
|
|
1377
1392
|
|
|
1378
1393
|
/**
|
|
1379
1394
|
* Initialization parameters for the PTC tool
|
|
@@ -1430,6 +1445,8 @@ export type CodeExecutionArtifact = {
|
|
|
1430
1445
|
/** Execution session — see `CodeSessionContext.session_id`. */
|
|
1431
1446
|
session_id?: string;
|
|
1432
1447
|
files?: FileRefs;
|
|
1448
|
+
/** Persisted input paths explicitly removed during this execution. */
|
|
1449
|
+
deleted_files?: string[];
|
|
1433
1450
|
artifact_delivery?: ArtifactDeliveryFailure;
|
|
1434
1451
|
/** Durable runtime session echo from a stateful backend (additive). */
|
|
1435
1452
|
runtime_session_id?: string;
|