deepagents 1.10.0 → 1.10.2
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/index.cjs +1146 -24
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +436 -6
- package/dist/index.d.ts +436 -6
- package/dist/index.js +1137 -25
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -46,6 +46,7 @@ let node_child_process = require("node:child_process");
|
|
|
46
46
|
node_child_process = __toESM(node_child_process, 1);
|
|
47
47
|
let fast_glob = require("fast-glob");
|
|
48
48
|
fast_glob = __toESM(fast_glob, 1);
|
|
49
|
+
let langsmith = require("langsmith");
|
|
49
50
|
let langsmith_experimental_sandbox = require("langsmith/experimental/sandbox");
|
|
50
51
|
let node_os = require("node:os");
|
|
51
52
|
node_os = __toESM(node_os, 1);
|
|
@@ -5726,6 +5727,341 @@ var FilesystemBackend = class {
|
|
|
5726
5727
|
}
|
|
5727
5728
|
};
|
|
5728
5729
|
//#endregion
|
|
5730
|
+
//#region src/backends/context-hub.ts
|
|
5731
|
+
/**
|
|
5732
|
+
* ContextHubBackend: Store files in a LangSmith Hub agent repo (persistent).
|
|
5733
|
+
*/
|
|
5734
|
+
const URL_COMMIT_SUFFIX_RE = /:([0-9a-f]{8,64})$/i;
|
|
5735
|
+
const TEXT_MIME_TYPE = "text/plain";
|
|
5736
|
+
const FNMATCH_OPTIONS = { bash: true };
|
|
5737
|
+
function getErrorMessage(error) {
|
|
5738
|
+
if (typeof error === "string") return error;
|
|
5739
|
+
if (typeof error === "object" && error !== null && "message" in error && typeof error.message === "string") return error.message;
|
|
5740
|
+
return String(error);
|
|
5741
|
+
}
|
|
5742
|
+
function splitLinesKeepEnds(content) {
|
|
5743
|
+
const lines = [];
|
|
5744
|
+
let lineStart = 0;
|
|
5745
|
+
for (let index = 0; index < content.length; index += 1) if (content[index] === "\n") {
|
|
5746
|
+
lines.push(content.slice(lineStart, index + 1));
|
|
5747
|
+
lineStart = index + 1;
|
|
5748
|
+
}
|
|
5749
|
+
if (lineStart < content.length) lines.push(content.slice(lineStart));
|
|
5750
|
+
return lines;
|
|
5751
|
+
}
|
|
5752
|
+
function sliceReadContent(content, offset, limit) {
|
|
5753
|
+
if (!content || content.trim() === "") return { content };
|
|
5754
|
+
const lines = splitLinesKeepEnds(content.replace(/\r\n/g, "\n").replace(/\r/g, "\n"));
|
|
5755
|
+
const startIndex = offset;
|
|
5756
|
+
const endIndex = Math.min(startIndex + limit, lines.length);
|
|
5757
|
+
if (startIndex >= lines.length) return { error: `Line offset ${offset} exceeds file length (${lines.length} lines)` };
|
|
5758
|
+
return { content: lines.slice(startIndex, endIndex).join("") };
|
|
5759
|
+
}
|
|
5760
|
+
function isLangSmithNotFoundError(error) {
|
|
5761
|
+
if (typeof error !== "object" || error === null) return false;
|
|
5762
|
+
const maybeError = error;
|
|
5763
|
+
return maybeError.name === "LangSmithNotFoundError" || maybeError.status === 404;
|
|
5764
|
+
}
|
|
5765
|
+
function isLangSmithError(error) {
|
|
5766
|
+
if (typeof error !== "object" || error === null) return false;
|
|
5767
|
+
const maybeError = error;
|
|
5768
|
+
return typeof maybeError.name === "string" && maybeError.name.startsWith("LangSmith") || typeof maybeError.status === "number";
|
|
5769
|
+
}
|
|
5770
|
+
function getLangSmithStatus(error) {
|
|
5771
|
+
if (typeof error !== "object" || error === null) return;
|
|
5772
|
+
const maybeError = error;
|
|
5773
|
+
if (typeof maybeError.status === "number") return maybeError.status;
|
|
5774
|
+
}
|
|
5775
|
+
function mapHubFileOperationError(error) {
|
|
5776
|
+
const status = getLangSmithStatus(error);
|
|
5777
|
+
if (status === 401 || status === 403) return "permission_denied";
|
|
5778
|
+
if (status === 404) return "file_not_found";
|
|
5779
|
+
return "invalid_path";
|
|
5780
|
+
}
|
|
5781
|
+
/**
|
|
5782
|
+
* Backend that stores files in a LangSmith Hub agent repo (persistent).
|
|
5783
|
+
*/
|
|
5784
|
+
var ContextHubBackend = class ContextHubBackend {
|
|
5785
|
+
identifier;
|
|
5786
|
+
client;
|
|
5787
|
+
cache = null;
|
|
5788
|
+
linkedEntries = {};
|
|
5789
|
+
commitHash = null;
|
|
5790
|
+
constructor(identifier, options = {}) {
|
|
5791
|
+
this.identifier = identifier;
|
|
5792
|
+
this.client = options.client ?? new langsmith.Client();
|
|
5793
|
+
}
|
|
5794
|
+
static stripPrefix(path) {
|
|
5795
|
+
return path.replace(/^\/+/, "");
|
|
5796
|
+
}
|
|
5797
|
+
static toHubUnavailableError(error) {
|
|
5798
|
+
return `Hub unavailable: ${getErrorMessage(error)}`;
|
|
5799
|
+
}
|
|
5800
|
+
async loadTree() {
|
|
5801
|
+
let context;
|
|
5802
|
+
try {
|
|
5803
|
+
context = await this.client.pullAgent(this.identifier);
|
|
5804
|
+
} catch (error) {
|
|
5805
|
+
if (isLangSmithNotFoundError(error)) {
|
|
5806
|
+
this.cache = {};
|
|
5807
|
+
this.linkedEntries = {};
|
|
5808
|
+
this.commitHash = null;
|
|
5809
|
+
return;
|
|
5810
|
+
}
|
|
5811
|
+
throw error;
|
|
5812
|
+
}
|
|
5813
|
+
this.commitHash = context.commit_hash;
|
|
5814
|
+
this.cache = {};
|
|
5815
|
+
this.linkedEntries = {};
|
|
5816
|
+
for (const [path, entry] of Object.entries(context.files)) if (entry.type === "file") this.cache[path] = entry.content;
|
|
5817
|
+
else if ((entry.type === "agent" || entry.type === "skill") && typeof entry.repo_handle === "string") this.linkedEntries[path] = entry.repo_handle;
|
|
5818
|
+
}
|
|
5819
|
+
async ensureCache() {
|
|
5820
|
+
if (this.cache === null) await this.loadTree();
|
|
5821
|
+
if (this.cache === null) throw new Error("Context Hub cache failed to initialize");
|
|
5822
|
+
return this.cache;
|
|
5823
|
+
}
|
|
5824
|
+
async commit(files) {
|
|
5825
|
+
if (Object.keys(files).length === 0) return;
|
|
5826
|
+
const payload = {};
|
|
5827
|
+
for (const [path, content] of Object.entries(files)) payload[path] = {
|
|
5828
|
+
type: "file",
|
|
5829
|
+
content
|
|
5830
|
+
};
|
|
5831
|
+
const url = await this.client.pushAgent(this.identifier, {
|
|
5832
|
+
files: payload,
|
|
5833
|
+
...this.commitHash ? { parentCommit: this.commitHash } : {}
|
|
5834
|
+
});
|
|
5835
|
+
const match = URL_COMMIT_SUFFIX_RE.exec(url);
|
|
5836
|
+
if (match) this.commitHash = match[1];
|
|
5837
|
+
if (this.cache !== null) for (const [path, content] of Object.entries(files)) this.cache[path] = content;
|
|
5838
|
+
}
|
|
5839
|
+
/**
|
|
5840
|
+
* Return linked-entry paths mapped to their repo handles.
|
|
5841
|
+
*/
|
|
5842
|
+
async getLinkedEntries() {
|
|
5843
|
+
await this.ensureCache();
|
|
5844
|
+
return { ...this.linkedEntries };
|
|
5845
|
+
}
|
|
5846
|
+
/**
|
|
5847
|
+
* Return true if the hub repo already exists with at least one commit.
|
|
5848
|
+
*/
|
|
5849
|
+
async hasPriorCommits() {
|
|
5850
|
+
await this.ensureCache();
|
|
5851
|
+
return this.commitHash !== null;
|
|
5852
|
+
}
|
|
5853
|
+
async ls(path = "/") {
|
|
5854
|
+
const hubPrefix = ContextHubBackend.stripPrefix(path).replace(/\/+$/, "");
|
|
5855
|
+
let cache;
|
|
5856
|
+
try {
|
|
5857
|
+
cache = await this.ensureCache();
|
|
5858
|
+
} catch (error) {
|
|
5859
|
+
if (isLangSmithError(error)) return { error: ContextHubBackend.toHubUnavailableError(error) };
|
|
5860
|
+
throw error;
|
|
5861
|
+
}
|
|
5862
|
+
const dirs = /* @__PURE__ */ new Set();
|
|
5863
|
+
const entries = [];
|
|
5864
|
+
for (const filePath of Object.keys(cache)) {
|
|
5865
|
+
if (hubPrefix && !filePath.startsWith(`${hubPrefix}/`)) continue;
|
|
5866
|
+
const relative = hubPrefix ? filePath.slice(hubPrefix.length + 1) : filePath;
|
|
5867
|
+
if (!relative) continue;
|
|
5868
|
+
const slashIndex = relative.indexOf("/");
|
|
5869
|
+
if (slashIndex === -1) {
|
|
5870
|
+
entries.push({
|
|
5871
|
+
path: `/${filePath}`,
|
|
5872
|
+
is_dir: false
|
|
5873
|
+
});
|
|
5874
|
+
continue;
|
|
5875
|
+
}
|
|
5876
|
+
const dirName = relative.slice(0, slashIndex);
|
|
5877
|
+
const dirPath = hubPrefix ? `${hubPrefix}/${dirName}` : dirName;
|
|
5878
|
+
if (!dirs.has(dirPath)) {
|
|
5879
|
+
dirs.add(dirPath);
|
|
5880
|
+
entries.push({
|
|
5881
|
+
path: `/${dirPath}`,
|
|
5882
|
+
is_dir: true
|
|
5883
|
+
});
|
|
5884
|
+
}
|
|
5885
|
+
}
|
|
5886
|
+
return { files: entries };
|
|
5887
|
+
}
|
|
5888
|
+
async read(filePath, offset = 0, limit = 2e3) {
|
|
5889
|
+
const hubPath = ContextHubBackend.stripPrefix(filePath);
|
|
5890
|
+
let cache;
|
|
5891
|
+
try {
|
|
5892
|
+
cache = await this.ensureCache();
|
|
5893
|
+
} catch (error) {
|
|
5894
|
+
if (isLangSmithError(error)) return { error: ContextHubBackend.toHubUnavailableError(error) };
|
|
5895
|
+
throw error;
|
|
5896
|
+
}
|
|
5897
|
+
const content = cache[hubPath];
|
|
5898
|
+
if (content === void 0) return { error: `File '${filePath}' not found` };
|
|
5899
|
+
const sliced = sliceReadContent(content, offset, limit);
|
|
5900
|
+
if (sliced.error) return { error: sliced.error };
|
|
5901
|
+
return {
|
|
5902
|
+
content: sliced.content ?? "",
|
|
5903
|
+
mimeType: TEXT_MIME_TYPE
|
|
5904
|
+
};
|
|
5905
|
+
}
|
|
5906
|
+
async readRaw(filePath) {
|
|
5907
|
+
const readResult = await this.read(filePath, 0, Number.MAX_SAFE_INTEGER);
|
|
5908
|
+
if (readResult.error || typeof readResult.content !== "string") return { error: readResult.error ?? `File '${filePath}' not found` };
|
|
5909
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
5910
|
+
return { data: {
|
|
5911
|
+
content: readResult.content,
|
|
5912
|
+
mimeType: TEXT_MIME_TYPE,
|
|
5913
|
+
created_at: now,
|
|
5914
|
+
modified_at: now
|
|
5915
|
+
} };
|
|
5916
|
+
}
|
|
5917
|
+
async grep(pattern, path = null, glob = null) {
|
|
5918
|
+
let cache;
|
|
5919
|
+
try {
|
|
5920
|
+
cache = await this.ensureCache();
|
|
5921
|
+
} catch (error) {
|
|
5922
|
+
if (isLangSmithError(error)) return { error: ContextHubBackend.toHubUnavailableError(error) };
|
|
5923
|
+
throw error;
|
|
5924
|
+
}
|
|
5925
|
+
const prefix = path ? ContextHubBackend.stripPrefix(path).replace(/\/+$/, "") : "";
|
|
5926
|
+
const matches = [];
|
|
5927
|
+
for (const [filePath, content] of Object.entries(cache)) {
|
|
5928
|
+
if (prefix && !filePath.startsWith(prefix)) continue;
|
|
5929
|
+
if (glob && !micromatch.default.isMatch(filePath, glob, FNMATCH_OPTIONS)) continue;
|
|
5930
|
+
const lines = content.split("\n");
|
|
5931
|
+
for (let index = 0; index < lines.length; index++) {
|
|
5932
|
+
const line = lines[index];
|
|
5933
|
+
if (line.includes(pattern)) matches.push({
|
|
5934
|
+
path: `/${filePath}`,
|
|
5935
|
+
line: index + 1,
|
|
5936
|
+
text: line
|
|
5937
|
+
});
|
|
5938
|
+
}
|
|
5939
|
+
}
|
|
5940
|
+
return { matches };
|
|
5941
|
+
}
|
|
5942
|
+
async glob(pattern, _path = "/") {
|
|
5943
|
+
let cache;
|
|
5944
|
+
try {
|
|
5945
|
+
cache = await this.ensureCache();
|
|
5946
|
+
} catch (error) {
|
|
5947
|
+
if (isLangSmithError(error)) return { error: ContextHubBackend.toHubUnavailableError(error) };
|
|
5948
|
+
throw error;
|
|
5949
|
+
}
|
|
5950
|
+
const files = [];
|
|
5951
|
+
for (const filePath of Object.keys(cache)) if (micromatch.default.isMatch(`/${filePath}`, pattern, FNMATCH_OPTIONS) || micromatch.default.isMatch(filePath, pattern, FNMATCH_OPTIONS)) files.push({
|
|
5952
|
+
path: `/${filePath}`,
|
|
5953
|
+
is_dir: false
|
|
5954
|
+
});
|
|
5955
|
+
return { files };
|
|
5956
|
+
}
|
|
5957
|
+
async write(filePath, content) {
|
|
5958
|
+
const hubPath = ContextHubBackend.stripPrefix(filePath);
|
|
5959
|
+
try {
|
|
5960
|
+
await this.ensureCache();
|
|
5961
|
+
await this.commit({ [hubPath]: content });
|
|
5962
|
+
} catch (error) {
|
|
5963
|
+
if (isLangSmithError(error)) {
|
|
5964
|
+
this.cache = null;
|
|
5965
|
+
return { error: ContextHubBackend.toHubUnavailableError(error) };
|
|
5966
|
+
}
|
|
5967
|
+
throw error;
|
|
5968
|
+
}
|
|
5969
|
+
return {
|
|
5970
|
+
path: filePath,
|
|
5971
|
+
filesUpdate: null
|
|
5972
|
+
};
|
|
5973
|
+
}
|
|
5974
|
+
async edit(filePath, oldString, newString, replaceAll = false) {
|
|
5975
|
+
const hubPath = ContextHubBackend.stripPrefix(filePath);
|
|
5976
|
+
try {
|
|
5977
|
+
const current = (await this.ensureCache())[hubPath];
|
|
5978
|
+
if (current === void 0) return { error: `Error: File '${filePath}' not found` };
|
|
5979
|
+
const replacementResult = performStringReplacement(current, oldString, newString, replaceAll);
|
|
5980
|
+
if (typeof replacementResult === "string") return { error: replacementResult };
|
|
5981
|
+
const [newContent, occurrences] = replacementResult;
|
|
5982
|
+
await this.commit({ [hubPath]: newContent });
|
|
5983
|
+
return {
|
|
5984
|
+
path: filePath,
|
|
5985
|
+
filesUpdate: null,
|
|
5986
|
+
occurrences
|
|
5987
|
+
};
|
|
5988
|
+
} catch (error) {
|
|
5989
|
+
if (isLangSmithError(error)) {
|
|
5990
|
+
this.cache = null;
|
|
5991
|
+
return { error: ContextHubBackend.toHubUnavailableError(error) };
|
|
5992
|
+
}
|
|
5993
|
+
throw error;
|
|
5994
|
+
}
|
|
5995
|
+
}
|
|
5996
|
+
async uploadFiles(files) {
|
|
5997
|
+
const decoder = new TextDecoder("utf-8", { fatal: true });
|
|
5998
|
+
const decoded = [];
|
|
5999
|
+
const validFiles = {};
|
|
6000
|
+
for (const [path, content] of files) try {
|
|
6001
|
+
const text = decoder.decode(content);
|
|
6002
|
+
decoded.push([path, text]);
|
|
6003
|
+
validFiles[ContextHubBackend.stripPrefix(path)] = text;
|
|
6004
|
+
} catch {
|
|
6005
|
+
decoded.push([path, null]);
|
|
6006
|
+
}
|
|
6007
|
+
let commitError = null;
|
|
6008
|
+
if (Object.keys(validFiles).length > 0) try {
|
|
6009
|
+
await this.ensureCache();
|
|
6010
|
+
await this.commit(validFiles);
|
|
6011
|
+
} catch (error) {
|
|
6012
|
+
if (isLangSmithError(error)) {
|
|
6013
|
+
this.cache = null;
|
|
6014
|
+
commitError = mapHubFileOperationError(error);
|
|
6015
|
+
} else throw error;
|
|
6016
|
+
}
|
|
6017
|
+
return decoded.map(([path, text]) => {
|
|
6018
|
+
if (text === null) return {
|
|
6019
|
+
path,
|
|
6020
|
+
error: "invalid_path"
|
|
6021
|
+
};
|
|
6022
|
+
if (commitError !== null) return {
|
|
6023
|
+
path,
|
|
6024
|
+
error: commitError
|
|
6025
|
+
};
|
|
6026
|
+
return {
|
|
6027
|
+
path,
|
|
6028
|
+
error: null
|
|
6029
|
+
};
|
|
6030
|
+
});
|
|
6031
|
+
}
|
|
6032
|
+
async downloadFiles(paths) {
|
|
6033
|
+
let cache;
|
|
6034
|
+
try {
|
|
6035
|
+
cache = await this.ensureCache();
|
|
6036
|
+
} catch (error) {
|
|
6037
|
+
if (isLangSmithError(error)) {
|
|
6038
|
+
const mappedError = mapHubFileOperationError(error);
|
|
6039
|
+
return paths.map((path) => ({
|
|
6040
|
+
path,
|
|
6041
|
+
content: null,
|
|
6042
|
+
error: mappedError
|
|
6043
|
+
}));
|
|
6044
|
+
}
|
|
6045
|
+
throw error;
|
|
6046
|
+
}
|
|
6047
|
+
const encoder = new TextEncoder();
|
|
6048
|
+
return paths.map((path) => {
|
|
6049
|
+
const hubPath = ContextHubBackend.stripPrefix(path);
|
|
6050
|
+
const content = cache[hubPath];
|
|
6051
|
+
if (content !== void 0) return {
|
|
6052
|
+
path,
|
|
6053
|
+
content: encoder.encode(content),
|
|
6054
|
+
error: null
|
|
6055
|
+
};
|
|
6056
|
+
return {
|
|
6057
|
+
path,
|
|
6058
|
+
content: null,
|
|
6059
|
+
error: "file_not_found"
|
|
6060
|
+
};
|
|
6061
|
+
});
|
|
6062
|
+
}
|
|
6063
|
+
};
|
|
6064
|
+
//#endregion
|
|
5729
6065
|
//#region src/backends/local-shell.ts
|
|
5730
6066
|
/**
|
|
5731
6067
|
* LocalShellBackend: Node.js implementation of the filesystem backend with unrestricted local shell execution.
|
|
@@ -6464,7 +6800,7 @@ var BaseSandbox = class {
|
|
|
6464
6800
|
* ```typescript
|
|
6465
6801
|
* import { LangSmithSandbox, createDeepAgent } from "deepagents";
|
|
6466
6802
|
*
|
|
6467
|
-
* const sandbox = await LangSmithSandbox.create({
|
|
6803
|
+
* const sandbox = await LangSmithSandbox.create({ snapshotId: "your-snapshot-id" });
|
|
6468
6804
|
*
|
|
6469
6805
|
* const agent = createDeepAgent({ model, backend: sandbox });
|
|
6470
6806
|
*
|
|
@@ -6588,6 +6924,41 @@ var LangSmithSandbox = class LangSmithSandbox extends BaseSandbox {
|
|
|
6588
6924
|
this.#isRunning = false;
|
|
6589
6925
|
}
|
|
6590
6926
|
/**
|
|
6927
|
+
* Start a stopped sandbox and wait until it is ready.
|
|
6928
|
+
*
|
|
6929
|
+
* After calling this, `isRunning` will be `true` and the sandbox
|
|
6930
|
+
* can be used for command execution and file operations again.
|
|
6931
|
+
*
|
|
6932
|
+
* @param options - Start options (timeout, signal).
|
|
6933
|
+
*/
|
|
6934
|
+
async start(options = {}) {
|
|
6935
|
+
await this.#sandbox.start(options);
|
|
6936
|
+
this.#isRunning = true;
|
|
6937
|
+
}
|
|
6938
|
+
/**
|
|
6939
|
+
* Stop the sandbox without deleting it.
|
|
6940
|
+
*
|
|
6941
|
+
* Sandbox files are preserved and the sandbox can be restarted later
|
|
6942
|
+
* with `start()`. After calling this, `isRunning` will be `false`.
|
|
6943
|
+
*/
|
|
6944
|
+
async stop() {
|
|
6945
|
+
await this.#sandbox.stop();
|
|
6946
|
+
this.#isRunning = false;
|
|
6947
|
+
}
|
|
6948
|
+
/**
|
|
6949
|
+
* Capture a snapshot from this running sandbox.
|
|
6950
|
+
*
|
|
6951
|
+
* Snapshots can be used to create new sandboxes via
|
|
6952
|
+
* `LangSmithSandbox.create({ snapshotId })`.
|
|
6953
|
+
*
|
|
6954
|
+
* @param name - Name for the snapshot.
|
|
6955
|
+
* @param options - Capture options (checkpoint, timeout).
|
|
6956
|
+
* @returns The created Snapshot in "ready" status.
|
|
6957
|
+
*/
|
|
6958
|
+
async captureSnapshot(name, options = {}) {
|
|
6959
|
+
return this.#sandbox.captureSnapshot(name, options);
|
|
6960
|
+
}
|
|
6961
|
+
/**
|
|
6591
6962
|
* Create and return a new LangSmithSandbox in one step.
|
|
6592
6963
|
*
|
|
6593
6964
|
* This is the recommended way to create a sandbox — no need to import
|
|
@@ -6595,7 +6966,10 @@ var LangSmithSandbox = class LangSmithSandbox extends BaseSandbox {
|
|
|
6595
6966
|
*
|
|
6596
6967
|
* @example
|
|
6597
6968
|
* ```typescript
|
|
6598
|
-
* const sandbox = await LangSmithSandbox.create({
|
|
6969
|
+
* const sandbox = await LangSmithSandbox.create({
|
|
6970
|
+
* snapshotId: "abc-123",
|
|
6971
|
+
* });
|
|
6972
|
+
*
|
|
6599
6973
|
* try {
|
|
6600
6974
|
* const agent = createDeepAgent({ model, backend: sandbox });
|
|
6601
6975
|
* await agent.invoke({ messages: [...] });
|
|
@@ -6604,10 +6978,14 @@ var LangSmithSandbox = class LangSmithSandbox extends BaseSandbox {
|
|
|
6604
6978
|
* }
|
|
6605
6979
|
* ```
|
|
6606
6980
|
*/
|
|
6607
|
-
static async create(options
|
|
6608
|
-
const { templateName
|
|
6981
|
+
static async create(options) {
|
|
6982
|
+
const { templateName, apiKey = process.env.LANGSMITH_API_KEY, defaultTimeout, snapshotId, ...createSandboxOptions } = options;
|
|
6983
|
+
if (snapshotId && templateName) throw new Error("snapshotId and templateName are mutually exclusive. Pass only one creation source.");
|
|
6984
|
+
if (!snapshotId && !templateName) throw new Error("Either snapshotId or templateName is required. snapshotId is recommended — template-based creation is deprecated.");
|
|
6985
|
+
const sandboxOptions = { ...createSandboxOptions };
|
|
6986
|
+
if (templateName) sandboxOptions.snapshotName = templateName;
|
|
6609
6987
|
return new LangSmithSandbox({
|
|
6610
|
-
sandbox: await new langsmith_experimental_sandbox.SandboxClient({ apiKey }).createSandbox(
|
|
6988
|
+
sandbox: await new langsmith_experimental_sandbox.SandboxClient({ apiKey }).createSandbox(snapshotId, sandboxOptions),
|
|
6611
6989
|
defaultTimeout
|
|
6612
6990
|
});
|
|
6613
6991
|
}
|
|
@@ -6959,6 +7337,717 @@ function createSubagentTransformer(path) {
|
|
|
6959
7337
|
};
|
|
6960
7338
|
}
|
|
6961
7339
|
//#endregion
|
|
7340
|
+
//#region src/profiles/keys.ts
|
|
7341
|
+
/**
|
|
7342
|
+
* Normalize and validate a profile registry key.
|
|
7343
|
+
*
|
|
7344
|
+
* Trims leading/trailing whitespace, then enforces the `"provider"` or
|
|
7345
|
+
* `"provider:model"` shape. Rejects empty strings, multiple colons, and
|
|
7346
|
+
* empty halves.
|
|
7347
|
+
*
|
|
7348
|
+
* @param key - The registry key to validate.
|
|
7349
|
+
* @returns The trimmed, validated key.
|
|
7350
|
+
* @throws {Error} When the key is malformed.
|
|
7351
|
+
*
|
|
7352
|
+
* @example
|
|
7353
|
+
* ```typescript
|
|
7354
|
+
* validateProfileKey("anthropic:claude-opus-4-7"); // "anthropic:claude-opus-4-7"
|
|
7355
|
+
* validateProfileKey(" openai "); // "openai"
|
|
7356
|
+
* validateProfileKey("openai:"); // throws
|
|
7357
|
+
* validateProfileKey(""); // throws
|
|
7358
|
+
* ```
|
|
7359
|
+
*/
|
|
7360
|
+
function validateProfileKey(key) {
|
|
7361
|
+
const trimmed = key.trim();
|
|
7362
|
+
if (!trimmed) throw new Error("Profile key must be a non-empty string");
|
|
7363
|
+
if (trimmed.split(":").length > 2) throw new Error(`Profile key "${trimmed}" has more than one ":"; expected "provider" or "provider:model"`);
|
|
7364
|
+
if (trimmed.includes(":")) {
|
|
7365
|
+
const [provider, model] = trimmed.split(":");
|
|
7366
|
+
if (!provider.trim() || !model.trim()) throw new Error(`Profile key "${trimmed}" has an empty provider or model half; expected "provider:model"`);
|
|
7367
|
+
}
|
|
7368
|
+
return trimmed;
|
|
7369
|
+
}
|
|
7370
|
+
//#endregion
|
|
7371
|
+
//#region src/profiles/harness/types.ts
|
|
7372
|
+
/**
|
|
7373
|
+
* Middleware names that provide essential agent capabilities and cannot
|
|
7374
|
+
* be excluded via `excludedMiddleware`.
|
|
7375
|
+
*
|
|
7376
|
+
* - `FilesystemMiddleware` backs all built-in file tools and enforces
|
|
7377
|
+
* filesystem permissions.
|
|
7378
|
+
* - `SubAgentMiddleware` backs the `task` tool for subagent delegation.
|
|
7379
|
+
*/
|
|
7380
|
+
const REQUIRED_MIDDLEWARE_NAMES = new Set(["FilesystemMiddleware", "SubAgentMiddleware"]);
|
|
7381
|
+
/**
|
|
7382
|
+
* Type guard: is this a fully-constructed HarnessProfile (frozen with
|
|
7383
|
+
* Set fields) or raw options?
|
|
7384
|
+
*
|
|
7385
|
+
* Options use arrays for `excludedTools`; profiles use `Set`. We
|
|
7386
|
+
* distinguish by checking whether `excludedTools` has a `.has` method
|
|
7387
|
+
* (present on Set, absent on Array).
|
|
7388
|
+
*/
|
|
7389
|
+
function isHarnessProfile(value) {
|
|
7390
|
+
return value.excludedTools != null && typeof value.excludedTools.has === "function" && !Array.isArray(value.excludedTools);
|
|
7391
|
+
}
|
|
7392
|
+
/**
|
|
7393
|
+
* Resolve middleware to a concrete array, invoking the factory if
|
|
7394
|
+
* needed.
|
|
7395
|
+
*
|
|
7396
|
+
* @internal
|
|
7397
|
+
*/
|
|
7398
|
+
function resolveMiddleware(middleware) {
|
|
7399
|
+
if (typeof middleware === "function") return middleware();
|
|
7400
|
+
return middleware;
|
|
7401
|
+
}
|
|
7402
|
+
//#endregion
|
|
7403
|
+
//#region src/profiles/harness/create.ts
|
|
7404
|
+
/**
|
|
7405
|
+
* Validate the grammar of an `excludedMiddleware` entry.
|
|
7406
|
+
*
|
|
7407
|
+
* Runs at profile construction time so malformed entries fail
|
|
7408
|
+
* immediately. Checks:
|
|
7409
|
+
*
|
|
7410
|
+
* 1. Non-empty, non-whitespace string.
|
|
7411
|
+
* 2. No colons (class-path `module:Class` syntax is reserved).
|
|
7412
|
+
* 3. No underscore prefix (private middleware is not part of the
|
|
7413
|
+
* exclusion surface).
|
|
7414
|
+
* 4. Not a required scaffolding name.
|
|
7415
|
+
*
|
|
7416
|
+
* @param name - The middleware name to validate.
|
|
7417
|
+
* @throws {Error} When the name violates any rule.
|
|
7418
|
+
*/
|
|
7419
|
+
function validateExcludedMiddlewareName(name) {
|
|
7420
|
+
if (!name || !name.trim()) throw new Error("excludedMiddleware entries must be non-empty, non-whitespace strings.");
|
|
7421
|
+
if (name.includes(":")) throw new Error(`excludedMiddleware entries must be plain middleware names; class-path syntax is not supported, got "${name}".`);
|
|
7422
|
+
if (name.startsWith("_")) throw new Error(`excludedMiddleware entry "${name}" cannot start with "_" (underscore-prefixed names refer to private middleware not part of the public exclusion surface).`);
|
|
7423
|
+
if (REQUIRED_MIDDLEWARE_NAMES.has(name)) throw new Error(`Cannot exclude required middleware "${name}" — it provides essential agent capabilities that the runtime depends on.`);
|
|
7424
|
+
}
|
|
7425
|
+
/**
|
|
7426
|
+
* Create a frozen {@link HarnessProfile} from user-provided options.
|
|
7427
|
+
*
|
|
7428
|
+
* Validates all fields, converts mutable collections to their
|
|
7429
|
+
* frozen counterparts, and returns a frozen object.
|
|
7430
|
+
* Empty options produce a no-op profile (all defaults).
|
|
7431
|
+
*
|
|
7432
|
+
* @param options - Partial profile configuration.
|
|
7433
|
+
* @returns A frozen, validated `HarnessProfile`.
|
|
7434
|
+
* @throws {Error} When any field violates validation rules (invalid
|
|
7435
|
+
* middleware names, scaffolding exclusion attempts).
|
|
7436
|
+
*
|
|
7437
|
+
* @example
|
|
7438
|
+
* ```typescript
|
|
7439
|
+
* const profile = createHarnessProfile({
|
|
7440
|
+
* systemPromptSuffix: "Think step by step.",
|
|
7441
|
+
* excludedTools: ["execute"],
|
|
7442
|
+
* });
|
|
7443
|
+
* ```
|
|
7444
|
+
*/
|
|
7445
|
+
function createHarnessProfile(options = {}) {
|
|
7446
|
+
for (const name of options.excludedMiddleware ?? []) validateExcludedMiddlewareName(name);
|
|
7447
|
+
const toolDescriptionOverrides = Object.freeze(Object.assign(Object.create(null), options.toolDescriptionOverrides));
|
|
7448
|
+
const generalPurposeSubagent = options.generalPurposeSubagent ? Object.freeze({ ...options.generalPurposeSubagent }) : void 0;
|
|
7449
|
+
const profile = {
|
|
7450
|
+
baseSystemPrompt: options.baseSystemPrompt,
|
|
7451
|
+
systemPromptSuffix: options.systemPromptSuffix,
|
|
7452
|
+
toolDescriptionOverrides,
|
|
7453
|
+
excludedTools: new Set(options.excludedTools),
|
|
7454
|
+
excludedMiddleware: new Set(options.excludedMiddleware),
|
|
7455
|
+
extraMiddleware: options.extraMiddleware ?? [],
|
|
7456
|
+
generalPurposeSubagent
|
|
7457
|
+
};
|
|
7458
|
+
return Object.freeze(profile);
|
|
7459
|
+
}
|
|
7460
|
+
/**
|
|
7461
|
+
* An empty no-op profile used as the default when no registered
|
|
7462
|
+
* profile matches. Avoids creating a new object on every miss.
|
|
7463
|
+
*/
|
|
7464
|
+
const EMPTY_HARNESS_PROFILE = createHarnessProfile();
|
|
7465
|
+
//#endregion
|
|
7466
|
+
//#region src/profiles/harness/serialization.ts
|
|
7467
|
+
const POISONED_KEYS = new Set([
|
|
7468
|
+
"__proto__",
|
|
7469
|
+
"constructor",
|
|
7470
|
+
"prototype"
|
|
7471
|
+
]);
|
|
7472
|
+
/**
|
|
7473
|
+
* Zod schema for the general-purpose subagent config section of an
|
|
7474
|
+
* external harness profile config file.
|
|
7475
|
+
*/
|
|
7476
|
+
const generalPurposeSubagentConfigSchema = zod_v4.z.object({
|
|
7477
|
+
enabled: zod_v4.z.boolean().optional(),
|
|
7478
|
+
description: zod_v4.z.string().optional(),
|
|
7479
|
+
systemPrompt: zod_v4.z.string().optional()
|
|
7480
|
+
}).strict();
|
|
7481
|
+
/**
|
|
7482
|
+
* Zod schema for parsing a harness profile from an external JSON or
|
|
7483
|
+
* YAML config file.
|
|
7484
|
+
*
|
|
7485
|
+
* Uses `.strict()` to reject unknown keys (catches typos early). Array
|
|
7486
|
+
* fields (`excludedTools`, `excludedMiddleware`) accept arrays of
|
|
7487
|
+
* strings; the result is passed to {@link createHarnessProfile} which
|
|
7488
|
+
* converts them to `Set`.
|
|
7489
|
+
*
|
|
7490
|
+
* Does not include `extraMiddleware` — middleware instances cannot be
|
|
7491
|
+
* represented in JSON/YAML.
|
|
7492
|
+
*
|
|
7493
|
+
* @example
|
|
7494
|
+
* ```typescript
|
|
7495
|
+
* import { readFileSync } from "fs";
|
|
7496
|
+
* import YAML from "yaml";
|
|
7497
|
+
*
|
|
7498
|
+
* const raw = YAML.parse(readFileSync("profile.yaml", "utf-8"));
|
|
7499
|
+
* const config = harnessProfileConfigSchema.parse(raw);
|
|
7500
|
+
* const profile = createHarnessProfile(config);
|
|
7501
|
+
* ```
|
|
7502
|
+
*/
|
|
7503
|
+
const harnessProfileConfigSchema = zod_v4.z.object({
|
|
7504
|
+
baseSystemPrompt: zod_v4.z.string().optional(),
|
|
7505
|
+
systemPromptSuffix: zod_v4.z.string().optional(),
|
|
7506
|
+
toolDescriptionOverrides: zod_v4.z.record(zod_v4.z.string(), zod_v4.z.string()).optional(),
|
|
7507
|
+
excludedTools: zod_v4.z.array(zod_v4.z.string()).optional(),
|
|
7508
|
+
excludedMiddleware: zod_v4.z.array(zod_v4.z.string()).optional(),
|
|
7509
|
+
generalPurposeSubagent: generalPurposeSubagentConfigSchema.optional()
|
|
7510
|
+
}).strict();
|
|
7511
|
+
/**
|
|
7512
|
+
* Recursively check an object for prototype-pollution keys.
|
|
7513
|
+
*
|
|
7514
|
+
* Rejects `__proto__`, `constructor`, and `prototype` at any nesting
|
|
7515
|
+
* depth. Called before Zod parsing so poisoned payloads never reach
|
|
7516
|
+
* schema validation.
|
|
7517
|
+
*/
|
|
7518
|
+
function rejectPoisonedKeys(value, path = "") {
|
|
7519
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) return;
|
|
7520
|
+
for (const key of Object.keys(value)) {
|
|
7521
|
+
if (POISONED_KEYS.has(key)) throw new Error(`Rejected dangerous key "${key}" at ${path || "root"} in harness profile config.`);
|
|
7522
|
+
rejectPoisonedKeys(value[key], path ? `${path}.${key}` : key);
|
|
7523
|
+
}
|
|
7524
|
+
}
|
|
7525
|
+
/**
|
|
7526
|
+
* Parse an untrusted JSON/YAML object into a validated
|
|
7527
|
+
* {@link HarnessProfile}.
|
|
7528
|
+
*
|
|
7529
|
+
* Combines Zod schema validation with prototype-pollution protection
|
|
7530
|
+
* and profile construction validation. Use this for any config data
|
|
7531
|
+
* that originates from files, network, or user input.
|
|
7532
|
+
*
|
|
7533
|
+
* @param data - Raw object from `JSON.parse()` or `YAML.parse()`.
|
|
7534
|
+
* @returns A frozen, validated `HarnessProfile`.
|
|
7535
|
+
* @throws {z.ZodError} When the data fails schema validation.
|
|
7536
|
+
* @throws {Error} When profile-level validation fails (e.g.,
|
|
7537
|
+
* scaffolding violation in `excludedMiddleware`).
|
|
7538
|
+
*/
|
|
7539
|
+
function parseHarnessProfileConfig(data) {
|
|
7540
|
+
rejectPoisonedKeys(data);
|
|
7541
|
+
return createHarnessProfile(harnessProfileConfigSchema.parse(data));
|
|
7542
|
+
}
|
|
7543
|
+
/**
|
|
7544
|
+
* Serialize a {@link HarnessProfile} to a JSON-compatible object.
|
|
7545
|
+
*
|
|
7546
|
+
* Omits `undefined` fields and `extraMiddleware` (runtime-only).
|
|
7547
|
+
* Throws if `extraMiddleware` contains instances — callers should
|
|
7548
|
+
* strip it before serializing if they've set it.
|
|
7549
|
+
*
|
|
7550
|
+
* @param profile - The profile to serialize.
|
|
7551
|
+
* @returns A plain object matching {@link HarnessProfileConfigData}.
|
|
7552
|
+
* @throws {Error} When `extraMiddleware` is non-empty (cannot be
|
|
7553
|
+
* serialized to JSON).
|
|
7554
|
+
*/
|
|
7555
|
+
function serializeProfile(profile) {
|
|
7556
|
+
if (resolveMiddleware(profile.extraMiddleware).length > 0) throw new Error("Cannot serialize a HarnessProfile with non-empty extraMiddleware — middleware instances are runtime-only and have no JSON representation.");
|
|
7557
|
+
const result = {};
|
|
7558
|
+
if (profile.baseSystemPrompt !== void 0) result.baseSystemPrompt = profile.baseSystemPrompt;
|
|
7559
|
+
if (profile.systemPromptSuffix !== void 0) result.systemPromptSuffix = profile.systemPromptSuffix;
|
|
7560
|
+
if (Object.keys(profile.toolDescriptionOverrides).length > 0) result.toolDescriptionOverrides = { ...profile.toolDescriptionOverrides };
|
|
7561
|
+
if (profile.excludedTools.size > 0) result.excludedTools = [...profile.excludedTools];
|
|
7562
|
+
if (profile.excludedMiddleware.size > 0) result.excludedMiddleware = [...profile.excludedMiddleware];
|
|
7563
|
+
if (profile.generalPurposeSubagent !== void 0) {
|
|
7564
|
+
const gp = {};
|
|
7565
|
+
if (profile.generalPurposeSubagent.enabled !== void 0) gp.enabled = profile.generalPurposeSubagent.enabled;
|
|
7566
|
+
if (profile.generalPurposeSubagent.description !== void 0) gp.description = profile.generalPurposeSubagent.description;
|
|
7567
|
+
if (profile.generalPurposeSubagent.systemPrompt !== void 0) gp.systemPrompt = profile.generalPurposeSubagent.systemPrompt;
|
|
7568
|
+
if (Object.keys(gp).length > 0) result.generalPurposeSubagent = gp;
|
|
7569
|
+
}
|
|
7570
|
+
return result;
|
|
7571
|
+
}
|
|
7572
|
+
//#endregion
|
|
7573
|
+
//#region src/profiles/harness/merge.ts
|
|
7574
|
+
/**
|
|
7575
|
+
* Merge two middleware sequences by `.name`.
|
|
7576
|
+
*
|
|
7577
|
+
* When the override has a middleware whose `.name` already appears in
|
|
7578
|
+
* the base, the override instance replaces the base instance at the
|
|
7579
|
+
* same position. Novel names from the override are appended. If the
|
|
7580
|
+
* base has duplicates of the same name, only the first is replaced;
|
|
7581
|
+
* later duplicates are dropped.
|
|
7582
|
+
*
|
|
7583
|
+
* Returns a factory to ensure fresh resolution on each call.
|
|
7584
|
+
*/
|
|
7585
|
+
function mergeMiddleware(base, override) {
|
|
7586
|
+
const baseArr = resolveMiddleware(base);
|
|
7587
|
+
const overrideArr = resolveMiddleware(override);
|
|
7588
|
+
if (baseArr.length === 0) return override;
|
|
7589
|
+
if (overrideArr.length === 0) return base;
|
|
7590
|
+
return () => {
|
|
7591
|
+
const baseSeq = resolveMiddleware(base);
|
|
7592
|
+
const overrideSeq = resolveMiddleware(override);
|
|
7593
|
+
const overrideByName = new Map(overrideSeq.map((m) => [m.name, m]));
|
|
7594
|
+
const merged = [];
|
|
7595
|
+
const replaced = /* @__PURE__ */ new Set();
|
|
7596
|
+
for (const entry of baseSeq) {
|
|
7597
|
+
const replacement = overrideByName.get(entry.name);
|
|
7598
|
+
if (replacement) {
|
|
7599
|
+
if (!replaced.has(entry.name)) {
|
|
7600
|
+
merged.push(replacement);
|
|
7601
|
+
replaced.add(entry.name);
|
|
7602
|
+
}
|
|
7603
|
+
} else merged.push(entry);
|
|
7604
|
+
}
|
|
7605
|
+
for (const entry of overrideSeq) if (!replaced.has(entry.name)) merged.push(entry);
|
|
7606
|
+
return merged;
|
|
7607
|
+
};
|
|
7608
|
+
}
|
|
7609
|
+
/**
|
|
7610
|
+
* Merge two GP subagent configs field-wise.
|
|
7611
|
+
*
|
|
7612
|
+
* Override wins per sub-field when not `undefined`; unset fields
|
|
7613
|
+
* inherit from base. Returns `undefined` only when both inputs are
|
|
7614
|
+
* `undefined`.
|
|
7615
|
+
*/
|
|
7616
|
+
function mergeGeneralPurposeSubagentConfigs(base, override) {
|
|
7617
|
+
if (base === void 0) return override;
|
|
7618
|
+
if (override === void 0) return base;
|
|
7619
|
+
return {
|
|
7620
|
+
enabled: override.enabled ?? base.enabled,
|
|
7621
|
+
description: override.description ?? base.description,
|
|
7622
|
+
systemPrompt: override.systemPrompt ?? base.systemPrompt
|
|
7623
|
+
};
|
|
7624
|
+
}
|
|
7625
|
+
/**
|
|
7626
|
+
* Merge two harness profiles, layering `override` on top of `base`.
|
|
7627
|
+
*
|
|
7628
|
+
* Merge semantics per field:
|
|
7629
|
+
*
|
|
7630
|
+
* | Field | Strategy |
|
|
7631
|
+
* |-------|----------|
|
|
7632
|
+
* | `baseSystemPrompt` | Override wins if not `undefined` |
|
|
7633
|
+
* | `systemPromptSuffix` | Override wins if not `undefined` |
|
|
7634
|
+
* | `toolDescriptionOverrides` | Object spread merge; override wins per key |
|
|
7635
|
+
* | `excludedTools` | Set union |
|
|
7636
|
+
* | `excludedMiddleware` | Set union |
|
|
7637
|
+
* | `extraMiddleware` | Merge by `.name`; override instance replaces base at same position; novel names appended |
|
|
7638
|
+
* | `generalPurposeSubagent` | Field-wise merge; override wins per sub-field |
|
|
7639
|
+
*
|
|
7640
|
+
* @param base - Lower-priority profile (e.g., provider-wide).
|
|
7641
|
+
* @param override - Higher-priority profile (e.g., exact model).
|
|
7642
|
+
* @returns A new merged profile.
|
|
7643
|
+
*/
|
|
7644
|
+
function mergeProfiles(base, override) {
|
|
7645
|
+
return createHarnessProfile({
|
|
7646
|
+
baseSystemPrompt: override.baseSystemPrompt ?? base.baseSystemPrompt,
|
|
7647
|
+
systemPromptSuffix: override.systemPromptSuffix ?? base.systemPromptSuffix,
|
|
7648
|
+
toolDescriptionOverrides: {
|
|
7649
|
+
...base.toolDescriptionOverrides,
|
|
7650
|
+
...override.toolDescriptionOverrides
|
|
7651
|
+
},
|
|
7652
|
+
excludedTools: [...base.excludedTools, ...override.excludedTools],
|
|
7653
|
+
excludedMiddleware: [...base.excludedMiddleware, ...override.excludedMiddleware],
|
|
7654
|
+
extraMiddleware: mergeMiddleware(base.extraMiddleware, override.extraMiddleware),
|
|
7655
|
+
generalPurposeSubagent: mergeGeneralPurposeSubagentConfigs(base.generalPurposeSubagent, override.generalPurposeSubagent)
|
|
7656
|
+
});
|
|
7657
|
+
}
|
|
7658
|
+
//#endregion
|
|
7659
|
+
//#region src/profiles/harness/builtins/anthropic-opus-4-7.ts
|
|
7660
|
+
const SYSTEM_PROMPT_SUFFIX$3 = `\
|
|
7661
|
+
<use_parallel_tool_calls>
|
|
7662
|
+
If you intend to call multiple tools and there are no dependencies between the tool calls, make all of the independent tool calls in parallel. Prioritize calling tools simultaneously whenever the actions can be done in parallel rather than sequentially. For example, when reading 3 files, run 3 tool calls in parallel to read all 3 files into context at the same time. Maximize use of parallel tool calls where possible to increase speed and efficiency. However, if some tool calls depend on previous calls to inform dependent values like the parameters, do NOT call these tools in parallel and instead call them sequentially. Never use placeholders or guess missing parameters in tool calls.
|
|
7663
|
+
</use_parallel_tool_calls>
|
|
7664
|
+
|
|
7665
|
+
<investigate_before_answering>
|
|
7666
|
+
Never speculate about code you have not opened. If the user references a specific file, you MUST read the file before answering. Make sure to investigate and read relevant files BEFORE answering questions about the codebase. Never make any claims about code before investigating unless you are certain of the correct answer - give grounded and hallucination-free answers.
|
|
7667
|
+
</investigate_before_answering>
|
|
7668
|
+
|
|
7669
|
+
<tool_result_reflection>
|
|
7670
|
+
After receiving tool results, carefully reflect on their quality and determine optimal next steps before proceeding. Use your thinking to plan and iterate based on this new information, and then take the best next action.
|
|
7671
|
+
</tool_result_reflection>
|
|
7672
|
+
|
|
7673
|
+
<tool_usage>
|
|
7674
|
+
When a task depends on the state of files, tests, or system output, use tools to observe that state directly rather than reasoning from memory about what it probably contains. Read files before describing them. Run tests before claiming they pass. Search the codebase before asserting a symbol does or does not exist. Active investigation with tools is the default mode of working, not a fallback.
|
|
7675
|
+
</tool_usage>
|
|
7676
|
+
|
|
7677
|
+
<subagent_usage>
|
|
7678
|
+
Do not spawn a subagent for work you can complete directly in a single response (e.g. refactoring a function you can already see).
|
|
7679
|
+
|
|
7680
|
+
Spawn multiple subagents in the same turn when fanning out across items or reading multiple files.
|
|
7681
|
+
</subagent_usage>`;
|
|
7682
|
+
/**
|
|
7683
|
+
* Register the built-in Claude Opus 4.7 harness profile.
|
|
7684
|
+
*
|
|
7685
|
+
* Layers a system-prompt suffix onto `anthropic:claude-opus-4-7`
|
|
7686
|
+
* tuned to the model's documented behaviors: parallel tool calls,
|
|
7687
|
+
* grounded answers, post-tool reflection, active investigation, and
|
|
7688
|
+
* subagent spawning guidance.
|
|
7689
|
+
*
|
|
7690
|
+
* @internal
|
|
7691
|
+
*/
|
|
7692
|
+
function register$3() {
|
|
7693
|
+
registerHarnessProfileImpl("anthropic:claude-opus-4-7", createHarnessProfile({ systemPromptSuffix: SYSTEM_PROMPT_SUFFIX$3 }));
|
|
7694
|
+
}
|
|
7695
|
+
//#endregion
|
|
7696
|
+
//#region src/profiles/harness/builtins/anthropic-sonnet-4-6.ts
|
|
7697
|
+
const SYSTEM_PROMPT_SUFFIX$2 = `\
|
|
7698
|
+
<use_parallel_tool_calls>
|
|
7699
|
+
If you intend to call multiple tools and there are no dependencies between the tool calls, make all of the independent tool calls in parallel. Prioritize calling tools simultaneously whenever the actions can be done in parallel rather than sequentially. For example, when reading 3 files, run 3 tool calls in parallel to read all 3 files into context at the same time. Maximize use of parallel tool calls where possible to increase speed and efficiency. However, if some tool calls depend on previous calls to inform dependent values like the parameters, do NOT call these tools in parallel and instead call them sequentially. Never use placeholders or guess missing parameters in tool calls.
|
|
7700
|
+
</use_parallel_tool_calls>
|
|
7701
|
+
|
|
7702
|
+
<investigate_before_answering>
|
|
7703
|
+
Never speculate about code you have not opened. If the user references a specific file, you MUST read the file before answering. Make sure to investigate and read relevant files BEFORE answering questions about the codebase. Never make any claims about code before investigating unless you are certain of the correct answer - give grounded and hallucination-free answers.
|
|
7704
|
+
</investigate_before_answering>
|
|
7705
|
+
|
|
7706
|
+
<tool_result_reflection>
|
|
7707
|
+
After receiving tool results, carefully reflect on their quality and determine optimal next steps before proceeding. Use your thinking to plan and iterate based on this new information, and then take the best next action.
|
|
7708
|
+
</tool_result_reflection>`;
|
|
7709
|
+
/**
|
|
7710
|
+
* Register the built-in Claude Sonnet 4.6 harness profile.
|
|
7711
|
+
*
|
|
7712
|
+
* Layers universal Claude guidance (parallel tool calls, grounded
|
|
7713
|
+
* answers, post-tool reflection) onto `anthropic:claude-sonnet-4-6`.
|
|
7714
|
+
*
|
|
7715
|
+
* No Sonnet-specific overlays — Anthropic's guidance for Sonnet 4.6
|
|
7716
|
+
* centers on API-level configuration rather than system-prompt
|
|
7717
|
+
* adjustments. This module exists as the audit anchor: its presence
|
|
7718
|
+
* documents the review and justifies the absence of model-specific
|
|
7719
|
+
* content.
|
|
7720
|
+
*
|
|
7721
|
+
* @internal
|
|
7722
|
+
*/
|
|
7723
|
+
function register$2() {
|
|
7724
|
+
registerHarnessProfileImpl("anthropic:claude-sonnet-4-6", createHarnessProfile({ systemPromptSuffix: SYSTEM_PROMPT_SUFFIX$2 }));
|
|
7725
|
+
}
|
|
7726
|
+
//#endregion
|
|
7727
|
+
//#region src/profiles/harness/builtins/anthropic-haiku-4-5.ts
|
|
7728
|
+
const SYSTEM_PROMPT_SUFFIX$1 = `\
|
|
7729
|
+
<use_parallel_tool_calls>
|
|
7730
|
+
If you intend to call multiple tools and there are no dependencies between the tool calls, make all of the independent tool calls in parallel. Prioritize calling tools simultaneously whenever the actions can be done in parallel rather than sequentially. For example, when reading 3 files, run 3 tool calls in parallel to read all 3 files into context at the same time. Maximize use of parallel tool calls where possible to increase speed and efficiency. However, if some tool calls depend on previous calls to inform dependent values like the parameters, do NOT call these tools in parallel and instead call them sequentially. Never use placeholders or guess missing parameters in tool calls.
|
|
7731
|
+
</use_parallel_tool_calls>
|
|
7732
|
+
|
|
7733
|
+
<investigate_before_answering>
|
|
7734
|
+
Never speculate about code you have not opened. If the user references a specific file, you MUST read the file before answering. Make sure to investigate and read relevant files BEFORE answering questions about the codebase. Never make any claims about code before investigating unless you are certain of the correct answer - give grounded and hallucination-free answers.
|
|
7735
|
+
</investigate_before_answering>
|
|
7736
|
+
|
|
7737
|
+
<tool_result_reflection>
|
|
7738
|
+
After receiving tool results, carefully reflect on their quality and determine optimal next steps before proceeding. Use your thinking to plan and iterate based on this new information, and then take the best next action.
|
|
7739
|
+
</tool_result_reflection>`;
|
|
7740
|
+
/**
|
|
7741
|
+
* Register the built-in Claude Haiku 4.5 harness profile.
|
|
7742
|
+
*
|
|
7743
|
+
* Same universal Claude guidance as Sonnet 4.6. No Haiku-specific
|
|
7744
|
+
* overlays.
|
|
7745
|
+
*
|
|
7746
|
+
* @internal
|
|
7747
|
+
*/
|
|
7748
|
+
function register$1() {
|
|
7749
|
+
registerHarnessProfileImpl("anthropic:claude-haiku-4-5", createHarnessProfile({ systemPromptSuffix: SYSTEM_PROMPT_SUFFIX$1 }));
|
|
7750
|
+
}
|
|
7751
|
+
//#endregion
|
|
7752
|
+
//#region src/profiles/harness/builtins/openai-codex.ts
|
|
7753
|
+
/**
|
|
7754
|
+
* Model specs that receive the Codex harness profile.
|
|
7755
|
+
*
|
|
7756
|
+
* All variants share the same trained response style, so a single
|
|
7757
|
+
* suffix works across the family.
|
|
7758
|
+
*/
|
|
7759
|
+
const CODEX_MODEL_SPECS = [
|
|
7760
|
+
"openai:gpt-5.1-codex",
|
|
7761
|
+
"openai:gpt-5.2-codex",
|
|
7762
|
+
"openai:gpt-5.3-codex"
|
|
7763
|
+
];
|
|
7764
|
+
const SYSTEM_PROMPT_SUFFIX = `\
|
|
7765
|
+
## Codex-Specific Behavior
|
|
7766
|
+
|
|
7767
|
+
- You are an autonomous senior engineer. Once given a direction, proactively \
|
|
7768
|
+
gather context, plan, implement, and verify without waiting for additional \
|
|
7769
|
+
prompts at each step.
|
|
7770
|
+
- Persist until the task is fully handled end-to-end within the current turn \
|
|
7771
|
+
whenever feasible. Do not stop at analysis or partial fixes; carry changes \
|
|
7772
|
+
through implementation, verification, and a clear explanation of outcomes.
|
|
7773
|
+
- Bias to action: default to implementing with reasonable assumptions. Do not \
|
|
7774
|
+
end your turn with clarifications unless truly blocked.
|
|
7775
|
+
- Do not communicate an upfront plan or status preamble before acting. Just act.
|
|
7776
|
+
|
|
7777
|
+
## Parallel Tool Use
|
|
7778
|
+
|
|
7779
|
+
- Before any tool call, decide ALL files and resources you will need.
|
|
7780
|
+
- Batch reads, searches, and other independent operations into parallel tool \
|
|
7781
|
+
calls instead of issuing them one at a time.
|
|
7782
|
+
- Only make sequential calls when you truly cannot determine the next step \
|
|
7783
|
+
without seeing a prior result.
|
|
7784
|
+
|
|
7785
|
+
## Plan Hygiene
|
|
7786
|
+
|
|
7787
|
+
- Before finishing, reconcile every TODO or plan item created via write_todos. \
|
|
7788
|
+
Mark each as done, blocked (with a one-sentence reason), or cancelled. Do not \
|
|
7789
|
+
finish with pending items.`;
|
|
7790
|
+
/**
|
|
7791
|
+
* Register the built-in Codex harness profiles.
|
|
7792
|
+
*
|
|
7793
|
+
* Registers the same profile under each Codex model spec. Per-model
|
|
7794
|
+
* keys (not the bare `"openai"` prefix) keep the default behavior of
|
|
7795
|
+
* non-Codex OpenAI models unchanged.
|
|
7796
|
+
*
|
|
7797
|
+
* @internal
|
|
7798
|
+
*/
|
|
7799
|
+
function register() {
|
|
7800
|
+
const profile = createHarnessProfile({ systemPromptSuffix: SYSTEM_PROMPT_SUFFIX });
|
|
7801
|
+
for (const spec of CODEX_MODEL_SPECS) registerHarnessProfileImpl(spec, profile);
|
|
7802
|
+
}
|
|
7803
|
+
//#endregion
|
|
7804
|
+
//#region src/profiles/harness/builtins/index.ts
|
|
7805
|
+
/**
|
|
7806
|
+
* Register all built-in harness profiles and snapshot the resulting
|
|
7807
|
+
* registry keys as the builtin baseline.
|
|
7808
|
+
*
|
|
7809
|
+
* Called once during lazy bootstrap by `ensureBuiltinsLoaded()`.
|
|
7810
|
+
* Uses `registerHarnessProfileImpl` internally (not the public
|
|
7811
|
+
* `registerHarnessProfile`) to avoid triggering re-entrant bootstrap.
|
|
7812
|
+
*
|
|
7813
|
+
* @internal
|
|
7814
|
+
*/
|
|
7815
|
+
function loadBuiltinProfiles() {
|
|
7816
|
+
register$3();
|
|
7817
|
+
register$2();
|
|
7818
|
+
register$1();
|
|
7819
|
+
register();
|
|
7820
|
+
snapshotBuiltinKeys();
|
|
7821
|
+
}
|
|
7822
|
+
//#endregion
|
|
7823
|
+
//#region src/profiles/harness/registry.ts
|
|
7824
|
+
/**
|
|
7825
|
+
* Process-global symbol key for the harness profile registry. The `.v1`
|
|
7826
|
+
* suffix is a version gate — bump it when the {@link HarnessProfileRegistry}
|
|
7827
|
+
* shape changes in a breaking way so that incompatible versions coexist
|
|
7828
|
+
* on `globalThis` without corrupting each other.
|
|
7829
|
+
*/
|
|
7830
|
+
const PROFILE_REGISTRY_KEY = Symbol.for("deepagents.harness-profiles.v1");
|
|
7831
|
+
/**
|
|
7832
|
+
* Returns the process-global registry, creating it on first access.
|
|
7833
|
+
*/
|
|
7834
|
+
function getHarnessProfileRegistry() {
|
|
7835
|
+
const global = globalThis;
|
|
7836
|
+
if (global[PROFILE_REGISTRY_KEY] == null) global[PROFILE_REGISTRY_KEY] = {
|
|
7837
|
+
profiles: /* @__PURE__ */ new Map(),
|
|
7838
|
+
builtinKeys: /* @__PURE__ */ new Set(),
|
|
7839
|
+
builtinsLoaded: false
|
|
7840
|
+
};
|
|
7841
|
+
return global[PROFILE_REGISTRY_KEY];
|
|
7842
|
+
}
|
|
7843
|
+
/**
|
|
7844
|
+
* Ensure lazy-loaded builtin profiles have been registered.
|
|
7845
|
+
*
|
|
7846
|
+
* Called by the public `registerHarnessProfile` and lookup functions.
|
|
7847
|
+
* Built-in registration modules call `registerHarnessProfileImpl`
|
|
7848
|
+
* directly to avoid re-entrant bootstrap.
|
|
7849
|
+
*
|
|
7850
|
+
* @internal
|
|
7851
|
+
*/
|
|
7852
|
+
function ensureBuiltinsLoaded() {
|
|
7853
|
+
const registry = getHarnessProfileRegistry();
|
|
7854
|
+
if (registry.builtinsLoaded) return;
|
|
7855
|
+
registry.builtinsLoaded = true;
|
|
7856
|
+
loadBuiltinProfiles();
|
|
7857
|
+
}
|
|
7858
|
+
/**
|
|
7859
|
+
* Snapshot the current registry keys as the builtin baseline.
|
|
7860
|
+
*
|
|
7861
|
+
* Called by the builtin loader after all built-in profiles are
|
|
7862
|
+
* registered. This allows {@link hasUserRegisteredProfiles} to
|
|
7863
|
+
* distinguish user registrations from built-ins.
|
|
7864
|
+
*
|
|
7865
|
+
* @internal
|
|
7866
|
+
*/
|
|
7867
|
+
function snapshotBuiltinKeys() {
|
|
7868
|
+
const registry = getHarnessProfileRegistry();
|
|
7869
|
+
registry.builtinKeys = new Set(registry.profiles.keys());
|
|
7870
|
+
}
|
|
7871
|
+
/**
|
|
7872
|
+
* Core registration implementation. Does not trigger lazy bootstrap.
|
|
7873
|
+
*
|
|
7874
|
+
* Used by built-in profile modules during bootstrap. External callers
|
|
7875
|
+
* should use {@link registerHarnessProfile} instead.
|
|
7876
|
+
*
|
|
7877
|
+
* @internal
|
|
7878
|
+
*/
|
|
7879
|
+
function registerHarnessProfileImpl(key, profile) {
|
|
7880
|
+
key = validateProfileKey(key);
|
|
7881
|
+
const { profiles } = getHarnessProfileRegistry();
|
|
7882
|
+
const existing = profiles.get(key);
|
|
7883
|
+
if (existing !== void 0) profiles.set(key, mergeProfiles(existing, profile));
|
|
7884
|
+
else profiles.set(key, profile);
|
|
7885
|
+
}
|
|
7886
|
+
/**
|
|
7887
|
+
* Register a harness profile for a provider or specific model.
|
|
7888
|
+
*
|
|
7889
|
+
* Accepts either a pre-built {@link HarnessProfile} (from
|
|
7890
|
+
* {@link createHarnessProfile}) or raw {@link HarnessProfileOptions}
|
|
7891
|
+
* that will be validated and frozen automatically.
|
|
7892
|
+
*
|
|
7893
|
+
* Registrations are **additive**: if a profile already exists under
|
|
7894
|
+
* `key`, the new profile is merged on top. The incoming profile's
|
|
7895
|
+
* fields win on scalar conflicts; set fields union; middleware
|
|
7896
|
+
* sequences merge by name.
|
|
7897
|
+
*
|
|
7898
|
+
* @param key - Either a bare provider (`"openai"`) for provider-wide
|
|
7899
|
+
* defaults, or `"provider:model"` for a per-model override.
|
|
7900
|
+
* @param profile - A `HarnessProfile` or options to build one from.
|
|
7901
|
+
* @throws {Error} When `key` is malformed or profile validation
|
|
7902
|
+
* fails.
|
|
7903
|
+
*
|
|
7904
|
+
* @example
|
|
7905
|
+
* ```typescript
|
|
7906
|
+
* import { registerHarnessProfile } from "@langchain/deepagents";
|
|
7907
|
+
*
|
|
7908
|
+
* registerHarnessProfile("openai", {
|
|
7909
|
+
* systemPromptSuffix: "Respond concisely.",
|
|
7910
|
+
* });
|
|
7911
|
+
*
|
|
7912
|
+
* registerHarnessProfile("openai:gpt-5.4", {
|
|
7913
|
+
* excludedTools: ["execute"],
|
|
7914
|
+
* });
|
|
7915
|
+
* ```
|
|
7916
|
+
*/
|
|
7917
|
+
function registerHarnessProfile(key, profile) {
|
|
7918
|
+
ensureBuiltinsLoaded();
|
|
7919
|
+
registerHarnessProfileImpl(key, isHarnessProfile(profile) ? profile : createHarnessProfile(profile));
|
|
7920
|
+
}
|
|
7921
|
+
/**
|
|
7922
|
+
* Look up the {@link HarnessProfile} for a model spec string.
|
|
7923
|
+
*
|
|
7924
|
+
* Resolution order:
|
|
7925
|
+
*
|
|
7926
|
+
* 1. **Exact match** on `spec` (e.g., `"openai:gpt-5.4"`).
|
|
7927
|
+
* 2. **Provider prefix** (everything before `:`) when `spec` contains
|
|
7928
|
+
* a colon and both halves are non-empty.
|
|
7929
|
+
* 3. When both exist, they are **merged** (provider as base, exact as
|
|
7930
|
+
* override).
|
|
7931
|
+
* 4. `undefined` when nothing matches.
|
|
7932
|
+
*
|
|
7933
|
+
* Malformed specs (empty, multiple colons, empty halves) return
|
|
7934
|
+
* `undefined` without consulting the registry.
|
|
7935
|
+
*
|
|
7936
|
+
* @param spec - Model spec in `"provider:model"` format, or a bare
|
|
7937
|
+
* provider/model identifier.
|
|
7938
|
+
* @returns The matching profile, or `undefined`.
|
|
7939
|
+
*/
|
|
7940
|
+
function getHarnessProfile(spec) {
|
|
7941
|
+
if (spec.split(":").length > 2) return;
|
|
7942
|
+
const colonIdx = spec.indexOf(":");
|
|
7943
|
+
const hasColon = colonIdx !== -1;
|
|
7944
|
+
const provider = hasColon ? spec.slice(0, colonIdx) : void 0;
|
|
7945
|
+
const model = hasColon ? spec.slice(colonIdx + 1) : void 0;
|
|
7946
|
+
if (hasColon && (!provider || !model)) return;
|
|
7947
|
+
ensureBuiltinsLoaded();
|
|
7948
|
+
const { profiles } = getHarnessProfileRegistry();
|
|
7949
|
+
const exact = profiles.get(spec);
|
|
7950
|
+
const base = provider ? profiles.get(provider) : void 0;
|
|
7951
|
+
if (exact !== void 0 && base !== void 0) return mergeProfiles(base, exact);
|
|
7952
|
+
return exact ?? base;
|
|
7953
|
+
}
|
|
7954
|
+
/**
|
|
7955
|
+
* Resolve the harness profile for a model, falling back to the
|
|
7956
|
+
* empty default when nothing matches.
|
|
7957
|
+
*
|
|
7958
|
+
* When `spec` is set (the original model parameter), it drives the
|
|
7959
|
+
* lookup directly. When absent (pre-built model instance),
|
|
7960
|
+
* `providerHint` and `identifierHint` are used to construct lookup
|
|
7961
|
+
* keys.
|
|
7962
|
+
*
|
|
7963
|
+
* @param opts - Model metadata used to resolve the profile.
|
|
7964
|
+
* @returns The resolved profile (never `undefined`).
|
|
7965
|
+
*
|
|
7966
|
+
* @internal
|
|
7967
|
+
*/
|
|
7968
|
+
function resolveHarnessProfile(opts = {}) {
|
|
7969
|
+
const { spec, providerHint, identifierHint } = opts;
|
|
7970
|
+
if (spec !== void 0) return getHarnessProfile(spec) ?? EMPTY_HARNESS_PROFILE;
|
|
7971
|
+
if (providerHint && identifierHint && !identifierHint.includes(":")) {
|
|
7972
|
+
const profile = getHarnessProfile(`${providerHint}:${identifierHint}`);
|
|
7973
|
+
if (profile) return profile;
|
|
7974
|
+
}
|
|
7975
|
+
if (identifierHint && identifierHint.includes(":")) {
|
|
7976
|
+
const profile = getHarnessProfile(identifierHint);
|
|
7977
|
+
if (profile) return profile;
|
|
7978
|
+
}
|
|
7979
|
+
if (providerHint) {
|
|
7980
|
+
const profile = getHarnessProfile(providerHint);
|
|
7981
|
+
if (profile) return profile;
|
|
7982
|
+
}
|
|
7983
|
+
return EMPTY_HARNESS_PROFILE;
|
|
7984
|
+
}
|
|
7985
|
+
/**
|
|
7986
|
+
* Apply a profile's prompt overlay to a base prompt string.
|
|
7987
|
+
*
|
|
7988
|
+
* - `baseSystemPrompt` (when set) replaces `basePrompt` entirely.
|
|
7989
|
+
* - `systemPromptSuffix` (when set) is appended with `\n\n`.
|
|
7990
|
+
*
|
|
7991
|
+
* Both are independently optional. A profile that sets only the suffix
|
|
7992
|
+
* layers it on top of whatever base the caller passes in.
|
|
7993
|
+
*
|
|
7994
|
+
* Used uniformly for the main agent, declarative subagents, and the
|
|
7995
|
+
* auto-added general-purpose subagent.
|
|
7996
|
+
*
|
|
7997
|
+
* @param profile - The harness profile to apply.
|
|
7998
|
+
* @param basePrompt - The default base prompt (e.g., `BASE_AGENT_PROMPT`).
|
|
7999
|
+
* @returns The assembled prompt string.
|
|
8000
|
+
*/
|
|
8001
|
+
function applyProfilePrompt(profile, basePrompt) {
|
|
8002
|
+
const prompt = profile.baseSystemPrompt !== void 0 ? profile.baseSystemPrompt : basePrompt;
|
|
8003
|
+
if (profile.systemPromptSuffix !== void 0) return `${prompt}\n\n${profile.systemPromptSuffix}`;
|
|
8004
|
+
return prompt;
|
|
8005
|
+
}
|
|
8006
|
+
//#endregion
|
|
8007
|
+
//#region src/utils.ts
|
|
8008
|
+
/**
|
|
8009
|
+
* Detect whether a model is an Anthropic model.
|
|
8010
|
+
*
|
|
8011
|
+
* Used to gate Anthropic-specific prompt caching optimizations
|
|
8012
|
+
* (cache_control breakpoints).
|
|
8013
|
+
*/
|
|
8014
|
+
function isAnthropicModel(model) {
|
|
8015
|
+
if (typeof model === "string") {
|
|
8016
|
+
if (model.includes(":")) return model.split(":")[0] === "anthropic";
|
|
8017
|
+
return model.startsWith("claude");
|
|
8018
|
+
}
|
|
8019
|
+
if (model.getName() === "ConfigurableModel") return model._defaultConfig?.modelProvider === "anthropic";
|
|
8020
|
+
return model.getName() === "ChatAnthropic";
|
|
8021
|
+
}
|
|
8022
|
+
/**
|
|
8023
|
+
* Extract the provider name from a model instance for profile lookup.
|
|
8024
|
+
*
|
|
8025
|
+
* Checks `_defaultConfig.modelProvider` (ConfigurableModel) and falls
|
|
8026
|
+
* back to known model class name → provider mappings.
|
|
8027
|
+
*
|
|
8028
|
+
* @internal
|
|
8029
|
+
*/
|
|
8030
|
+
function getModelProvider(model) {
|
|
8031
|
+
if (model.getName() === "ConfigurableModel") return model._defaultConfig?.modelProvider;
|
|
8032
|
+
return {
|
|
8033
|
+
ChatAnthropic: "anthropic",
|
|
8034
|
+
ChatOpenAI: "openai",
|
|
8035
|
+
ChatGoogleGenerativeAI: "google"
|
|
8036
|
+
}[model.getName()];
|
|
8037
|
+
}
|
|
8038
|
+
/**
|
|
8039
|
+
* Extract the model identifier from a model instance for profile
|
|
8040
|
+
* lookup.
|
|
8041
|
+
*
|
|
8042
|
+
* Checks `_defaultConfig.model`, `model_name`, and `modelName` in
|
|
8043
|
+
* that order.
|
|
8044
|
+
*
|
|
8045
|
+
* @internal
|
|
8046
|
+
*/
|
|
8047
|
+
function getModelIdentifier(model) {
|
|
8048
|
+
return (model.getName() === "ConfigurableModel" ? model._defaultConfig : void 0)?.model ?? model.model_name ?? model.modelName ?? void 0;
|
|
8049
|
+
}
|
|
8050
|
+
//#endregion
|
|
6962
8051
|
//#region src/agent.ts
|
|
6963
8052
|
const BASE_AGENT_PROMPT = langchain.context`
|
|
6964
8053
|
You are a Deep Agent, an AI assistant that helps users accomplish tasks using tools. You respond with text and tool calls. The user can see your responses and tool outputs in real time.
|
|
@@ -7002,18 +8091,6 @@ const BUILTIN_TOOL_NAMES = new Set([
|
|
|
7002
8091
|
"write_todos"
|
|
7003
8092
|
]);
|
|
7004
8093
|
/**
|
|
7005
|
-
* Detect whether a model is an Anthropic model.
|
|
7006
|
-
* Used to gate Anthropic-specific prompt caching optimizations (cache_control breakpoints).
|
|
7007
|
-
*/
|
|
7008
|
-
function isAnthropicModel(model) {
|
|
7009
|
-
if (typeof model === "string") {
|
|
7010
|
-
if (model.includes(":")) return model.split(":")[0] === "anthropic";
|
|
7011
|
-
return model.startsWith("claude");
|
|
7012
|
-
}
|
|
7013
|
-
if (model.getName() === "ConfigurableModel") return model._defaultConfig?.modelProvider === "anthropic";
|
|
7014
|
-
return model.getName() === "ChatAnthropic";
|
|
7015
|
-
}
|
|
7016
|
-
/**
|
|
7017
8094
|
* Create a Deep Agent.
|
|
7018
8095
|
*
|
|
7019
8096
|
* This is the main entry point for building a production-style agent with
|
|
@@ -7047,6 +8124,12 @@ function createDeepAgent(params = {}) {
|
|
|
7047
8124
|
const { model = "anthropic:claude-sonnet-4-6", tools = [], systemPrompt, middleware: customMiddleware = [], subagents = [], responseFormat, contextSchema, checkpointer, store, backend = (config) => new StateBackend(config), interruptOn, name, memory, skills, permissions = [], streamTransformers = [] } = params;
|
|
7048
8125
|
const collidingTools = tools.map((t) => t.name).filter((n) => typeof n === "string" && BUILTIN_TOOL_NAMES.has(n));
|
|
7049
8126
|
if (collidingTools.length > 0) throw new ConfigurationError(`Tool name(s) [${collidingTools.join(", ")}] conflict with built-in tools. Rename your custom tools to avoid this.`, "TOOL_NAME_COLLISION");
|
|
8127
|
+
const harnessProfile = typeof model === "string" ? resolveHarnessProfile({ spec: model }) : resolveHarnessProfile({
|
|
8128
|
+
providerHint: getModelProvider(model),
|
|
8129
|
+
identifierHint: getModelIdentifier(model)
|
|
8130
|
+
});
|
|
8131
|
+
const toolOverrides = harnessProfile.toolDescriptionOverrides;
|
|
8132
|
+
const effectiveTools = Object.keys(toolOverrides).length > 0 ? tools.map((t) => t.name in toolOverrides ? Object.assign(Object.create(Object.getPrototypeOf(t)), t, { description: toolOverrides[t.name] }) : t) : tools;
|
|
7050
8133
|
const anthropicModel = isAnthropicModel(model);
|
|
7051
8134
|
const cacheMiddleware = anthropicModel ? [(0, langchain.anthropicPromptCachingMiddleware)({
|
|
7052
8135
|
unsupportedModelBehavior: "ignore",
|
|
@@ -7085,12 +8168,16 @@ function createDeepAgent(params = {}) {
|
|
|
7085
8168
|
const allSubagents = subagents;
|
|
7086
8169
|
const asyncSubAgents = allSubagents.filter((item) => isAsyncSubAgent(item));
|
|
7087
8170
|
const inlineSubagents = allSubagents.filter((item) => !isAsyncSubAgent(item)).map((item) => "runnable" in item ? item : normalizeSubagentSpec(item));
|
|
7088
|
-
|
|
8171
|
+
const gpConfig = harnessProfile.generalPurposeSubagent;
|
|
8172
|
+
if (!(gpConfig?.enabled === false) && !inlineSubagents.some((item) => item.name === GENERAL_PURPOSE_SUBAGENT["name"])) {
|
|
8173
|
+
const gpSystemPrompt = gpConfig?.systemPrompt ?? applyProfilePrompt(harnessProfile, GENERAL_PURPOSE_SUBAGENT.systemPrompt);
|
|
7089
8174
|
const generalPurposeSpec = normalizeSubagentSpec({
|
|
7090
8175
|
...GENERAL_PURPOSE_SUBAGENT,
|
|
8176
|
+
description: gpConfig?.description ?? GENERAL_PURPOSE_SUBAGENT.description,
|
|
8177
|
+
systemPrompt: gpSystemPrompt,
|
|
7091
8178
|
model,
|
|
7092
8179
|
skills,
|
|
7093
|
-
tools
|
|
8180
|
+
tools: effectiveTools
|
|
7094
8181
|
});
|
|
7095
8182
|
inlineSubagents.unshift(generalPurposeSpec);
|
|
7096
8183
|
}
|
|
@@ -7106,7 +8193,7 @@ function createDeepAgent(params = {}) {
|
|
|
7106
8193
|
}),
|
|
7107
8194
|
createSubAgentMiddleware({
|
|
7108
8195
|
defaultModel: model,
|
|
7109
|
-
defaultTools:
|
|
8196
|
+
defaultTools: effectiveTools,
|
|
7110
8197
|
defaultInterruptOn: interruptOn,
|
|
7111
8198
|
subagents: inlineSubagents,
|
|
7112
8199
|
generalPurposeAgent: false
|
|
@@ -7131,6 +8218,31 @@ function createDeepAgent(params = {}) {
|
|
|
7131
8218
|
})] : [],
|
|
7132
8219
|
...interruptOn ? [(0, langchain.humanInTheLoopMiddleware)({ interruptOn })] : []
|
|
7133
8220
|
];
|
|
8221
|
+
const profileMiddleware = resolveMiddleware(harnessProfile.extraMiddleware);
|
|
8222
|
+
if (profileMiddleware.length > 0) {
|
|
8223
|
+
const cacheIdx = middleware.findIndex((m) => m.name === "AnthropicPromptCachingMiddleware");
|
|
8224
|
+
if (cacheIdx !== -1) middleware.splice(cacheIdx, 0, ...profileMiddleware);
|
|
8225
|
+
else middleware.push(...profileMiddleware);
|
|
8226
|
+
}
|
|
8227
|
+
if (harnessProfile.excludedMiddleware.size > 0) {
|
|
8228
|
+
const excluded = harnessProfile.excludedMiddleware;
|
|
8229
|
+
const filtered = middleware.filter((m) => !excluded.has(m.name));
|
|
8230
|
+
middleware.length = 0;
|
|
8231
|
+
middleware.push(...filtered);
|
|
8232
|
+
}
|
|
8233
|
+
if (harnessProfile.excludedTools.size > 0) {
|
|
8234
|
+
const excludedTools = harnessProfile.excludedTools;
|
|
8235
|
+
middleware.push((0, langchain.createMiddleware)({
|
|
8236
|
+
name: "_ToolExclusionMiddleware",
|
|
8237
|
+
wrapModelCall: async (request, handler) => {
|
|
8238
|
+
return handler({
|
|
8239
|
+
...request,
|
|
8240
|
+
tools: request.tools?.filter((t) => !excludedTools.has(t.name))
|
|
8241
|
+
});
|
|
8242
|
+
}
|
|
8243
|
+
}));
|
|
8244
|
+
}
|
|
8245
|
+
const effectiveBasePrompt = applyProfilePrompt(harnessProfile, BASE_AGENT_PROMPT);
|
|
7134
8246
|
/**
|
|
7135
8247
|
* Return as DeepAgent with proper DeepAgentTypeConfig
|
|
7136
8248
|
* - Response: InferStructuredResponse<TResponse> (unwraps ToolStrategy<T>/ProviderStrategy<T> → T)
|
|
@@ -7148,15 +8260,15 @@ function createDeepAgent(params = {}) {
|
|
|
7148
8260
|
text: systemPrompt
|
|
7149
8261
|
}, {
|
|
7150
8262
|
type: "text",
|
|
7151
|
-
text:
|
|
8263
|
+
text: effectiveBasePrompt
|
|
7152
8264
|
}] }) : langchain.SystemMessage.isInstance(systemPrompt) ? new langchain.SystemMessage({ contentBlocks: [...systemPrompt.contentBlocks, {
|
|
7153
8265
|
type: "text",
|
|
7154
|
-
text:
|
|
8266
|
+
text: effectiveBasePrompt
|
|
7155
8267
|
}] }) : new langchain.SystemMessage({ contentBlocks: [{
|
|
7156
8268
|
type: "text",
|
|
7157
|
-
text:
|
|
8269
|
+
text: effectiveBasePrompt
|
|
7158
8270
|
}] }),
|
|
7159
|
-
tools,
|
|
8271
|
+
tools: effectiveTools,
|
|
7160
8272
|
middleware,
|
|
7161
8273
|
...responseFormat !== null && { responseFormat },
|
|
7162
8274
|
contextSchema,
|
|
@@ -7688,8 +8800,10 @@ function listSkills(options) {
|
|
|
7688
8800
|
exports.BaseSandbox = BaseSandbox;
|
|
7689
8801
|
exports.CompositeBackend = CompositeBackend;
|
|
7690
8802
|
exports.ConfigurationError = ConfigurationError;
|
|
8803
|
+
exports.ContextHubBackend = ContextHubBackend;
|
|
7691
8804
|
exports.DEFAULT_GENERAL_PURPOSE_DESCRIPTION = DEFAULT_GENERAL_PURPOSE_DESCRIPTION;
|
|
7692
8805
|
exports.DEFAULT_SUBAGENT_PROMPT = DEFAULT_SUBAGENT_PROMPT;
|
|
8806
|
+
exports.EMPTY_HARNESS_PROFILE = EMPTY_HARNESS_PROFILE;
|
|
7693
8807
|
exports.FilesystemBackend = FilesystemBackend;
|
|
7694
8808
|
exports.GENERAL_PURPOSE_SUBAGENT = GENERAL_PURPOSE_SUBAGENT;
|
|
7695
8809
|
exports.LangSmithSandbox = LangSmithSandbox;
|
|
@@ -7697,6 +8811,7 @@ exports.LocalShellBackend = LocalShellBackend;
|
|
|
7697
8811
|
exports.MAX_SKILL_DESCRIPTION_LENGTH = MAX_SKILL_DESCRIPTION_LENGTH;
|
|
7698
8812
|
exports.MAX_SKILL_FILE_SIZE = MAX_SKILL_FILE_SIZE;
|
|
7699
8813
|
exports.MAX_SKILL_NAME_LENGTH = MAX_SKILL_NAME_LENGTH;
|
|
8814
|
+
exports.REQUIRED_MIDDLEWARE_NAMES = REQUIRED_MIDDLEWARE_NAMES;
|
|
7700
8815
|
exports.SandboxError = SandboxError;
|
|
7701
8816
|
exports.StateBackend = StateBackend;
|
|
7702
8817
|
exports.StoreBackend = StoreBackend;
|
|
@@ -7709,6 +8824,7 @@ exports.createAsyncSubAgentMiddleware = createAsyncSubAgentMiddleware;
|
|
|
7709
8824
|
exports.createCompletionCallbackMiddleware = createCompletionCallbackMiddleware;
|
|
7710
8825
|
exports.createDeepAgent = createDeepAgent;
|
|
7711
8826
|
exports.createFilesystemMiddleware = createFilesystemMiddleware;
|
|
8827
|
+
exports.createHarnessProfile = createHarnessProfile;
|
|
7712
8828
|
exports.createMemoryMiddleware = createMemoryMiddleware;
|
|
7713
8829
|
exports.createPatchToolCallsMiddleware = createPatchToolCallsMiddleware;
|
|
7714
8830
|
exports.createSettings = createSettings;
|
|
@@ -7718,11 +8834,17 @@ exports.createSubagentTransformer = createSubagentTransformer;
|
|
|
7718
8834
|
exports.createSummarizationMiddleware = createSummarizationMiddleware;
|
|
7719
8835
|
exports.filesValue = filesValue;
|
|
7720
8836
|
exports.findProjectRoot = findProjectRoot;
|
|
8837
|
+
exports.generalPurposeSubagentConfigSchema = generalPurposeSubagentConfigSchema;
|
|
8838
|
+
exports.getHarnessProfile = getHarnessProfile;
|
|
8839
|
+
exports.harnessProfileConfigSchema = harnessProfileConfigSchema;
|
|
7721
8840
|
exports.isAsyncSubAgent = isAsyncSubAgent;
|
|
7722
8841
|
exports.isSandboxBackend = isSandboxBackend;
|
|
7723
8842
|
exports.isSandboxProtocol = isSandboxProtocol;
|
|
7724
8843
|
exports.listSkills = listSkills;
|
|
8844
|
+
exports.parseHarnessProfileConfig = parseHarnessProfileConfig;
|
|
7725
8845
|
exports.parseSkillMetadata = parseSkillMetadata;
|
|
8846
|
+
exports.registerHarnessProfile = registerHarnessProfile;
|
|
7726
8847
|
exports.resolveBackend = resolveBackend;
|
|
8848
|
+
exports.serializeProfile = serializeProfile;
|
|
7727
8849
|
|
|
7728
8850
|
//# sourceMappingURL=index.cjs.map
|