@botbotgo/agent-harness 0.0.134 → 0.0.136
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/README.md +110 -49
- package/README.zh.md +102 -49
- package/dist/config/agents/direct.yaml +70 -71
- package/dist/config/agents/orchestra.yaml +90 -91
- package/dist/contracts/workspace.d.ts +12 -2
- package/dist/extensions.js +13 -1
- package/dist/init-project.js +19 -21
- package/dist/package-version.d.ts +1 -1
- package/dist/package-version.js +1 -1
- package/dist/resource/mcp-tool-support.d.ts +4 -0
- package/dist/resource/mcp-tool-support.js +112 -35
- package/dist/resource/resource-impl.js +199 -7
- package/dist/runtime/adapter/runtime-shell.d.ts +3 -1
- package/dist/runtime/adapter/runtime-shell.js +2 -1
- package/dist/runtime/adapter/tool/tool-arguments.js +1 -0
- package/dist/runtime/adapter/tool/tool-hitl.js +3 -0
- package/dist/runtime/agent-runtime-adapter.d.ts +6 -0
- package/dist/runtime/agent-runtime-adapter.js +32 -2
- package/dist/runtime/harness.js +2 -0
- package/dist/tool-modules.d.ts +5 -0
- package/dist/tool-modules.js +10 -0
- package/dist/workspace/agent-binding-compiler.d.ts +2 -2
- package/dist/workspace/agent-binding-compiler.js +78 -6
- package/dist/workspace/compile.js +150 -6
- package/dist/workspace/object-loader.js +148 -53
- package/dist/workspace/resource-compilers.js +6 -0
- package/dist/workspace/support/source-collectors.js +1 -1
- package/dist/workspace/support/workspace-ref-utils.d.ts +1 -0
- package/dist/workspace/support/workspace-ref-utils.js +9 -0
- package/dist/workspace/tool-hydration.js +87 -13
- package/dist/workspace/yaml-object-reader.js +40 -13
- package/package.json +1 -1
|
@@ -20,6 +20,18 @@ function toMcpServerConfig(server) {
|
|
|
20
20
|
function readStringArray(value) {
|
|
21
21
|
return Array.isArray(value) ? value.filter((item) => typeof item === "string" && item.trim().length > 0) : [];
|
|
22
22
|
}
|
|
23
|
+
function readStringRecord(value) {
|
|
24
|
+
if (typeof value !== "object" || !value || Array.isArray(value)) {
|
|
25
|
+
return undefined;
|
|
26
|
+
}
|
|
27
|
+
const entries = Object.entries(value).filter((entry) => typeof entry[1] === "string");
|
|
28
|
+
return entries.length > 0 ? Object.fromEntries(entries) : undefined;
|
|
29
|
+
}
|
|
30
|
+
function asObject(value) {
|
|
31
|
+
return typeof value === "object" && value !== null && !Array.isArray(value)
|
|
32
|
+
? value
|
|
33
|
+
: undefined;
|
|
34
|
+
}
|
|
23
35
|
function compileRegexList(value, label) {
|
|
24
36
|
return readStringArray(value).map((pattern) => {
|
|
25
37
|
try {
|
|
@@ -53,6 +65,52 @@ function shouldIncludeRemoteMcpTool(filter, toolName) {
|
|
|
53
65
|
}
|
|
54
66
|
return true;
|
|
55
67
|
}
|
|
68
|
+
function normalizeAgentMcpServerUsage(item) {
|
|
69
|
+
const config = asObject(item.config);
|
|
70
|
+
if (!config) {
|
|
71
|
+
return item;
|
|
72
|
+
}
|
|
73
|
+
return {
|
|
74
|
+
...config,
|
|
75
|
+
...item,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
function hasMcpServerUsageOverrides(item) {
|
|
79
|
+
return [
|
|
80
|
+
"transport",
|
|
81
|
+
"command",
|
|
82
|
+
"args",
|
|
83
|
+
"env",
|
|
84
|
+
"cwd",
|
|
85
|
+
"url",
|
|
86
|
+
"token",
|
|
87
|
+
"headers",
|
|
88
|
+
].some((key) => item[key] !== undefined);
|
|
89
|
+
}
|
|
90
|
+
function mergeReferencedMcpServer(referencedServer, item, agentId, name, sourcePath) {
|
|
91
|
+
return {
|
|
92
|
+
id: `${agentId}.${name}`,
|
|
93
|
+
transport: item.transport === "stdio" || item.transport === "http" || item.transport === "sse" || item.transport === "websocket"
|
|
94
|
+
? item.transport
|
|
95
|
+
: referencedServer.transport,
|
|
96
|
+
url: typeof item.url === "string" ? item.url : referencedServer.url,
|
|
97
|
+
command: typeof item.command === "string" ? item.command : referencedServer.command,
|
|
98
|
+
args: Array.isArray(item.args)
|
|
99
|
+
? item.args.filter((entry) => typeof entry === "string")
|
|
100
|
+
: referencedServer.args,
|
|
101
|
+
env: {
|
|
102
|
+
...(referencedServer.env ?? {}),
|
|
103
|
+
...(readStringRecord(item.env) ?? {}),
|
|
104
|
+
},
|
|
105
|
+
cwd: typeof item.cwd === "string" ? item.cwd : referencedServer.cwd,
|
|
106
|
+
token: typeof item.token === "string" ? item.token : referencedServer.token,
|
|
107
|
+
headers: {
|
|
108
|
+
...(referencedServer.headers ?? {}),
|
|
109
|
+
...(readStringRecord(item.headers) ?? {}),
|
|
110
|
+
},
|
|
111
|
+
sourcePath,
|
|
112
|
+
};
|
|
113
|
+
}
|
|
56
114
|
async function hydrateExternalToolSource(tools, source, workspaceRoot) {
|
|
57
115
|
const externalRoot = await ensureExternalResourceSource(source, workspaceRoot);
|
|
58
116
|
const discoveredToolRefs = [];
|
|
@@ -102,35 +160,51 @@ export async function hydrateResourceAndExternalTools(tools, toolSourceRefs, wor
|
|
|
102
160
|
name: existing?.name || resourceTool.name,
|
|
103
161
|
description: existing?.description || resourceTool.description,
|
|
104
162
|
config: existing?.config,
|
|
163
|
+
embeddingModelRef: existing?.embeddingModelRef,
|
|
105
164
|
backendOperation: existing?.backendOperation ?? resourceTool.backendOperation,
|
|
106
165
|
bundleRefs: existing?.bundleRefs ?? [],
|
|
107
166
|
hitl: existing?.hitl ?? resourceTool.hitl,
|
|
108
167
|
retryable: existing?.retryable ?? resourceTool.retryable,
|
|
109
|
-
sourcePath:
|
|
168
|
+
sourcePath: resourceTool.toolPath,
|
|
110
169
|
});
|
|
111
170
|
}
|
|
112
171
|
}
|
|
113
172
|
export async function hydrateAgentMcpTools(agents, mcpServers, tools) {
|
|
114
173
|
for (const agent of agents) {
|
|
115
174
|
const discoveredRefs = new Set(agent.toolRefs);
|
|
116
|
-
for (const
|
|
175
|
+
for (const rawItem of agent.mcpServers ?? []) {
|
|
176
|
+
const item = normalizeAgentMcpServerUsage(rawItem);
|
|
177
|
+
const serverRef = typeof item.ref === "string" ? item.ref.trim() : "";
|
|
178
|
+
const referencedServerId = serverRef.startsWith("mcp/") ? serverRef.slice(4) : serverRef;
|
|
179
|
+
const referencedServer = referencedServerId ? mcpServers.get(referencedServerId) : undefined;
|
|
117
180
|
const name = typeof item.name === "string" && item.name.trim()
|
|
118
181
|
? item.name.trim()
|
|
119
|
-
:
|
|
120
|
-
?
|
|
121
|
-
: ""
|
|
182
|
+
: referencedServer?.id
|
|
183
|
+
? referencedServer.id
|
|
184
|
+
: typeof item.id === "string" && item.id.trim()
|
|
185
|
+
? item.id.trim()
|
|
186
|
+
: "";
|
|
122
187
|
if (!name) {
|
|
123
188
|
throw new Error(`Agent ${agent.id} has an MCP server entry without a name`);
|
|
124
189
|
}
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
190
|
+
let serverId = referencedServer?.id;
|
|
191
|
+
let parsedServer = referencedServer;
|
|
192
|
+
if (!parsedServer) {
|
|
193
|
+
serverId = `${agent.id}.${name}`;
|
|
194
|
+
parsedServer = parseMcpServerObject({
|
|
195
|
+
id: serverId,
|
|
196
|
+
kind: "mcp",
|
|
197
|
+
sourcePath: agent.sourcePath,
|
|
198
|
+
value: item,
|
|
199
|
+
});
|
|
200
|
+
mcpServers.set(serverId, parsedServer);
|
|
201
|
+
}
|
|
202
|
+
else if (referencedServer && hasMcpServerUsageOverrides(item)) {
|
|
203
|
+
parsedServer = mergeReferencedMcpServer(referencedServer, item, agent.id, name, agent.sourcePath);
|
|
204
|
+
serverId = parsedServer.id;
|
|
205
|
+
mcpServers.set(serverId, parsedServer);
|
|
206
|
+
}
|
|
132
207
|
const filter = compileMcpToolFilter(item);
|
|
133
|
-
mcpServers.set(serverId, parsedServer);
|
|
134
208
|
const remoteTools = await listRemoteMcpTools(toMcpServerConfig(parsedServer));
|
|
135
209
|
for (const remoteTool of remoteTools) {
|
|
136
210
|
if (!shouldIncludeRemoteMcpTool(filter, remoteTool.name)) {
|
|
@@ -3,9 +3,31 @@ import { readdir } from "node:fs/promises";
|
|
|
3
3
|
import { parseAllDocuments } from "yaml";
|
|
4
4
|
import { fileExists, listFilesRecursive, readYamlOrJson } from "../utils/fs.js";
|
|
5
5
|
const MODEL_FILENAMES = ["models.yaml", "models.yml"];
|
|
6
|
+
const ENV_PLACEHOLDER_PATTERN = /\$\{env:([A-Za-z_][A-Za-z0-9_]*)\}/g;
|
|
6
7
|
function asObject(value) {
|
|
7
8
|
return typeof value === "object" && value ? value : undefined;
|
|
8
9
|
}
|
|
10
|
+
function interpolateEnvString(value, sourcePath) {
|
|
11
|
+
return value.replaceAll(ENV_PLACEHOLDER_PATTERN, (_match, envName) => {
|
|
12
|
+
const resolved = process.env[envName];
|
|
13
|
+
if (typeof resolved !== "string" || resolved.length === 0) {
|
|
14
|
+
throw new Error(`YAML document ${sourcePath} references missing environment variable ${envName}`);
|
|
15
|
+
}
|
|
16
|
+
return resolved;
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
function interpolateEnvPlaceholders(value, sourcePath) {
|
|
20
|
+
if (typeof value === "string") {
|
|
21
|
+
return interpolateEnvString(value, sourcePath);
|
|
22
|
+
}
|
|
23
|
+
if (Array.isArray(value)) {
|
|
24
|
+
return value.map((entry) => interpolateEnvPlaceholders(entry, sourcePath));
|
|
25
|
+
}
|
|
26
|
+
if (typeof value === "object" && value !== null) {
|
|
27
|
+
return Object.fromEntries(Object.entries(value).map(([key, entry]) => [key, interpolateEnvPlaceholders(entry, sourcePath)]));
|
|
28
|
+
}
|
|
29
|
+
return value;
|
|
30
|
+
}
|
|
9
31
|
function normalizeCatalogSpec(document, options = {}) {
|
|
10
32
|
const typed = asObject(document);
|
|
11
33
|
const spec = typed?.spec;
|
|
@@ -82,7 +104,7 @@ function normalizeKind(kind) {
|
|
|
82
104
|
}
|
|
83
105
|
export function normalizeYamlItem(item) {
|
|
84
106
|
if (item.kind === "DeepAgent" || item.kind === "LangChainAgent") {
|
|
85
|
-
throw new Error(`YAML object kind ${String(item.kind)} is no longer supported; use kind: Agent with spec.
|
|
107
|
+
throw new Error(`YAML object kind ${String(item.kind)} is no longer supported; use kind: Agent with spec.backend instead`);
|
|
86
108
|
}
|
|
87
109
|
const metadata = asObject(item.metadata);
|
|
88
110
|
const spec = asObject(item.spec);
|
|
@@ -116,15 +138,17 @@ async function objectItemsFromDocument(document, sourcePath) {
|
|
|
116
138
|
: undefined;
|
|
117
139
|
const catalogItems = catalogKind === "Models"
|
|
118
140
|
? normalizeCatalogSpec(document, { defaultKind: "Model" })
|
|
119
|
-
: catalogKind === "
|
|
120
|
-
? normalizeCatalogSpec(document)
|
|
121
|
-
: catalogKind === "
|
|
122
|
-
? normalizeCatalogSpec(document
|
|
123
|
-
: catalogKind === "
|
|
124
|
-
? normalizeCatalogSpec(document, { defaultKind: "
|
|
125
|
-
: catalogKind === "
|
|
126
|
-
? normalizeCatalogSpec(document, { defaultKind: "
|
|
127
|
-
:
|
|
141
|
+
: catalogKind === "EmbeddingModels"
|
|
142
|
+
? normalizeCatalogSpec(document, { defaultKind: "EmbeddingModel" })
|
|
143
|
+
: catalogKind === "Stores"
|
|
144
|
+
? normalizeCatalogSpec(document)
|
|
145
|
+
: catalogKind === "Backends"
|
|
146
|
+
? normalizeCatalogSpec(document, { defaultKind: "Backend" })
|
|
147
|
+
: catalogKind === "Tools"
|
|
148
|
+
? normalizeCatalogSpec(document, { defaultKind: "Tool" })
|
|
149
|
+
: catalogKind === "McpServers"
|
|
150
|
+
? normalizeCatalogSpec(document, { defaultKind: "McpServer" })
|
|
151
|
+
: [];
|
|
128
152
|
if (catalogItems.length > 0) {
|
|
129
153
|
return catalogItems;
|
|
130
154
|
}
|
|
@@ -148,7 +172,8 @@ export async function readYamlItems(root, relativeDir, options = {}) {
|
|
|
148
172
|
for (const filePath of files) {
|
|
149
173
|
const parsedDocuments = parseAllDocuments(await readYamlOrJson(filePath));
|
|
150
174
|
for (const parsedDocument of parsedDocuments) {
|
|
151
|
-
|
|
175
|
+
const resolvedDocument = interpolateEnvPlaceholders(parsedDocument.toJSON(), filePath);
|
|
176
|
+
for (const item of await objectItemsFromDocument(resolvedDocument, filePath)) {
|
|
152
177
|
records.push({ item: normalizeYamlItem(item), sourcePath: filePath });
|
|
153
178
|
}
|
|
154
179
|
}
|
|
@@ -164,7 +189,8 @@ export async function readNamedYamlItems(root, filenames) {
|
|
|
164
189
|
}
|
|
165
190
|
const parsedDocuments = parseAllDocuments(await readYamlOrJson(filePath));
|
|
166
191
|
for (const parsedDocument of parsedDocuments) {
|
|
167
|
-
|
|
192
|
+
const resolvedDocument = interpolateEnvPlaceholders(parsedDocument.toJSON(), filePath);
|
|
193
|
+
for (const item of await objectItemsFromDocument(resolvedDocument, filePath)) {
|
|
168
194
|
records.push({ item: normalizeYamlItem(item), sourcePath: filePath });
|
|
169
195
|
}
|
|
170
196
|
}
|
|
@@ -190,7 +216,8 @@ export async function readNamedModelItems(root) {
|
|
|
190
216
|
for (const filePath of [...filePaths].sort()) {
|
|
191
217
|
const parsedDocuments = parseAllDocuments(await readYamlOrJson(filePath));
|
|
192
218
|
for (const parsedDocument of parsedDocuments) {
|
|
193
|
-
|
|
219
|
+
const resolvedDocument = interpolateEnvPlaceholders(parsedDocument.toJSON(), filePath);
|
|
220
|
+
for (const item of await objectItemsFromDocument(resolvedDocument, filePath)) {
|
|
194
221
|
const normalized = normalizeYamlItem(item);
|
|
195
222
|
if (normalized.kind === "model" && typeof normalized.id === "string" && normalized.id.trim()) {
|
|
196
223
|
records.push({ item: normalized, sourcePath: filePath });
|