deepagents 1.13.4 → 1.14.0
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-D7nWARfg.d.cts → agent-CrQgOsbl.d.cts} +111 -27
- package/dist/{agent-CVBXKZAu.d.ts → agent-DGkf2K7U.d.ts} +111 -27
- package/dist/browser.cjs +2 -1
- package/dist/browser.d.cts +2 -2
- package/dist/browser.d.ts +2 -2
- package/dist/browser.js +2 -2
- package/dist/index.cjs +3 -2
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +3 -3
- package/dist/{langsmith-BAO_h4J6.js → langsmith-3LzYb-m7.js} +202 -76
- package/dist/langsmith-3LzYb-m7.js.map +1 -0
- package/dist/{langsmith-yA3yhBRa.cjs → langsmith-C9zNPKIz.cjs} +207 -75
- package/dist/langsmith-C9zNPKIz.cjs.map +1 -0
- package/dist/node.cjs +3 -2
- package/dist/node.d.cts +2 -2
- package/dist/node.d.ts +2 -2
- package/dist/node.js +3 -3
- package/dist/{src-D_XxmEak.cjs → src-Nhlq1O0Y.cjs} +2 -2
- package/dist/{src-D_XxmEak.cjs.map → src-Nhlq1O0Y.cjs.map} +1 -1
- package/dist/{src-CcY34Ckx.js → src-gIoHrhh3.js} +2 -2
- package/dist/{src-CcY34Ckx.js.map → src-gIoHrhh3.js.map} +1 -1
- package/package.json +2 -2
- package/dist/langsmith-BAO_h4J6.js.map +0 -1
- package/dist/langsmith-yA3yhBRa.cjs.map +0 -1
|
@@ -230,6 +230,21 @@ function formatContentWithLineNumbers(content, startLine = 1) {
|
|
|
230
230
|
return formatContentWithLineNumbersAndBoundaries(content, startLine).text;
|
|
231
231
|
}
|
|
232
232
|
/**
|
|
233
|
+
* Join file content into the unmodified source body of a `read_file` result.
|
|
234
|
+
*
|
|
235
|
+
* Source lines are emitted unchanged, with no numbering. A status header
|
|
236
|
+
* carries the line-range metadata separately, so nothing here needs to
|
|
237
|
+
* encode line numbers or escape anything.
|
|
238
|
+
*/
|
|
239
|
+
function formatSourceBlock(content) {
|
|
240
|
+
let lines;
|
|
241
|
+
if (typeof content === "string") {
|
|
242
|
+
lines = content.split("\n");
|
|
243
|
+
if (lines.length > 0 && lines[lines.length - 1] === "") lines = lines.slice(0, -1);
|
|
244
|
+
} else lines = content;
|
|
245
|
+
return lines.join("\n");
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
233
248
|
* Check if content is empty and return warning message.
|
|
234
249
|
*
|
|
235
250
|
* @param content - Content to check
|
|
@@ -1563,7 +1578,6 @@ var CompositeBackend = class {
|
|
|
1563
1578
|
* - Pluggable backends (StateBackend, StoreBackend, FilesystemBackend, CompositeBackend)
|
|
1564
1579
|
* - Tool result eviction for large outputs
|
|
1565
1580
|
*/
|
|
1566
|
-
const INT_FORMATTER = new Intl.NumberFormat("en-US");
|
|
1567
1581
|
/**
|
|
1568
1582
|
* Normalizes tool input so that models sending `path` instead of `file_path`
|
|
1569
1583
|
* still work. If the input has `path` but not `file_path`, copies `path` into
|
|
@@ -1633,29 +1647,100 @@ const READ_FILE_TRUNCATION_MSG = `
|
|
|
1633
1647
|
|
|
1634
1648
|
[Output was truncated due to size limits. The file content is very large. Consider reformatting the file to make it easier to navigate. For example, if this is JSON, use execute(command='jq . {file_path}') to pretty-print it with line breaks. For other formats, you can use appropriate formatting tools to split long lines.]`;
|
|
1635
1649
|
/**
|
|
1636
|
-
*
|
|
1650
|
+
* Describe the window a read returned, as status header fields.
|
|
1637
1651
|
*
|
|
1638
1652
|
* Backends own source-level pagination because only they know how much of the
|
|
1639
|
-
* file was read. The middleware owns presentation: it
|
|
1640
|
-
* and turns optional metadata into a
|
|
1641
|
-
* optional preserves compatibility with custom backends that predate
|
|
1642
|
-
* contract
|
|
1653
|
+
* file was read. The middleware owns presentation: it renders the source
|
|
1654
|
+
* unmodified and turns optional metadata into a terse header. Keeping the
|
|
1655
|
+
* fields optional preserves compatibility with custom backends that predate
|
|
1656
|
+
* this contract — `prepareReadWindow` derives a range for those instead.
|
|
1643
1657
|
*
|
|
1644
|
-
*
|
|
1645
|
-
*
|
|
1658
|
+
* Returns the `lines A-B[ of T]` field, followed by `next offset N` when the
|
|
1659
|
+
* window stopped short of the end of the file.
|
|
1646
1660
|
*/
|
|
1647
|
-
function
|
|
1661
|
+
function describeReadWindow(readResult) {
|
|
1648
1662
|
const { startLine, endLine, nextOffset, totalLines } = readResult;
|
|
1649
|
-
if (startLine === void 0 || endLine === void 0 ||
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
if (endLine
|
|
1654
|
-
|
|
1655
|
-
|
|
1663
|
+
if (startLine === void 0 || endLine === void 0 || !Number.isSafeInteger(startLine) || !Number.isSafeInteger(endLine) || startLine < 1 || endLine < startLine || totalLines !== void 0 && (!Number.isSafeInteger(totalLines) || totalLines < endLine)) return [];
|
|
1664
|
+
let span = `lines ${startLine}-${endLine}`;
|
|
1665
|
+
if (totalLines !== void 0) span += ` of ${totalLines}`;
|
|
1666
|
+
const fields = [span];
|
|
1667
|
+
if (nextOffset !== void 0 && (totalLines === void 0 || endLine < totalLines)) fields.push(`next offset ${nextOffset}`);
|
|
1668
|
+
return fields;
|
|
1669
|
+
}
|
|
1670
|
+
/** Render the status header that sits above a text `read_file` result. */
|
|
1671
|
+
function readHeader(fields) {
|
|
1672
|
+
return `@@ ${fields.join(" | ")} @@`;
|
|
1673
|
+
}
|
|
1674
|
+
/** Notice disclosing that a negative `offset` was reinterpreted as the start of the file, or `undefined` for a non-negative offset. */
|
|
1675
|
+
function clampedOffsetNotice(offset) {
|
|
1676
|
+
if (offset >= 0) return;
|
|
1677
|
+
return `[Requested offset ${offset} is before the start of the file; read from line 1 instead.]`;
|
|
1678
|
+
}
|
|
1679
|
+
/**
|
|
1680
|
+
* Compose a read result from its notices, status header, and source body.
|
|
1681
|
+
*
|
|
1682
|
+
* Notices sit above the header, so every line below it is unmodified file
|
|
1683
|
+
* content and consumers (including `stripLineNumbers` in the quickjs
|
|
1684
|
+
* provider) can tell the two apart by position.
|
|
1685
|
+
*/
|
|
1686
|
+
function assembleRead(body, options) {
|
|
1687
|
+
const { fields, notices = [] } = options;
|
|
1688
|
+
return [
|
|
1689
|
+
...notices,
|
|
1690
|
+
readHeader(fields),
|
|
1691
|
+
body
|
|
1692
|
+
].join("\n");
|
|
1693
|
+
}
|
|
1694
|
+
/** Derives a header range from `offset` when the backend left `startLine`/`endLine` unset. */
|
|
1695
|
+
function prepareReadWindow(readResult, content, offset) {
|
|
1696
|
+
const body = formatSourceBlock(content);
|
|
1697
|
+
if (readResult.startLine !== void 0 && readResult.endLine !== void 0) return {
|
|
1698
|
+
readResult,
|
|
1699
|
+
body
|
|
1700
|
+
};
|
|
1701
|
+
const startLine = offset + 1;
|
|
1702
|
+
const endLine = startLine + body.split("\n").length - 1;
|
|
1703
|
+
return {
|
|
1704
|
+
readResult: {
|
|
1705
|
+
...readResult,
|
|
1706
|
+
startLine,
|
|
1707
|
+
endLine
|
|
1708
|
+
},
|
|
1709
|
+
body
|
|
1710
|
+
};
|
|
1711
|
+
}
|
|
1712
|
+
/**
|
|
1713
|
+
* Assemble a read cut inside a single source line too long to fit.
|
|
1714
|
+
*
|
|
1715
|
+
* No offset reaches the remainder of such a line, so the header reports how
|
|
1716
|
+
* much of it is shown in place of a resume point.
|
|
1717
|
+
*/
|
|
1718
|
+
function midlineTruncatedRead(body, readResult, threshold, notices) {
|
|
1719
|
+
const newlineIdx = body.indexOf("\n");
|
|
1720
|
+
const oversized = newlineIdx === -1 ? body.length : newlineIdx;
|
|
1721
|
+
const clipped = {
|
|
1722
|
+
totalLines: readResult.totalLines,
|
|
1723
|
+
startLine: readResult.startLine,
|
|
1724
|
+
endLine: readResult.startLine,
|
|
1725
|
+
nextOffset: void 0
|
|
1726
|
+
};
|
|
1727
|
+
const fieldsFor = (shown) => [
|
|
1728
|
+
...describeReadWindow(clipped),
|
|
1729
|
+
"truncated mid-line",
|
|
1730
|
+
`${shown} of ${oversized} chars`
|
|
1731
|
+
];
|
|
1732
|
+
const reserved = assembleRead("", {
|
|
1733
|
+
fields: fieldsFor(oversized),
|
|
1734
|
+
notices
|
|
1735
|
+
}).length;
|
|
1736
|
+
const shown = Math.max(0, Math.min(oversized, threshold - reserved));
|
|
1737
|
+
return assembleRead(body.slice(0, shown), {
|
|
1738
|
+
fields: fieldsFor(shown),
|
|
1739
|
+
notices
|
|
1740
|
+
});
|
|
1656
1741
|
}
|
|
1657
1742
|
/**
|
|
1658
|
-
* Fit a
|
|
1743
|
+
* Fit a paginated read into the middleware's output budget without
|
|
1659
1744
|
* publishing a resume offset that skips content the model did not see.
|
|
1660
1745
|
*
|
|
1661
1746
|
* There are two independent forms of limiting:
|
|
@@ -1666,38 +1751,50 @@ function remainingLinesNotice(readResult) {
|
|
|
1666
1751
|
* If the backend returned lines 1-100 but the middleware only displayed lines
|
|
1667
1752
|
* 1-30, forwarding the backend's original `nextOffset: 100` would silently skip
|
|
1668
1753
|
* lines 31-100 on the next read. This function therefore truncates only after a
|
|
1669
|
-
* complete source line and rebuilds the
|
|
1670
|
-
* line
|
|
1671
|
-
*
|
|
1672
|
-
*
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
const
|
|
1682
|
-
if (!tokenLimit || content.length + notice.length < 4 * tokenLimit) return content + notice;
|
|
1683
|
-
const truncationMsg = READ_FILE_TRUNCATION_MSG.replace("{file_path}", filePath);
|
|
1754
|
+
* complete source line and rebuilds the header from the last line actually
|
|
1755
|
+
* displayed. If no complete source line fits alongside the truncation
|
|
1756
|
+
* message, it falls back to `midlineTruncatedRead`, which reports exactly how
|
|
1757
|
+
* much of the oversized line is shown rather than guessing at a resume point.
|
|
1758
|
+
*/
|
|
1759
|
+
function truncatePaginatedRead(body, readResult, options) {
|
|
1760
|
+
const { filePath, tokenLimit, notices = [] } = options;
|
|
1761
|
+
const result = assembleRead(body, {
|
|
1762
|
+
fields: describeReadWindow(readResult),
|
|
1763
|
+
notices
|
|
1764
|
+
});
|
|
1765
|
+
if (!tokenLimit || result.length < 4 * tokenLimit) return result;
|
|
1766
|
+
const truncationMsg = READ_FILE_TRUNCATION_MSG.replace("{file_path}", filePath).trim();
|
|
1684
1767
|
const threshold = 4 * tokenLimit;
|
|
1685
1768
|
if (readResult.startLine !== void 0 && readResult.endLine !== void 0) {
|
|
1686
|
-
const
|
|
1687
|
-
|
|
1769
|
+
const rows = body.split("\n");
|
|
1770
|
+
let position = 0;
|
|
1771
|
+
const boundaries = [];
|
|
1772
|
+
for (let index = 0; index < rows.length; index += 1) {
|
|
1773
|
+
const sourceLine = readResult.startLine + index;
|
|
1774
|
+
if (sourceLine > readResult.endLine) break;
|
|
1775
|
+
position += rows[index].length;
|
|
1776
|
+
boundaries.push({
|
|
1777
|
+
endOffset: position,
|
|
1778
|
+
endLine: sourceLine
|
|
1779
|
+
});
|
|
1780
|
+
position += 1;
|
|
1781
|
+
}
|
|
1688
1782
|
for (let index = boundaries.length - 1; index >= 0; index -= 1) {
|
|
1689
|
-
const
|
|
1690
|
-
const
|
|
1783
|
+
const { endOffset, endLine } = boundaries[index];
|
|
1784
|
+
const adjustedResult = {
|
|
1691
1785
|
totalLines: readResult.totalLines,
|
|
1692
1786
|
startLine: readResult.startLine,
|
|
1693
|
-
endLine
|
|
1694
|
-
nextOffset:
|
|
1787
|
+
endLine,
|
|
1788
|
+
nextOffset: endLine
|
|
1789
|
+
};
|
|
1790
|
+
const candidate = assembleRead(body.slice(0, endOffset), {
|
|
1791
|
+
fields: [...describeReadWindow(adjustedResult), "truncated due to size"],
|
|
1792
|
+
notices: [...notices, truncationMsg]
|
|
1695
1793
|
});
|
|
1696
|
-
if (
|
|
1794
|
+
if (candidate.length <= threshold) return candidate;
|
|
1697
1795
|
}
|
|
1698
1796
|
}
|
|
1699
|
-
|
|
1700
|
-
return content.substring(0, maxContentLength) + truncationMsg;
|
|
1797
|
+
return midlineTruncatedRead(body, readResult, threshold, [...notices, truncationMsg]);
|
|
1701
1798
|
}
|
|
1702
1799
|
/**
|
|
1703
1800
|
* Note appended to grep results that were cut short by the match-count cap.
|
|
@@ -2093,8 +2190,7 @@ const READ_FILE_TOOL_DESCRIPTION = langchain.context`
|
|
|
2093
2190
|
|
|
2094
2191
|
Usage:
|
|
2095
2192
|
- By default, it reads up to ${100} lines starting from the beginning of the file. Use \`offset\`/\`limit\` to page through large files instead of reading them whole.
|
|
2096
|
-
-
|
|
2097
|
-
- Lines over ${INT_FORMATTER.format(MAX_LINE_LENGTH)} characters are split with continuation markers (e.g. 5.1, 5.2); \`limit\` counts source lines, so continuation rows do not consume the budget.
|
|
2193
|
+
- A status header, \`@@ field | field | ... @@\`, sits above the file content, and every line after it is unmodified file content. When content is truncated, there may be an explanation before the header. Never include the header when editing.
|
|
2098
2194
|
- Speculatively batch multiple \`read_file\` calls in one response when several files may be useful.
|
|
2099
2195
|
- An empty file returns a system-reminder warning in place of contents.
|
|
2100
2196
|
- Large tool results may be offloaded to a file; the tool message gives the path. Read that path here, paging with \`offset\`/\`limit\`.
|
|
@@ -2114,7 +2210,7 @@ const EDIT_FILE_TOOL_DESCRIPTION = langchain.context`
|
|
|
2114
2210
|
|
|
2115
2211
|
Usage:
|
|
2116
2212
|
- You must read the file before editing; this tool errors otherwise.
|
|
2117
|
-
- Preserve the exact indentation from the read output, and never include
|
|
2213
|
+
- Preserve the exact source indentation from the read output, and never include the read status header in old_string or new_string.
|
|
2118
2214
|
- Prefer editing an existing file over creating a new one.
|
|
2119
2215
|
- Only use emojis if the user explicitly requests it.
|
|
2120
2216
|
`;
|
|
@@ -2249,6 +2345,10 @@ function createReadFileTool(backend, options) {
|
|
|
2249
2345
|
}];
|
|
2250
2346
|
}
|
|
2251
2347
|
let content = typeof readResult.content === "string" ? readResult.content : "";
|
|
2348
|
+
if (content === "System reminder: File exists but has empty contents") return [{
|
|
2349
|
+
type: "text",
|
|
2350
|
+
text: content
|
|
2351
|
+
}];
|
|
2252
2352
|
const lines = content.split("\n");
|
|
2253
2353
|
let paginationResult = readResult;
|
|
2254
2354
|
if (lines.length > limit) {
|
|
@@ -2262,9 +2362,15 @@ function createReadFileTool(backend, options) {
|
|
|
2262
2362
|
};
|
|
2263
2363
|
}
|
|
2264
2364
|
}
|
|
2365
|
+
const { readResult: preparedResult, body } = prepareReadWindow(paginationResult, content, offset);
|
|
2366
|
+
const clampNotice = clampedOffsetNotice(requestedOffset);
|
|
2265
2367
|
return [{
|
|
2266
2368
|
type: "text",
|
|
2267
|
-
text: truncatePaginatedRead(
|
|
2369
|
+
text: truncatePaginatedRead(body, preparedResult, {
|
|
2370
|
+
filePath: file_path,
|
|
2371
|
+
tokenLimit: toolTokenLimitBeforeEvict,
|
|
2372
|
+
notices: clampNotice ? [clampNotice] : []
|
|
2373
|
+
})
|
|
2268
2374
|
}];
|
|
2269
2375
|
}, {
|
|
2270
2376
|
name: "read_file",
|
|
@@ -3514,6 +3620,11 @@ const EXCLUDED_STATE_KEYS = [
|
|
|
3514
3620
|
* the parent's private channels (skills metadata, memory contents, etc.)
|
|
3515
3621
|
* to rebuild an equivalent prompt.
|
|
3516
3622
|
*
|
|
3623
|
+
* A pending invalidation propagates: a fork spawned in the same iteration
|
|
3624
|
+
* that set `skillsMetadata` to `null` inherits the `null`, so its mirrored
|
|
3625
|
+
* skills middleware reloads from its own sources rather than seeing the
|
|
3626
|
+
* parent's last loaded list.
|
|
3627
|
+
*
|
|
3517
3628
|
* Call counts stay excluded: a fork inherits the parent's middleware, so
|
|
3518
3629
|
* sharing them collides on the parent's channel.
|
|
3519
3630
|
*/
|
|
@@ -4478,30 +4589,37 @@ const SkillMetadataEntrySchema = zod.z.object({
|
|
|
4478
4589
|
module: zod.z.string().optional()
|
|
4479
4590
|
});
|
|
4480
4591
|
/**
|
|
4481
|
-
*
|
|
4482
|
-
* Skills are deduplicated by name, with later values overriding earlier ones.
|
|
4592
|
+
* State value for a middleware's `skillsMetadata` field.
|
|
4483
4593
|
*
|
|
4484
|
-
*
|
|
4485
|
-
*
|
|
4486
|
-
*
|
|
4594
|
+
* A middleware can only read and write the fields declared on its own state
|
|
4595
|
+
* schema. A middleware that needs `skillsMetadata` — to inspect the skills
|
|
4596
|
+
* loaded for the thread, or to set the field to `null` and make the next model
|
|
4597
|
+
* call reload every source — declares it with this value.
|
|
4598
|
+
*
|
|
4599
|
+
* Treat the value as opaque: it is meant to be passed to `StateSchema`, and
|
|
4600
|
+
* its concrete type is an implementation detail that may change. To type an
|
|
4601
|
+
* individual entry, use {@link SkillMetadataEntry}.
|
|
4602
|
+
*
|
|
4603
|
+
* @example
|
|
4604
|
+
* ```typescript
|
|
4605
|
+
* import { createMiddleware } from "langchain";
|
|
4606
|
+
* import { StateSchema } from "@langchain/langgraph";
|
|
4607
|
+
* import { skillsMetadataValue } from "deepagents";
|
|
4608
|
+
*
|
|
4609
|
+
* const reloadEditedSkills = createMiddleware({
|
|
4610
|
+
* name: "ReloadEditedSkills",
|
|
4611
|
+
* stateSchema: new StateSchema({ skillsMetadata: skillsMetadataValue }),
|
|
4612
|
+
* afterAgent: (state) =>
|
|
4613
|
+
* agentEditedSkills(state) ? { skillsMetadata: null } : undefined,
|
|
4614
|
+
* });
|
|
4615
|
+
* ```
|
|
4487
4616
|
*/
|
|
4488
|
-
|
|
4489
|
-
if (!update || update.length === 0) return current || [];
|
|
4490
|
-
if (!current || current.length === 0) return update;
|
|
4491
|
-
const merged = /* @__PURE__ */ new Map();
|
|
4492
|
-
for (const skill of current) merged.set(skill.name, skill);
|
|
4493
|
-
for (const skill of update) merged.set(skill.name, skill);
|
|
4494
|
-
return Array.from(merged.values());
|
|
4495
|
-
}
|
|
4617
|
+
const skillsMetadataValue = zod.z.array(SkillMetadataEntrySchema).nullish();
|
|
4496
4618
|
/**
|
|
4497
4619
|
* State schema for skills middleware.
|
|
4498
|
-
* Uses ReducedValue for skillsMetadata to allow concurrent updates from parallel subagents.
|
|
4499
4620
|
*/
|
|
4500
4621
|
const SkillsStateSchema = new _langchain_langgraph.StateSchema({
|
|
4501
|
-
skillsMetadata:
|
|
4502
|
-
inputSchema: zod.z.array(SkillMetadataEntrySchema).optional(),
|
|
4503
|
-
reducer: skillsMetadataReducer
|
|
4504
|
-
}),
|
|
4622
|
+
skillsMetadata: skillsMetadataValue,
|
|
4505
4623
|
files: filesValue
|
|
4506
4624
|
});
|
|
4507
4625
|
/**
|
|
@@ -4838,6 +4956,21 @@ function validateModulePath(raw) {
|
|
|
4838
4956
|
* pattern: skill names and descriptions are shown in the prompt, but the agent
|
|
4839
4957
|
* reads full SKILL.md content only when needed.
|
|
4840
4958
|
*
|
|
4959
|
+
* Skills are loaded once per thread and stored in state. To pick up skills
|
|
4960
|
+
* added, edited, or deleted since then, set `skillsMetadata` to `null`; the
|
|
4961
|
+
* next model call reloads every source:
|
|
4962
|
+
*
|
|
4963
|
+
* ```ts
|
|
4964
|
+
* await agent.updateState(config, { skillsMetadata: null });
|
|
4965
|
+
* // or as part of the next run's input
|
|
4966
|
+
* await agent.invoke({ messages, skillsMetadata: null }, config);
|
|
4967
|
+
* ```
|
|
4968
|
+
*
|
|
4969
|
+
* Loading happens in `beforeModel`, so the reload is served by the next model
|
|
4970
|
+
* call rather than the next run. A middleware of your own can therefore
|
|
4971
|
+
* invalidate from any hook — including mid-run, from `afterModel` — and see
|
|
4972
|
+
* the fresh list on the following call. See {@link skillsMetadataValue}.
|
|
4973
|
+
*
|
|
4841
4974
|
* @param options - Configuration options
|
|
4842
4975
|
* @returns AgentMiddleware for skills loading and injection
|
|
4843
4976
|
*
|
|
@@ -4851,17 +4984,11 @@ function validateModulePath(raw) {
|
|
|
4851
4984
|
*/
|
|
4852
4985
|
function createSkillsMiddleware(options) {
|
|
4853
4986
|
const { backend, sources } = options;
|
|
4854
|
-
let loadedSkills = [];
|
|
4855
4987
|
return (0, langchain.createMiddleware)({
|
|
4856
4988
|
name: "SkillsMiddleware",
|
|
4857
4989
|
stateSchema: SkillsStateSchema,
|
|
4858
|
-
async
|
|
4859
|
-
|
|
4860
|
-
if (loadedSkills.length > 0) return stateHasSkills ? void 0 : { skillsMetadata: loadedSkills };
|
|
4861
|
-
if (stateHasSkills) {
|
|
4862
|
-
loadedSkills = state.skillsMetadata;
|
|
4863
|
-
return;
|
|
4864
|
-
}
|
|
4990
|
+
async beforeModel(state) {
|
|
4991
|
+
if (state.skillsMetadata !== null && state.skillsMetadata !== void 0) return;
|
|
4865
4992
|
const resolvedBackend = await resolveBackend(backend, { state });
|
|
4866
4993
|
const allSkills = /* @__PURE__ */ new Map();
|
|
4867
4994
|
for (const sourcePath of sources) try {
|
|
@@ -4870,11 +4997,10 @@ function createSkillsMiddleware(options) {
|
|
|
4870
4997
|
} catch (error) {
|
|
4871
4998
|
console.debug(`[BackendSkillsMiddleware] Failed to load skills from ${sourcePath}:`, error);
|
|
4872
4999
|
}
|
|
4873
|
-
|
|
4874
|
-
return { skillsMetadata: loadedSkills };
|
|
5000
|
+
return { skillsMetadata: Array.from(allSkills.values()) };
|
|
4875
5001
|
},
|
|
4876
5002
|
wrapModelCall(request, handler) {
|
|
4877
|
-
const skillsMetadata =
|
|
5003
|
+
const skillsMetadata = request.state?.skillsMetadata || [];
|
|
4878
5004
|
const skillsLocations = formatSkillsLocations(sources);
|
|
4879
5005
|
const skillsList = formatSkillsList(skillsMetadata, sources);
|
|
4880
5006
|
const skillsSection = SKILLS_SYSTEM_PROMPT.replace("{skills_locations}", skillsLocations).replace("{skills_list}", skillsList);
|
|
@@ -9094,5 +9220,11 @@ Object.defineProperty(exports, "serializeProfile", {
|
|
|
9094
9220
|
return serializeProfile;
|
|
9095
9221
|
}
|
|
9096
9222
|
});
|
|
9223
|
+
Object.defineProperty(exports, "skillsMetadataValue", {
|
|
9224
|
+
enumerable: true,
|
|
9225
|
+
get: function() {
|
|
9226
|
+
return skillsMetadataValue;
|
|
9227
|
+
}
|
|
9228
|
+
});
|
|
9097
9229
|
|
|
9098
|
-
//# sourceMappingURL=langsmith-
|
|
9230
|
+
//# sourceMappingURL=langsmith-C9zNPKIz.cjs.map
|