deepagents 1.12.2 → 1.12.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/{agent-pS9QvkWZ.d.ts → agent-BNyBUA4W.d.ts} +109 -8
- package/dist/{agent-DwU6Gs2-.d.cts → agent-DMJjn99p.d.cts} +109 -8
- package/dist/browser.cjs +1 -1
- package/dist/browser.d.cts +1 -1
- package/dist/browser.d.ts +1 -1
- package/dist/browser.js +1 -1
- package/dist/index.cjs +2 -2
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +2 -2
- package/dist/{langsmith-D2d3Dwcc.cjs → langsmith-BJ2PdYqB.cjs} +558 -122
- package/dist/langsmith-BJ2PdYqB.cjs.map +1 -0
- package/dist/{langsmith-b3Dpu8rS.js → langsmith-DRyafCNe.js} +557 -121
- package/dist/langsmith-DRyafCNe.js.map +1 -0
- package/dist/node.cjs +2 -2
- package/dist/node.d.cts +1 -1
- package/dist/node.d.ts +1 -1
- package/dist/node.js +2 -2
- package/dist/{src-DMUJ51B3.js → src-DMHrmLGK.js} +8 -5
- package/dist/{src-DMUJ51B3.js.map → src-DMHrmLGK.js.map} +1 -1
- package/dist/{src-Dkrvbmp1.cjs → src-eTaw7gha.cjs} +8 -5
- package/dist/src-eTaw7gha.cjs.map +1 -0
- package/package.json +1 -1
- package/dist/langsmith-D2d3Dwcc.cjs.map +0 -1
- package/dist/langsmith-b3Dpu8rS.js.map +0 -1
- package/dist/src-Dkrvbmp1.cjs.map +0 -1
|
@@ -302,13 +302,13 @@ function performStringReplacement(content, oldString, newString, replaceAll) {
|
|
|
302
302
|
function truncateIfTooLong(result) {
|
|
303
303
|
if (Array.isArray(result)) {
|
|
304
304
|
const totalChars = result.reduce((sum, item) => sum + item.length, 0);
|
|
305
|
-
if (totalChars >
|
|
305
|
+
if (totalChars > 8e4) {
|
|
306
306
|
const truncateAt = Math.floor(result.length * TOOL_RESULT_TOKEN_LIMIT * 4 / totalChars);
|
|
307
307
|
return [...result.slice(0, truncateAt), TRUNCATION_GUIDANCE];
|
|
308
308
|
}
|
|
309
309
|
return result;
|
|
310
310
|
}
|
|
311
|
-
if (result.length >
|
|
311
|
+
if (result.length > 8e4) return result.substring(0, TOOL_RESULT_TOKEN_LIMIT * 4) + "\n... [results truncated, try being more specific with your parameters]";
|
|
312
312
|
return result;
|
|
313
313
|
}
|
|
314
314
|
/**
|
|
@@ -401,6 +401,30 @@ function globSearchFiles(files, pattern, path = "/") {
|
|
|
401
401
|
return matches.map(([fp]) => fp).join("\n");
|
|
402
402
|
}
|
|
403
403
|
/**
|
|
404
|
+
* Format grep search results based on output mode.
|
|
405
|
+
*
|
|
406
|
+
* @param results - Dictionary mapping file paths to list of [line_num, line_content] tuples
|
|
407
|
+
* @param outputMode - Output format - "files_with_matches", "content", or "count"
|
|
408
|
+
* @returns Formatted string output
|
|
409
|
+
*/
|
|
410
|
+
function formatGrepResults(results, outputMode) {
|
|
411
|
+
if (outputMode === "files_with_matches") return Object.keys(results).sort().join("\n");
|
|
412
|
+
if (outputMode === "count") {
|
|
413
|
+
const lines = [];
|
|
414
|
+
for (const filePath of Object.keys(results).sort()) {
|
|
415
|
+
const count = results[filePath].length;
|
|
416
|
+
lines.push(`${filePath}: ${count}`);
|
|
417
|
+
}
|
|
418
|
+
return lines.join("\n");
|
|
419
|
+
}
|
|
420
|
+
const lines = [];
|
|
421
|
+
for (const filePath of Object.keys(results).sort()) {
|
|
422
|
+
lines.push(`${filePath}:`);
|
|
423
|
+
for (const [lineNum, line] of results[filePath]) lines.push(` ${lineNum}: ${line}`);
|
|
424
|
+
}
|
|
425
|
+
return lines.join("\n");
|
|
426
|
+
}
|
|
427
|
+
/**
|
|
404
428
|
* Return structured grep matches from an in-memory files mapping.
|
|
405
429
|
*
|
|
406
430
|
* Performs literal text search (not regex). Binary files are skipped.
|
|
@@ -431,6 +455,24 @@ function grepMatchesFromFiles(files, pattern, path = null, glob = null) {
|
|
|
431
455
|
return matches;
|
|
432
456
|
}
|
|
433
457
|
/**
|
|
458
|
+
* Group structured matches into the legacy dict form used by formatters.
|
|
459
|
+
*/
|
|
460
|
+
function buildGrepResultsDict(matches) {
|
|
461
|
+
const grouped = {};
|
|
462
|
+
for (const m of matches) {
|
|
463
|
+
if (!grouped[m.path]) grouped[m.path] = [];
|
|
464
|
+
grouped[m.path].push([m.line, m.text]);
|
|
465
|
+
}
|
|
466
|
+
return grouped;
|
|
467
|
+
}
|
|
468
|
+
/**
|
|
469
|
+
* Format structured grep matches using existing formatting logic.
|
|
470
|
+
*/
|
|
471
|
+
function formatGrepMatches(matches, outputMode) {
|
|
472
|
+
if (matches.length === 0) return "No matches found";
|
|
473
|
+
return formatGrepResults(buildGrepResultsDict(matches), outputMode);
|
|
474
|
+
}
|
|
475
|
+
/**
|
|
434
476
|
* Determine MIME type from a file path's extension.
|
|
435
477
|
*
|
|
436
478
|
* Defaults to "text/plain" for unknown extensions. Only the known non-text
|
|
@@ -1421,7 +1463,7 @@ const TOOLS_EXCLUDED_FROM_EVICTION = FILESYSTEM_TOOL_NAMES.filter((name) => name
|
|
|
1421
1463
|
* Base64-encoded content is ~33% larger, so 10MB raw ≈ 13.3MB in context.
|
|
1422
1464
|
* This keeps inline multimodal payloads within all major provider limits.
|
|
1423
1465
|
*/
|
|
1424
|
-
const MAX_BINARY_READ_SIZE_BYTES =
|
|
1466
|
+
const MAX_BINARY_READ_SIZE_BYTES = 10485760;
|
|
1425
1467
|
/**
|
|
1426
1468
|
* Template for truncation message in read_file.
|
|
1427
1469
|
* {file_path} will be filled in at runtime.
|
|
@@ -1510,8 +1552,9 @@ function buildEvictedHumanContent(message, replacementText) {
|
|
|
1510
1552
|
*/
|
|
1511
1553
|
function buildTruncatedHumanMessage(message, filePath) {
|
|
1512
1554
|
const contentSample = createContentPreview(extractTextFromMessage(message));
|
|
1555
|
+
const evictedContent = buildEvictedHumanContent(message, TOO_LARGE_HUMAN_MSG.replace("{file_path}", filePath).replace("{content_sample}", contentSample));
|
|
1513
1556
|
return new HumanMessage({
|
|
1514
|
-
content:
|
|
1557
|
+
content: evictedContent,
|
|
1515
1558
|
id: message.id,
|
|
1516
1559
|
additional_kwargs: { ...message.additional_kwargs },
|
|
1517
1560
|
response_metadata: { ...message.response_metadata }
|
|
@@ -1785,7 +1828,7 @@ function createReadFileTool(backend, options) {
|
|
|
1785
1828
|
const sizeBytes = Math.ceil(base64Data.length * 3 / 4);
|
|
1786
1829
|
if (sizeBytes > 10485760) return [{
|
|
1787
1830
|
type: "text",
|
|
1788
|
-
text: `Error: file too large to read (${Math.round(sizeBytes /
|
|
1831
|
+
text: `Error: file too large to read (${Math.round(sizeBytes / 1048576)}MB exceeds ${MAX_BINARY_READ_SIZE_BYTES / 1048576}MB limit for binary files)`
|
|
1789
1832
|
}];
|
|
1790
1833
|
if (mimeType.startsWith("image/")) return [{
|
|
1791
1834
|
type: "image",
|
|
@@ -1932,23 +1975,14 @@ function createGrepTool(backend, options) {
|
|
|
1932
1975
|
const permissionError = checkPermission(permissions, "read", input.path ?? "/");
|
|
1933
1976
|
if (permissionError !== void 0) return toolError(runtime, "grep", permissionError);
|
|
1934
1977
|
const resolvedBackend = await resolveBackend(backend, runtime);
|
|
1935
|
-
const { pattern, path = "/", glob = null } = input;
|
|
1978
|
+
const { pattern, path = "/", glob = null, output_mode = "content" } = input;
|
|
1936
1979
|
const maxCount = input.max_count ?? grepMaxCount;
|
|
1937
1980
|
const result = await resolvedBackend.grep(pattern, path, glob, maxCount);
|
|
1938
1981
|
if (result.error) return result.error;
|
|
1939
1982
|
const matches = filterByPermissions(result.matches ?? [], permissions, "read", (m) => m.path);
|
|
1940
1983
|
if (matches.length === 0) return `No matches found for pattern '${pattern}'`;
|
|
1941
|
-
const
|
|
1942
|
-
let
|
|
1943
|
-
for (const match of matches) {
|
|
1944
|
-
if (match.path !== currentFile) {
|
|
1945
|
-
currentFile = match.path;
|
|
1946
|
-
lines.push(`\n${currentFile}:`);
|
|
1947
|
-
}
|
|
1948
|
-
lines.push(` ${match.line}: ${match.text}`);
|
|
1949
|
-
}
|
|
1950
|
-
const truncated = truncateIfTooLong(lines);
|
|
1951
|
-
let content = Array.isArray(truncated) ? truncated.join("\n") : truncated;
|
|
1984
|
+
const truncated = truncateIfTooLong(formatGrepMatches(matches, output_mode));
|
|
1985
|
+
let content = typeof truncated === "string" ? truncated : truncated.join("\n");
|
|
1952
1986
|
if (result.truncated) content += `\n\n${GREP_TRUNCATION_NOTE}`;
|
|
1953
1987
|
return content;
|
|
1954
1988
|
}, {
|
|
@@ -1958,7 +1992,12 @@ function createGrepTool(backend, options) {
|
|
|
1958
1992
|
pattern: z.string().describe("Literal text pattern to search for (not regex)"),
|
|
1959
1993
|
path: z.string().optional().default("/").describe("Base path to search from (default: /)"),
|
|
1960
1994
|
glob: z.string().optional().nullable().default(null).describe("Optional glob pattern to filter files (e.g., '*.py')"),
|
|
1961
|
-
max_count: z.number().int().positive().optional().nullable().default(null).describe("Optional cap on the total number of matches returned across all files. Leave unset to use the configured default. When the cap is hit, results are truncated and a note says so; narrow the pattern or path to see the rest.")
|
|
1995
|
+
max_count: z.coerce.number().int().positive().optional().nullable().default(null).describe("Optional cap on the total number of matches returned across all files. Leave unset to use the configured default. When the cap is hit, results are truncated and a note says so; narrow the pattern or path to see the rest."),
|
|
1996
|
+
output_mode: z.enum([
|
|
1997
|
+
"files_with_matches",
|
|
1998
|
+
"content",
|
|
1999
|
+
"count"
|
|
2000
|
+
]).optional().default("content").describe("Output format: 'files_with_matches' lists matching file paths, 'content' shows matching lines (default), 'count' shows match counts per file")
|
|
1962
2001
|
})
|
|
1963
2002
|
});
|
|
1964
2003
|
}
|
|
@@ -2095,9 +2134,10 @@ function createFilesystemMiddleware(options = {}) {
|
|
|
2095
2134
|
const evictPath = `/large_tool_results/${sanitizeToolCallId(fallbackToolCallId || msg.tool_call_id)}.txt`;
|
|
2096
2135
|
const writeResult = await resolvedBackend.write(evictPath, textContent);
|
|
2097
2136
|
const contentSample = createContentPreview(textContent);
|
|
2137
|
+
const replacementText = writeResult.error ? `Tool result too large, but the result could not be saved to the filesystem: ${writeResult.error}` : TOO_LARGE_TOOL_MSG.replace("{tool_call_id}", msg.tool_call_id).replace("{file_path}", evictPath).replace("{content_sample}", contentSample);
|
|
2098
2138
|
return {
|
|
2099
2139
|
message: new ToolMessage({
|
|
2100
|
-
content:
|
|
2140
|
+
content: replacementText,
|
|
2101
2141
|
tool_call_id: msg.tool_call_id,
|
|
2102
2142
|
name: msg.name,
|
|
2103
2143
|
id: msg.id,
|
|
@@ -2214,23 +2254,18 @@ const SUBAGENT_RESPONSE_FORMAT_CONFIG_KEY = "__deepagents_subagent_response_form
|
|
|
2214
2254
|
*/
|
|
2215
2255
|
const DEFAULT_SUBAGENT_PROMPT = "In order to complete the objective that the user asks of you, you have access to a number of standard tools.";
|
|
2216
2256
|
/**
|
|
2217
|
-
* State keys
|
|
2218
|
-
* updates from subagents.
|
|
2219
|
-
*
|
|
2220
|
-
* When returning updates:
|
|
2221
|
-
* 1. The messages key is handled explicitly to ensure only the final message is included
|
|
2222
|
-
* 2. The todos and structuredResponse keys are excluded as they do not have a defined reducer
|
|
2223
|
-
* and no clear meaning for returning them from a subagent to the main agent.
|
|
2224
|
-
* 3. The skillsMetadata and memoryContents keys are automatically excluded from subagent output
|
|
2225
|
-
* to prevent parent state from leaking to child agents. Each agent loads its own skills/memory
|
|
2226
|
-
* independently based on its middleware configuration.
|
|
2257
|
+
* State keys excluded when passing state to subagents and when returning
|
|
2258
|
+
* updates from subagents. Summarization keys are excluded because their
|
|
2259
|
+
* cutoffIndex is only valid against the message list it was computed from.
|
|
2227
2260
|
*/
|
|
2228
2261
|
const EXCLUDED_STATE_KEYS = [
|
|
2229
2262
|
"messages",
|
|
2230
2263
|
"todos",
|
|
2231
2264
|
"structuredResponse",
|
|
2232
2265
|
"skillsMetadata",
|
|
2233
|
-
"memoryContents"
|
|
2266
|
+
"memoryContents",
|
|
2267
|
+
"_summarizationEvent",
|
|
2268
|
+
"_summarizationSessionId"
|
|
2234
2269
|
];
|
|
2235
2270
|
/**
|
|
2236
2271
|
* Default description for the general-purpose subagent.
|
|
@@ -2442,6 +2477,7 @@ function createTaskTool(options) {
|
|
|
2442
2477
|
}
|
|
2443
2478
|
return subagentGraphs[subagentType];
|
|
2444
2479
|
}
|
|
2480
|
+
const finalTaskDescription = taskDescription ? taskDescription : getTaskToolDescription(subagentDescriptions);
|
|
2445
2481
|
return tool(async (input, config) => {
|
|
2446
2482
|
const { description, subagent_type } = input;
|
|
2447
2483
|
if (!(subagent_type in subagentGraphs)) {
|
|
@@ -2451,6 +2487,7 @@ function createTaskTool(options) {
|
|
|
2451
2487
|
const subagent = selectSubagent(subagent_type, config);
|
|
2452
2488
|
const subagentState = filterStateForSubagent(getCurrentTaskInput());
|
|
2453
2489
|
subagentState.messages = [new HumanMessage$1({ content: description })];
|
|
2490
|
+
subagentState._summarizationSessionId = `session_${crypto.randomUUID().substring(0, 8)}`;
|
|
2454
2491
|
const subagentConfig = {
|
|
2455
2492
|
...config,
|
|
2456
2493
|
metadata: {
|
|
@@ -2477,7 +2514,7 @@ function createTaskTool(options) {
|
|
|
2477
2514
|
return returnCommandWithStateUpdate(result, config.toolCall.id);
|
|
2478
2515
|
}, {
|
|
2479
2516
|
name: "task",
|
|
2480
|
-
description:
|
|
2517
|
+
description: finalTaskDescription,
|
|
2481
2518
|
schema: z.object({
|
|
2482
2519
|
description: z.string().describe("The task to execute with the selected agent"),
|
|
2483
2520
|
subagent_type: z.string().describe(`Name of the agent to use. Available: ${Object.keys(subagentGraphs).join(", ")}`)
|
|
@@ -2489,18 +2526,19 @@ function createTaskTool(options) {
|
|
|
2489
2526
|
*/
|
|
2490
2527
|
function createSubAgentMiddleware(options) {
|
|
2491
2528
|
const { defaultModel, defaultTools = [], defaultMiddleware = null, generalPurposeMiddleware = null, defaultInterruptOn = null, subagents = [], systemPrompt = null, generalPurposeAgent = true, taskDescription = null } = options;
|
|
2529
|
+
const taskTool = createTaskTool({
|
|
2530
|
+
defaultModel,
|
|
2531
|
+
defaultTools,
|
|
2532
|
+
defaultMiddleware,
|
|
2533
|
+
generalPurposeMiddleware,
|
|
2534
|
+
defaultInterruptOn,
|
|
2535
|
+
subagents,
|
|
2536
|
+
generalPurposeAgent,
|
|
2537
|
+
taskDescription
|
|
2538
|
+
});
|
|
2492
2539
|
return createMiddleware({
|
|
2493
2540
|
name: "subAgentMiddleware",
|
|
2494
|
-
tools: [
|
|
2495
|
-
defaultModel,
|
|
2496
|
-
defaultTools,
|
|
2497
|
-
defaultMiddleware,
|
|
2498
|
-
generalPurposeMiddleware,
|
|
2499
|
-
defaultInterruptOn,
|
|
2500
|
-
subagents,
|
|
2501
|
-
generalPurposeAgent,
|
|
2502
|
-
taskDescription
|
|
2503
|
-
})],
|
|
2541
|
+
tools: [taskTool],
|
|
2504
2542
|
wrapModelCall: async (request, handler) => {
|
|
2505
2543
|
if (systemPrompt !== null) return handler({
|
|
2506
2544
|
...request,
|
|
@@ -2690,6 +2728,43 @@ function isAnthropicModel(model) {
|
|
|
2690
2728
|
return model.getName() === "ChatAnthropic";
|
|
2691
2729
|
}
|
|
2692
2730
|
/**
|
|
2731
|
+
* A one-shot promise whose settlement is controlled externally.
|
|
2732
|
+
*
|
|
2733
|
+
* Use this when one part of a workflow must wait for an event that is owned
|
|
2734
|
+
* elsewhere—for example, a queued mutation waiting for the worker that will
|
|
2735
|
+
* push it. `Deferred` is awaitable because it implements `PromiseLike`, and
|
|
2736
|
+
* `.promise` is available when a concrete `Promise` is required.
|
|
2737
|
+
*
|
|
2738
|
+
* The first call to `resolve` or `reject` wins; later calls are ignored. This
|
|
2739
|
+
* class deliberately does not provide cancellation, reset, or notification
|
|
2740
|
+
* semantics. It models exactly one eventual outcome.
|
|
2741
|
+
*/
|
|
2742
|
+
var Deferred = class {
|
|
2743
|
+
promise;
|
|
2744
|
+
settled = false;
|
|
2745
|
+
resolvePromise;
|
|
2746
|
+
rejectPromise;
|
|
2747
|
+
constructor() {
|
|
2748
|
+
this.promise = new Promise((resolve, reject) => {
|
|
2749
|
+
this.resolvePromise = resolve;
|
|
2750
|
+
this.rejectPromise = reject;
|
|
2751
|
+
});
|
|
2752
|
+
}
|
|
2753
|
+
resolve(value) {
|
|
2754
|
+
if (this.settled) return;
|
|
2755
|
+
this.settled = true;
|
|
2756
|
+
this.resolvePromise(value);
|
|
2757
|
+
}
|
|
2758
|
+
reject(reason) {
|
|
2759
|
+
if (this.settled) return;
|
|
2760
|
+
this.settled = true;
|
|
2761
|
+
this.rejectPromise(reason);
|
|
2762
|
+
}
|
|
2763
|
+
then(onfulfilled, onrejected) {
|
|
2764
|
+
return this.promise.then(onfulfilled, onrejected);
|
|
2765
|
+
}
|
|
2766
|
+
};
|
|
2767
|
+
/**
|
|
2693
2768
|
* Detect whether a model is an AWS Bedrock Converse model.
|
|
2694
2769
|
*
|
|
2695
2770
|
* Accepts the wider `RunnableInterface` shape (the type of `request.model`
|
|
@@ -3005,7 +3080,7 @@ function createMemoryMiddleware(options) {
|
|
|
3005
3080
|
* });
|
|
3006
3081
|
* ```
|
|
3007
3082
|
*/
|
|
3008
|
-
const MAX_SKILL_FILE_SIZE =
|
|
3083
|
+
const MAX_SKILL_FILE_SIZE = 10485760;
|
|
3009
3084
|
const DEFAULT_SKILL_READ_LINE_LIMIT = 1e3;
|
|
3010
3085
|
const MAX_SKILL_NAME_LENGTH = 64;
|
|
3011
3086
|
const MAX_SKILL_DESCRIPTION_LENGTH = 1024;
|
|
@@ -3768,7 +3843,6 @@ function createCompletionCallbackMiddleware(options) {
|
|
|
3768
3843
|
* from `langchain` directly.
|
|
3769
3844
|
*/
|
|
3770
3845
|
const DEFAULT_MESSAGES_TO_KEEP = 20;
|
|
3771
|
-
const DEFAULT_TRIM_TOKEN_LIMIT = 4e3;
|
|
3772
3846
|
const FALLBACK_TRIGGER = {
|
|
3773
3847
|
type: "tokens",
|
|
3774
3848
|
value: 17e4
|
|
@@ -3885,7 +3959,7 @@ function isSummaryMessage(msg) {
|
|
|
3885
3959
|
* @returns AgentMiddleware for summarization and history offloading
|
|
3886
3960
|
*/
|
|
3887
3961
|
function createSummarizationMiddleware(options) {
|
|
3888
|
-
const { model, backend, summaryPrompt = DEFAULT_SUMMARY_PROMPT, trimTokensToSummarize
|
|
3962
|
+
const { model, backend, summaryPrompt = DEFAULT_SUMMARY_PROMPT, trimTokensToSummarize, historyPathPrefix = "/conversation_history" } = options;
|
|
3889
3963
|
let trigger = options.trigger;
|
|
3890
3964
|
let keep = options.keep ?? {
|
|
3891
3965
|
type: "messages",
|
|
@@ -4089,7 +4163,9 @@ function createSummarizationMiddleware(options) {
|
|
|
4089
4163
|
* This gives a more accurate picture of what actually gets sent to the model.
|
|
4090
4164
|
*/
|
|
4091
4165
|
function countTotalTokens(messages, systemMessage, tools) {
|
|
4092
|
-
|
|
4166
|
+
const countedMessages = systemMessage && SystemMessage.isInstance(systemMessage) ? [systemMessage, ...messages] : [...messages];
|
|
4167
|
+
const toolsArray = tools && Array.isArray(tools) && tools.length > 0 ? tools : null;
|
|
4168
|
+
return countTokensApproximately(countedMessages, toolsArray);
|
|
4093
4169
|
}
|
|
4094
4170
|
/**
|
|
4095
4171
|
* Truncate ToolMessage content so that the total payload fits within the
|
|
@@ -4236,7 +4312,8 @@ function createSummarizationMiddleware(options) {
|
|
|
4236
4312
|
*/
|
|
4237
4313
|
async function createSummary(messages, chatModel) {
|
|
4238
4314
|
let messagesToSummarize = messages;
|
|
4239
|
-
|
|
4315
|
+
const tokens = countTokensApproximately(messages);
|
|
4316
|
+
if (trimTokensToSummarize !== void 0 && tokens > trimTokensToSummarize) {
|
|
4240
4317
|
let kept = 0;
|
|
4241
4318
|
const trimmedMessages = [];
|
|
4242
4319
|
for (let i = messages.length - 1; i >= 0; i--) {
|
|
@@ -4249,8 +4326,7 @@ function createSummarizationMiddleware(options) {
|
|
|
4249
4326
|
}
|
|
4250
4327
|
const conversation = getBufferString(messagesToSummarize);
|
|
4251
4328
|
const prompt = summaryPrompt.replace("{conversation}", conversation);
|
|
4252
|
-
|
|
4253
|
-
return typeof response.content === "string" ? response.content : JSON.stringify(response.content);
|
|
4329
|
+
return (await chatModel.invoke([new HumanMessage({ content: prompt })])).text;
|
|
4254
4330
|
}
|
|
4255
4331
|
/**
|
|
4256
4332
|
* Build the summary message with file path reference.
|
|
@@ -6543,9 +6619,36 @@ var StoreBackend = class {
|
|
|
6543
6619
|
/**
|
|
6544
6620
|
* ContextHubBackend: Store files in a LangSmith Hub agent repo (persistent).
|
|
6545
6621
|
*/
|
|
6546
|
-
const
|
|
6622
|
+
const CONTEXT_URL_COMMIT_PATH_RE = /^\/context\/([^/]+)\/([0-9a-f]{8})$/;
|
|
6623
|
+
const LEGACY_URL_COMMIT_PATH_RE = /^\/hub\/([^/]+)\/([^/:]+):([0-9a-f]{8})$/;
|
|
6624
|
+
const MUTATION_COALESCE_MS = 50;
|
|
6625
|
+
const MAX_CONFLICT_RETRIES = 3;
|
|
6547
6626
|
const TEXT_MIME_TYPE = "text/plain";
|
|
6548
6627
|
const FNMATCH_OPTIONS = { bash: true };
|
|
6628
|
+
function parseHubTargetIdentifier(identifier) {
|
|
6629
|
+
if (!identifier || identifier.split("/").length > 2 || identifier.startsWith("/") || identifier.endsWith("/") || identifier.split(":").length > 2) return null;
|
|
6630
|
+
const [ownerNamePart] = identifier.split(":");
|
|
6631
|
+
if (ownerNamePart.includes("/")) {
|
|
6632
|
+
const [owner, name] = ownerNamePart.split("/", 2);
|
|
6633
|
+
return owner && name ? [owner, name] : null;
|
|
6634
|
+
}
|
|
6635
|
+
return ownerNamePart ? ["-", ownerNamePart] : null;
|
|
6636
|
+
}
|
|
6637
|
+
function parseCommitHashFromUrl(url, identifier) {
|
|
6638
|
+
try {
|
|
6639
|
+
const pathname = decodeURIComponent(new URL(url).pathname);
|
|
6640
|
+
const target = parseHubTargetIdentifier(identifier);
|
|
6641
|
+
if (target === null) return null;
|
|
6642
|
+
const [targetOwner, targetName] = target;
|
|
6643
|
+
const contextMatch = CONTEXT_URL_COMMIT_PATH_RE.exec(pathname);
|
|
6644
|
+
if (contextMatch !== null && contextMatch[1] === targetName) return contextMatch[2];
|
|
6645
|
+
const legacyMatch = LEGACY_URL_COMMIT_PATH_RE.exec(pathname);
|
|
6646
|
+
if (legacyMatch !== null && legacyMatch[1] === targetOwner && legacyMatch[2] === targetName) return legacyMatch[3];
|
|
6647
|
+
return null;
|
|
6648
|
+
} catch {
|
|
6649
|
+
return null;
|
|
6650
|
+
}
|
|
6651
|
+
}
|
|
6549
6652
|
function getErrorMessage(error) {
|
|
6550
6653
|
if (typeof error === "string") return error;
|
|
6551
6654
|
if (typeof error === "object" && error !== null && "message" in error && typeof error.message === "string") return error.message;
|
|
@@ -6584,6 +6687,12 @@ function getLangSmithStatus(error) {
|
|
|
6584
6687
|
const maybeError = error;
|
|
6585
6688
|
if (typeof maybeError.status === "number") return maybeError.status;
|
|
6586
6689
|
}
|
|
6690
|
+
function createLangSmithConflictError(message) {
|
|
6691
|
+
const error = new Error(message);
|
|
6692
|
+
error.name = "LangSmithConflictError";
|
|
6693
|
+
error.status = 409;
|
|
6694
|
+
return error;
|
|
6695
|
+
}
|
|
6587
6696
|
function mapHubFileOperationError(error) {
|
|
6588
6697
|
const status = getLangSmithStatus(error);
|
|
6589
6698
|
if (status === 401 || status === 403) return "permission_denied";
|
|
@@ -6593,12 +6702,49 @@ function mapHubFileOperationError(error) {
|
|
|
6593
6702
|
/**
|
|
6594
6703
|
* Backend that stores files in a LangSmith Hub agent repo (persistent).
|
|
6595
6704
|
*/
|
|
6705
|
+
/**
|
|
6706
|
+
* Backend that stores files in a LangSmith Hub agent repository.
|
|
6707
|
+
*
|
|
6708
|
+
* ## Mutation model
|
|
6709
|
+
*
|
|
6710
|
+
* Mutations are accepted in call order, coalesced for a short window, and
|
|
6711
|
+
* pushed by one worker. Only one batch is in flight at a time; mutations that
|
|
6712
|
+
* arrive during a push form the next batch. This serializes one backend
|
|
6713
|
+
* instance's writes while still reducing the number of Hub commits.
|
|
6714
|
+
*
|
|
6715
|
+
* Reads use an optimistic view: the last durable cache overlaid with the
|
|
6716
|
+
* in-flight batch and then the pending batch. A read can therefore observe an
|
|
6717
|
+
* accepted mutation before it is durable; a failed push invalidates that view
|
|
6718
|
+
* and the next operation reloads from Hub.
|
|
6719
|
+
*
|
|
6720
|
+
* A `409` parent conflict triggers an authoritative pull and rematerializes
|
|
6721
|
+
* the in-flight batch over the fetched tree before retrying. Edits replay their
|
|
6722
|
+
* original replacement intent; absolute writes, deletes, and uploads replay as
|
|
6723
|
+
* absolute changes. Retries are bounded by `MAX_CONFLICT_RETRIES`.
|
|
6724
|
+
*/
|
|
6596
6725
|
var ContextHubBackend = class ContextHubBackend {
|
|
6597
6726
|
identifier;
|
|
6598
6727
|
client;
|
|
6728
|
+
/** Last durable Hub file state; `null` means the next access must load it. */
|
|
6599
6729
|
cache = null;
|
|
6600
6730
|
linkedEntries = {};
|
|
6731
|
+
/** Parent hash for the durable cache, used for optimistic-concurrency pushes. */
|
|
6601
6732
|
commitHash = null;
|
|
6733
|
+
/** Shared cold-load promise so concurrent first operations perform one pull. */
|
|
6734
|
+
loadPromise = null;
|
|
6735
|
+
/** Promise chain serializing mutation acceptance and optimistic projections. */
|
|
6736
|
+
mutationOrder = Promise.resolve();
|
|
6737
|
+
/** Mutations accepted for the next coalesced push. */
|
|
6738
|
+
pendingBatch = null;
|
|
6739
|
+
/** The batch currently submitted to Hub and visible to optimistic reads. */
|
|
6740
|
+
inFlightBatch = null;
|
|
6741
|
+
/** The single queue-draining worker, when active. */
|
|
6742
|
+
workerPromise = null;
|
|
6743
|
+
/**
|
|
6744
|
+
* Blocks cache consumers while a successful push without a parseable commit
|
|
6745
|
+
* hash is being confirmed by an authoritative pull.
|
|
6746
|
+
*/
|
|
6747
|
+
snapshotPublication = null;
|
|
6602
6748
|
constructor(identifier, options = {}) {
|
|
6603
6749
|
this.identifier = identifier;
|
|
6604
6750
|
this.client = options.client ?? new Client$1();
|
|
@@ -6609,49 +6755,319 @@ var ContextHubBackend = class ContextHubBackend {
|
|
|
6609
6755
|
static toHubUnavailableError(error) {
|
|
6610
6756
|
return `Hub unavailable: ${getErrorMessage(error)}`;
|
|
6611
6757
|
}
|
|
6612
|
-
async
|
|
6758
|
+
async fetchTree() {
|
|
6613
6759
|
let context;
|
|
6614
6760
|
try {
|
|
6615
6761
|
context = await this.client.pullAgent(this.identifier);
|
|
6616
6762
|
} catch (error) {
|
|
6617
|
-
if (isLangSmithNotFoundError(error)) {
|
|
6618
|
-
|
|
6619
|
-
|
|
6620
|
-
|
|
6621
|
-
|
|
6622
|
-
}
|
|
6763
|
+
if (isLangSmithNotFoundError(error)) return {
|
|
6764
|
+
cache: {},
|
|
6765
|
+
linkedEntries: {},
|
|
6766
|
+
commitHash: null
|
|
6767
|
+
};
|
|
6623
6768
|
throw error;
|
|
6624
6769
|
}
|
|
6625
|
-
|
|
6626
|
-
|
|
6627
|
-
|
|
6628
|
-
|
|
6629
|
-
|
|
6770
|
+
const cache = {};
|
|
6771
|
+
const linkedEntries = {};
|
|
6772
|
+
for (const [path, entry] of Object.entries(context.files)) if (entry.type === "file") cache[path] = entry.content;
|
|
6773
|
+
else if ((entry.type === "agent" || entry.type === "skill") && typeof entry.repo_handle === "string") linkedEntries[path] = entry.repo_handle;
|
|
6774
|
+
return {
|
|
6775
|
+
cache,
|
|
6776
|
+
linkedEntries,
|
|
6777
|
+
commitHash: context.commit_hash
|
|
6778
|
+
};
|
|
6630
6779
|
}
|
|
6631
|
-
|
|
6632
|
-
|
|
6780
|
+
publishSnapshot(snapshot) {
|
|
6781
|
+
this.cache = snapshot.cache;
|
|
6782
|
+
this.linkedEntries = snapshot.linkedEntries;
|
|
6783
|
+
this.commitHash = snapshot.commitHash;
|
|
6784
|
+
}
|
|
6785
|
+
async loadTree() {
|
|
6786
|
+
this.publishSnapshot(await this.fetchTree());
|
|
6787
|
+
}
|
|
6788
|
+
beginSnapshotPublication() {
|
|
6789
|
+
if (this.snapshotPublication !== null) throw new Error("Context Hub snapshot publication is already pending");
|
|
6790
|
+
this.snapshotPublication = new Deferred();
|
|
6791
|
+
}
|
|
6792
|
+
finishSnapshotPublication() {
|
|
6793
|
+
const publication = this.snapshotPublication;
|
|
6794
|
+
this.snapshotPublication = null;
|
|
6795
|
+
publication?.resolve();
|
|
6796
|
+
}
|
|
6797
|
+
async ensureCacheLoaded() {
|
|
6798
|
+
while (this.snapshotPublication !== null) await this.snapshotPublication;
|
|
6799
|
+
if (this.cache === null) {
|
|
6800
|
+
let loadPromise = this.loadPromise;
|
|
6801
|
+
if (loadPromise === null) {
|
|
6802
|
+
loadPromise = this.loadTree();
|
|
6803
|
+
this.loadPromise = loadPromise;
|
|
6804
|
+
}
|
|
6805
|
+
try {
|
|
6806
|
+
await loadPromise;
|
|
6807
|
+
} finally {
|
|
6808
|
+
if (this.loadPromise === loadPromise) this.loadPromise = null;
|
|
6809
|
+
}
|
|
6810
|
+
}
|
|
6633
6811
|
if (this.cache === null) throw new Error("Context Hub cache failed to initialize");
|
|
6634
|
-
|
|
6635
|
-
|
|
6636
|
-
|
|
6637
|
-
|
|
6638
|
-
|
|
6639
|
-
|
|
6640
|
-
|
|
6641
|
-
|
|
6812
|
+
}
|
|
6813
|
+
async ensureCache() {
|
|
6814
|
+
await this.ensureCacheLoaded();
|
|
6815
|
+
return this.visibleCache();
|
|
6816
|
+
}
|
|
6817
|
+
static applyChanges(cache, changes) {
|
|
6818
|
+
const next = { ...cache };
|
|
6819
|
+
for (const [path, content] of Object.entries(changes)) if (content === null) delete next[path];
|
|
6820
|
+
else next[path] = content;
|
|
6821
|
+
return next;
|
|
6822
|
+
}
|
|
6823
|
+
/**
|
|
6824
|
+
* Build the read-your-writes view without publishing speculative data as the
|
|
6825
|
+
* durable cache. Later batches overlay earlier ones, matching worker order.
|
|
6826
|
+
*/
|
|
6827
|
+
visibleCache() {
|
|
6828
|
+
let visible = { ...this.cache ?? {} };
|
|
6829
|
+
if (this.inFlightBatch !== null) visible = ContextHubBackend.applyChanges(visible, this.inFlightBatch.changes);
|
|
6830
|
+
if (this.pendingBatch !== null) visible = ContextHubBackend.applyChanges(visible, this.pendingBatch.changes);
|
|
6831
|
+
return visible;
|
|
6832
|
+
}
|
|
6833
|
+
invalidateCache() {
|
|
6834
|
+
this.cache = null;
|
|
6835
|
+
this.linkedEntries = {};
|
|
6836
|
+
this.commitHash = null;
|
|
6837
|
+
this.loadPromise = null;
|
|
6838
|
+
}
|
|
6839
|
+
async acquireMutationTurn() {
|
|
6840
|
+
let release;
|
|
6841
|
+
const previous = this.mutationOrder;
|
|
6842
|
+
this.mutationOrder = new Promise((resolve) => {
|
|
6843
|
+
release = resolve;
|
|
6844
|
+
});
|
|
6845
|
+
await previous;
|
|
6846
|
+
return release;
|
|
6847
|
+
}
|
|
6848
|
+
/**
|
|
6849
|
+
* Serialize validation and enqueueing so each operation is evaluated against
|
|
6850
|
+
* a stable optimistic projection. Cache loading begins before acquiring the
|
|
6851
|
+
* turn, allowing concurrent cold-start callers to share the same pull.
|
|
6852
|
+
*/
|
|
6853
|
+
async acceptMutation(operation) {
|
|
6854
|
+
const turn = this.acquireMutationTurn();
|
|
6855
|
+
const cacheOutcome = this.ensureCacheLoaded().then(() => ({ loaded: true }), (error) => ({
|
|
6856
|
+
loaded: false,
|
|
6857
|
+
error
|
|
6858
|
+
}));
|
|
6859
|
+
const release = await turn;
|
|
6860
|
+
try {
|
|
6861
|
+
const outcome = await cacheOutcome;
|
|
6862
|
+
if (!outcome.loaded) throw outcome.error;
|
|
6863
|
+
while (this.cache === null) await this.ensureCacheLoaded();
|
|
6864
|
+
return operation(this.visibleCache());
|
|
6865
|
+
} finally {
|
|
6866
|
+
release();
|
|
6867
|
+
}
|
|
6868
|
+
}
|
|
6869
|
+
/**
|
|
6870
|
+
* Start a batch's coalescing window. The worker waits for this signal before
|
|
6871
|
+
* detaching the batch; cancellation resolves it immediately so failures do
|
|
6872
|
+
* not leave the worker waiting on a timer.
|
|
6873
|
+
*/
|
|
6874
|
+
createMutationBatch() {
|
|
6875
|
+
const batch = {
|
|
6876
|
+
changes: {},
|
|
6877
|
+
waiters: [],
|
|
6878
|
+
ready: new Deferred(),
|
|
6879
|
+
timer: null
|
|
6642
6880
|
};
|
|
6643
|
-
|
|
6644
|
-
|
|
6645
|
-
|
|
6881
|
+
batch.timer = setTimeout(() => {
|
|
6882
|
+
batch.timer = null;
|
|
6883
|
+
batch.ready.resolve();
|
|
6884
|
+
}, MUTATION_COALESCE_MS);
|
|
6885
|
+
return batch;
|
|
6886
|
+
}
|
|
6887
|
+
cancelBatchTimer(batch) {
|
|
6888
|
+
if (batch.timer !== null) {
|
|
6889
|
+
clearTimeout(batch.timer);
|
|
6890
|
+
batch.timer = null;
|
|
6891
|
+
}
|
|
6892
|
+
batch.ready.resolve();
|
|
6893
|
+
}
|
|
6894
|
+
enqueueCommit(changes, intent = {
|
|
6895
|
+
kind: "changes",
|
|
6896
|
+
changes: { ...changes }
|
|
6897
|
+
}) {
|
|
6898
|
+
if (Object.keys(changes).length === 0) return Promise.resolve();
|
|
6899
|
+
let batch = this.pendingBatch;
|
|
6900
|
+
if (batch === null) {
|
|
6901
|
+
batch = this.createMutationBatch();
|
|
6902
|
+
this.pendingBatch = batch;
|
|
6903
|
+
}
|
|
6904
|
+
Object.assign(batch.changes, changes);
|
|
6905
|
+
const completion = new Deferred();
|
|
6906
|
+
batch.waiters.push({
|
|
6907
|
+
intent,
|
|
6908
|
+
completion
|
|
6646
6909
|
});
|
|
6647
|
-
|
|
6648
|
-
|
|
6649
|
-
|
|
6650
|
-
|
|
6651
|
-
|
|
6652
|
-
|
|
6653
|
-
|
|
6654
|
-
|
|
6910
|
+
this.startWorker();
|
|
6911
|
+
return completion.promise;
|
|
6912
|
+
}
|
|
6913
|
+
/**
|
|
6914
|
+
* Replay ordered intents over an authoritative base after a conflict. This
|
|
6915
|
+
* rebuilds the push payload and optimistic overlay. An edit that no longer
|
|
6916
|
+
* applies throws the supplied conflict error; absolute changes are reapplied.
|
|
6917
|
+
*/
|
|
6918
|
+
rematerializeBatch(batch, base, conflictError) {
|
|
6919
|
+
let cache = { ...base };
|
|
6920
|
+
const changes = {};
|
|
6921
|
+
for (const waiter of batch.waiters) {
|
|
6922
|
+
const { intent } = waiter;
|
|
6923
|
+
if (intent.kind === "changes") {
|
|
6924
|
+
Object.assign(changes, intent.changes);
|
|
6925
|
+
cache = ContextHubBackend.applyChanges(cache, intent.changes);
|
|
6926
|
+
continue;
|
|
6927
|
+
}
|
|
6928
|
+
const current = cache[intent.path];
|
|
6929
|
+
if (current === void 0) throw conflictError;
|
|
6930
|
+
const replacementResult = performStringReplacement(current, intent.oldString, intent.newString, intent.replaceAll);
|
|
6931
|
+
if (typeof replacementResult === "string") throw conflictError;
|
|
6932
|
+
const [newContent, occurrences] = replacementResult;
|
|
6933
|
+
const editChanges = { [intent.path]: newContent };
|
|
6934
|
+
Object.assign(changes, editChanges);
|
|
6935
|
+
cache = ContextHubBackend.applyChanges(cache, editChanges);
|
|
6936
|
+
intent.updateOccurrences(occurrences);
|
|
6937
|
+
}
|
|
6938
|
+
batch.changes = changes;
|
|
6939
|
+
return cache;
|
|
6940
|
+
}
|
|
6941
|
+
rematerializeAfterConflict(batch, snapshot, conflictError) {
|
|
6942
|
+
const cache = this.rematerializeBatch(batch, snapshot.cache, conflictError);
|
|
6943
|
+
let pendingReplayError = null;
|
|
6944
|
+
if (this.pendingBatch !== null) try {
|
|
6945
|
+
this.rematerializeBatch(this.pendingBatch, cache, conflictError);
|
|
6946
|
+
} catch (error) {
|
|
6947
|
+
if (error !== conflictError) throw error;
|
|
6948
|
+
pendingReplayError = error;
|
|
6949
|
+
}
|
|
6950
|
+
this.publishSnapshot(snapshot);
|
|
6951
|
+
if (pendingReplayError !== null) this.failPendingBatch(pendingReplayError);
|
|
6952
|
+
}
|
|
6953
|
+
rematerializePendingBatch(snapshot) {
|
|
6954
|
+
if (this.pendingBatch === null) return null;
|
|
6955
|
+
const conflictError = createLangSmithConflictError("Pending Context Hub mutation conflicts with authoritative state");
|
|
6956
|
+
try {
|
|
6957
|
+
this.rematerializeBatch(this.pendingBatch, snapshot.cache, conflictError);
|
|
6958
|
+
return null;
|
|
6959
|
+
} catch (error) {
|
|
6960
|
+
if (error !== conflictError) throw error;
|
|
6961
|
+
return conflictError;
|
|
6962
|
+
}
|
|
6963
|
+
}
|
|
6964
|
+
startWorker() {
|
|
6965
|
+
if (this.workerPromise !== null) return;
|
|
6966
|
+
const worker = this.drainMutationQueue().catch((error) => {
|
|
6967
|
+
this.failAllBatches(error);
|
|
6968
|
+
}).finally(() => {
|
|
6969
|
+
if (this.workerPromise === worker) {
|
|
6970
|
+
this.workerPromise = null;
|
|
6971
|
+
if (this.pendingBatch !== null) this.startWorker();
|
|
6972
|
+
}
|
|
6973
|
+
});
|
|
6974
|
+
this.workerPromise = worker;
|
|
6975
|
+
}
|
|
6976
|
+
/**
|
|
6977
|
+
* Drain coalesced batches sequentially. A completed batch publishes durable
|
|
6978
|
+
* state before settling its callers; a failed batch invalidates local state
|
|
6979
|
+
* and rejects both in-flight and queued callers so the next mutation reloads.
|
|
6980
|
+
*/
|
|
6981
|
+
async drainMutationQueue() {
|
|
6982
|
+
while (this.pendingBatch !== null) {
|
|
6983
|
+
const batch = this.pendingBatch;
|
|
6984
|
+
await batch.ready;
|
|
6985
|
+
if (this.pendingBatch !== batch) continue;
|
|
6986
|
+
this.pendingBatch = null;
|
|
6987
|
+
this.inFlightBatch = batch;
|
|
6988
|
+
let pendingReplayError = null;
|
|
6989
|
+
try {
|
|
6990
|
+
const result = await this.pushBatch(batch);
|
|
6991
|
+
if (result.kind === "snapshot") {
|
|
6992
|
+
pendingReplayError = this.rematerializePendingBatch(result.snapshot);
|
|
6993
|
+
this.publishSnapshot(result.snapshot);
|
|
6994
|
+
} else {
|
|
6995
|
+
this.cache = ContextHubBackend.applyChanges(this.cache ?? {}, batch.changes);
|
|
6996
|
+
this.commitHash = result.commitHash;
|
|
6997
|
+
}
|
|
6998
|
+
} catch (error) {
|
|
6999
|
+
this.inFlightBatch = null;
|
|
7000
|
+
this.invalidateCache();
|
|
7001
|
+
this.finishSnapshotPublication();
|
|
7002
|
+
for (const waiter of batch.waiters) waiter.completion.reject(error);
|
|
7003
|
+
this.failPendingBatch(error);
|
|
7004
|
+
return;
|
|
7005
|
+
}
|
|
7006
|
+
this.inFlightBatch = null;
|
|
7007
|
+
this.finishSnapshotPublication();
|
|
7008
|
+
for (const waiter of batch.waiters) waiter.completion.resolve();
|
|
7009
|
+
if (pendingReplayError !== null) {
|
|
7010
|
+
this.failPendingBatch(pendingReplayError);
|
|
7011
|
+
return;
|
|
7012
|
+
}
|
|
7013
|
+
}
|
|
7014
|
+
}
|
|
7015
|
+
failPendingBatch(error) {
|
|
7016
|
+
const pending = this.pendingBatch;
|
|
7017
|
+
if (pending === null) return;
|
|
7018
|
+
this.pendingBatch = null;
|
|
7019
|
+
this.cancelBatchTimer(pending);
|
|
7020
|
+
for (const waiter of pending.waiters) waiter.completion.reject(error);
|
|
7021
|
+
}
|
|
7022
|
+
failAllBatches(error) {
|
|
7023
|
+
const inFlight = this.inFlightBatch;
|
|
7024
|
+
this.inFlightBatch = null;
|
|
7025
|
+
this.invalidateCache();
|
|
7026
|
+
this.finishSnapshotPublication();
|
|
7027
|
+
if (inFlight !== null) {
|
|
7028
|
+
this.cancelBatchTimer(inFlight);
|
|
7029
|
+
for (const waiter of inFlight.waiters) waiter.completion.reject(error);
|
|
7030
|
+
}
|
|
7031
|
+
this.failPendingBatch(error);
|
|
7032
|
+
}
|
|
7033
|
+
/**
|
|
7034
|
+
* Push a materialized batch with the durable commit as its parent. On a 409,
|
|
7035
|
+
* refresh Hub state, replay the batch, and retry with the new parent. A push
|
|
7036
|
+
* response without a trustworthy hash is confirmed by a pull before callers
|
|
7037
|
+
* are allowed to observe it as durable.
|
|
7038
|
+
*/
|
|
7039
|
+
async pushBatch(batch) {
|
|
7040
|
+
for (let attempt = 0;; attempt += 1) {
|
|
7041
|
+
const payload = {};
|
|
7042
|
+
for (const [path, content] of Object.entries(batch.changes)) payload[path] = content === null ? null : {
|
|
7043
|
+
type: "file",
|
|
7044
|
+
content
|
|
7045
|
+
};
|
|
7046
|
+
let url;
|
|
7047
|
+
try {
|
|
7048
|
+
url = await this.client.pushAgent(this.identifier, {
|
|
7049
|
+
files: payload,
|
|
7050
|
+
...this.commitHash ? { parentCommit: this.commitHash } : {}
|
|
7051
|
+
});
|
|
7052
|
+
} catch (error) {
|
|
7053
|
+
if (getLangSmithStatus(error) !== 409 || attempt >= MAX_CONFLICT_RETRIES) throw error;
|
|
7054
|
+
const snapshot = await this.fetchTree();
|
|
7055
|
+
this.rematerializeAfterConflict(batch, snapshot, error);
|
|
7056
|
+
continue;
|
|
7057
|
+
}
|
|
7058
|
+
const pushedCommitHash = parseCommitHashFromUrl(url, this.identifier);
|
|
7059
|
+
if (pushedCommitHash === null) {
|
|
7060
|
+
this.beginSnapshotPublication();
|
|
7061
|
+
const snapshot = await this.fetchTree();
|
|
7062
|
+
if (snapshot.commitHash === null) throw new Error("Context Hub commit succeeded but its hash could not be resolved");
|
|
7063
|
+
return {
|
|
7064
|
+
kind: "snapshot",
|
|
7065
|
+
snapshot
|
|
7066
|
+
};
|
|
7067
|
+
}
|
|
7068
|
+
return {
|
|
7069
|
+
kind: "commit",
|
|
7070
|
+
commitHash: pushedCommitHash
|
|
6655
7071
|
};
|
|
6656
7072
|
}
|
|
6657
7073
|
}
|
|
@@ -6779,53 +7195,71 @@ var ContextHubBackend = class ContextHubBackend {
|
|
|
6779
7195
|
async write(filePath, content) {
|
|
6780
7196
|
const hubPath = ContextHubBackend.stripPrefix(filePath);
|
|
6781
7197
|
try {
|
|
6782
|
-
await this.
|
|
6783
|
-
|
|
7198
|
+
const accepted = await this.acceptMutation(() => {
|
|
7199
|
+
return {
|
|
7200
|
+
result: {
|
|
7201
|
+
path: filePath,
|
|
7202
|
+
filesUpdate: null
|
|
7203
|
+
},
|
|
7204
|
+
completion: this.enqueueCommit({ [hubPath]: content })
|
|
7205
|
+
};
|
|
7206
|
+
});
|
|
7207
|
+
await accepted.completion;
|
|
7208
|
+
return accepted.result;
|
|
6784
7209
|
} catch (error) {
|
|
6785
|
-
if (isLangSmithError(error)) {
|
|
6786
|
-
this.cache = null;
|
|
6787
|
-
return { error: ContextHubBackend.toHubUnavailableError(error) };
|
|
6788
|
-
}
|
|
7210
|
+
if (isLangSmithError(error)) return { error: ContextHubBackend.toHubUnavailableError(error) };
|
|
6789
7211
|
throw error;
|
|
6790
7212
|
}
|
|
6791
|
-
return {
|
|
6792
|
-
path: filePath,
|
|
6793
|
-
filesUpdate: null
|
|
6794
|
-
};
|
|
6795
7213
|
}
|
|
6796
7214
|
async edit(filePath, oldString, newString, replaceAll = false) {
|
|
6797
7215
|
const hubPath = ContextHubBackend.stripPrefix(filePath);
|
|
6798
7216
|
try {
|
|
6799
|
-
const
|
|
6800
|
-
|
|
6801
|
-
|
|
6802
|
-
|
|
6803
|
-
|
|
6804
|
-
|
|
6805
|
-
|
|
6806
|
-
|
|
6807
|
-
|
|
6808
|
-
|
|
6809
|
-
|
|
7217
|
+
const accepted = await this.acceptMutation((cache) => {
|
|
7218
|
+
const current = cache[hubPath];
|
|
7219
|
+
if (current === void 0) return { result: { error: `Error: File '${filePath}' not found` } };
|
|
7220
|
+
const replacementResult = performStringReplacement(current, oldString, newString, replaceAll);
|
|
7221
|
+
if (typeof replacementResult === "string") return { result: { error: replacementResult } };
|
|
7222
|
+
const [newContent, occurrences] = replacementResult;
|
|
7223
|
+
const result = {
|
|
7224
|
+
path: filePath,
|
|
7225
|
+
filesUpdate: null,
|
|
7226
|
+
occurrences
|
|
7227
|
+
};
|
|
7228
|
+
return {
|
|
7229
|
+
result,
|
|
7230
|
+
completion: this.enqueueCommit({ [hubPath]: newContent }, {
|
|
7231
|
+
kind: "edit",
|
|
7232
|
+
path: hubPath,
|
|
7233
|
+
oldString,
|
|
7234
|
+
newString,
|
|
7235
|
+
replaceAll,
|
|
7236
|
+
updateOccurrences: (replayedOccurrences) => {
|
|
7237
|
+
result.occurrences = replayedOccurrences;
|
|
7238
|
+
}
|
|
7239
|
+
})
|
|
7240
|
+
};
|
|
7241
|
+
});
|
|
7242
|
+
await accepted.completion;
|
|
7243
|
+
return accepted.result;
|
|
6810
7244
|
} catch (error) {
|
|
6811
|
-
if (isLangSmithError(error)) {
|
|
6812
|
-
this.cache = null;
|
|
6813
|
-
return { error: ContextHubBackend.toHubUnavailableError(error) };
|
|
6814
|
-
}
|
|
7245
|
+
if (isLangSmithError(error)) return { error: ContextHubBackend.toHubUnavailableError(error) };
|
|
6815
7246
|
throw error;
|
|
6816
7247
|
}
|
|
6817
7248
|
}
|
|
6818
7249
|
async delete(filePath) {
|
|
6819
7250
|
const hubPath = ContextHubBackend.stripPrefix(filePath);
|
|
6820
7251
|
try {
|
|
6821
|
-
|
|
6822
|
-
|
|
6823
|
-
|
|
7252
|
+
const accepted = await this.acceptMutation((cache) => {
|
|
7253
|
+
if (!(hubPath in cache)) return { result: { error: `Error: File '${filePath}' not found` } };
|
|
7254
|
+
return {
|
|
7255
|
+
result: { path: filePath },
|
|
7256
|
+
completion: this.enqueueCommit({ [hubPath]: null })
|
|
7257
|
+
};
|
|
7258
|
+
});
|
|
7259
|
+
await accepted.completion;
|
|
7260
|
+
return accepted.result;
|
|
6824
7261
|
} catch (error) {
|
|
6825
|
-
if (isLangSmithError(error)) {
|
|
6826
|
-
this.cache = null;
|
|
6827
|
-
return { error: ContextHubBackend.toHubUnavailableError(error) };
|
|
6828
|
-
}
|
|
7262
|
+
if (isLangSmithError(error)) return { error: ContextHubBackend.toHubUnavailableError(error) };
|
|
6829
7263
|
throw error;
|
|
6830
7264
|
}
|
|
6831
7265
|
}
|
|
@@ -6842,13 +7276,15 @@ var ContextHubBackend = class ContextHubBackend {
|
|
|
6842
7276
|
}
|
|
6843
7277
|
let commitError = null;
|
|
6844
7278
|
if (Object.keys(validFiles).length > 0) try {
|
|
6845
|
-
await this.
|
|
6846
|
-
|
|
7279
|
+
await (await this.acceptMutation(() => {
|
|
7280
|
+
return {
|
|
7281
|
+
result: null,
|
|
7282
|
+
completion: this.enqueueCommit(validFiles)
|
|
7283
|
+
};
|
|
7284
|
+
})).completion;
|
|
6847
7285
|
} catch (error) {
|
|
6848
|
-
if (isLangSmithError(error))
|
|
6849
|
-
|
|
6850
|
-
commitError = mapHubFileOperationError(error);
|
|
6851
|
-
} else throw error;
|
|
7286
|
+
if (isLangSmithError(error)) commitError = mapHubFileOperationError(error);
|
|
7287
|
+
else throw error;
|
|
6852
7288
|
}
|
|
6853
7289
|
return decoded.map(([path, text]) => {
|
|
6854
7290
|
if (text === null) return {
|
|
@@ -7533,4 +7969,4 @@ var LangSmithSandbox = class LangSmithSandbox extends BaseSandbox {
|
|
|
7533
7969
|
//#endregion
|
|
7534
7970
|
export { filesValue as A, StateBackend as B, createSummarizationMiddleware as C, MAX_SKILL_NAME_LENGTH as D, MAX_SKILL_FILE_SIZE as E, SUBAGENT_RESPONSE_FORMAT_CONFIG_KEY as F, resolveBackend as G, applyGrepMaxCount as H, createSubAgent as I, checkEmptyContent as J, adaptBackendProtocol as K, createSubAgentMiddleware as L, DEFAULT_GENERAL_PURPOSE_DESCRIPTION as M, DEFAULT_SUBAGENT_PROMPT as N, createSkillsMiddleware as O, GENERAL_PURPOSE_SUBAGENT as P, createFilesystemMiddleware as R, computeSummarizationDefaults as S, MAX_SKILL_DESCRIPTION_LENGTH as T, isSandboxBackend as U, SandboxError as V, isSandboxProtocol as W, isTextMimeType as X, getMimeType as Y, performStringReplacement as Z, createHarnessProfile as _, ASYNC_TASK_SYSTEM_PROMPT as a, createAsyncSubAgentMiddleware as b, TASK_SYSTEM_PROMPT as c, registerHarnessProfile as d, generalPurposeSubagentConfigSchema as f, EMPTY_HARNESS_PROFILE as g, serializeProfile as h, StoreBackend as i, createPatchToolCallsMiddleware as j, createMemoryMiddleware as k, createDeepAgent as l, parseHarnessProfileConfig as m, BaseSandbox as n, BASE_AGENT_PROMPT as o, harnessProfileConfigSchema as p, adaptSandboxProtocol as q, ContextHubBackend as r, EXECUTION_SYSTEM_PROMPT as s, LangSmithSandbox as t, getHarnessProfile as u, REQUIRED_MIDDLEWARE_NAMES as v, createCompletionCallbackMiddleware as w, isAsyncSubAgent as x, ConfigurationError as y, CompositeBackend as z };
|
|
7535
7971
|
|
|
7536
|
-
//# sourceMappingURL=langsmith-
|
|
7972
|
+
//# sourceMappingURL=langsmith-DRyafCNe.js.map
|