@botbotgo/agent-harness 0.0.135 → 0.0.137
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 +49 -51
- package/README.zh.md +50 -49
- package/dist/config/agents/direct.yaml +69 -73
- package/dist/config/agents/orchestra.yaml +88 -93
- package/dist/config/catalogs/tools.yaml +1 -1
- package/dist/contracts/workspace.d.ts +2 -2
- package/dist/extensions.js +5 -1
- package/dist/init-project.js +22 -29
- package/dist/package-version.d.ts +1 -1
- package/dist/package-version.js +1 -1
- package/dist/resource/resource-impl.js +1 -1
- package/dist/tool-modules.d.ts +5 -0
- package/dist/tool-modules.js +10 -0
- package/dist/workspace/agent-binding-compiler.js +9 -1
- package/dist/workspace/compile.js +140 -11
- package/dist/workspace/object-loader.js +184 -124
- package/dist/workspace/resource-compilers.js +6 -4
- 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/yaml-object-reader.js +2 -2
- package/package.json +1 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# agent-harness feature: schema version for this declarative config object.
|
|
2
2
|
apiVersion: agent-harness/v1alpha1
|
|
3
3
|
# agent-harness feature: object type discriminator.
|
|
4
|
-
# Prefer the generic `Agent` form and
|
|
4
|
+
# Prefer the generic `Agent` form and place execution fields directly under `spec`.
|
|
5
5
|
kind: Agent
|
|
6
6
|
metadata:
|
|
7
7
|
# agent-harness feature: stable object id used for refs and runtime naming.
|
|
@@ -14,104 +14,99 @@ spec:
|
|
|
14
14
|
# This matches the workspace default and keeps the default config shape explicit.
|
|
15
15
|
runRoot: ./.agent
|
|
16
16
|
# agent-harness feature: workspace-level durable long-term memory defaults for this host profile.
|
|
17
|
-
runtimeMemory:
|
|
18
|
-
ref: runtime-memory/default
|
|
17
|
+
runtimeMemory: default
|
|
19
18
|
# =====================
|
|
20
19
|
# Runtime Agent Features
|
|
21
20
|
# =====================
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
memory
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
21
|
+
# Current backend adapter for this host profile.
|
|
22
|
+
backend: deepagent
|
|
23
|
+
# Upstream execution feature: model ref for the underlying LLM used by this execution host.
|
|
24
|
+
modelRef: model/default
|
|
25
|
+
memory:
|
|
26
|
+
# Upstream execution feature: bootstrap memory sources supplied to the selected backend at construction time.
|
|
27
|
+
# These paths resolve relative to the workspace root unless they are already absolute.
|
|
28
|
+
# Treat this as agent-owned startup context, not as a dynamic long-term memory sink:
|
|
29
|
+
# - keep `systemPrompt` for stable role, boundaries, and hard behavioral rules
|
|
30
|
+
# - use `memory:` for stable project knowledge, operating conventions, and shared or agent-specific context files
|
|
31
|
+
# - use `/memories/*` via the backend/store below for durable knowledge learned from prior runs
|
|
32
|
+
# - use the harness checkpointer for resumable graph state for an in-flight run
|
|
33
|
+
# Updating these files changes future agent constructions, but they are still bootstrap inputs rather than
|
|
34
|
+
# self-updating runtime memory.
|
|
35
|
+
- path: config/agent-context.md
|
|
36
|
+
# Upstream execution feature: top-level host starts with no extra direct tool refs beyond discovered workspace tools.
|
|
37
|
+
tools: []
|
|
38
|
+
# Upstream execution feature: top-level host starts with no explicit skill package refs in the default workspace.
|
|
39
|
+
skills: []
|
|
40
|
+
# Upstream execution feature: subagent topology is empty in the repository default and can be filled in YAML.
|
|
41
|
+
subagents: []
|
|
42
|
+
# Upstream execution feature: host-level MCP servers are opt-in and empty by default.
|
|
43
|
+
mcpServers: []
|
|
44
|
+
# Runtime execution feature: checkpointer config passed into the selected backend adapter.
|
|
45
|
+
# This persists resumable graph state for this agent.
|
|
46
|
+
# Available `kind` options in this harness: `FileCheckpointer`, `MemorySaver`, `SqliteSaver`.
|
|
47
|
+
# `path` is only used by `FileCheckpointer` and `SqliteSaver`; omit it for `MemorySaver`.
|
|
48
|
+
checkpointer: sqlite
|
|
49
|
+
# Upstream execution feature: store config passed into the selected backend adapter.
|
|
50
|
+
# In the default deepagent adapter this is the LangGraph store used by `StoreBackend` routes.
|
|
51
|
+
# Built-in kinds in this harness today: `FileStore`, `InMemoryStore`.
|
|
52
|
+
# Other store kinds should flow through a custom runtime resolver instead of being claimed as built in.
|
|
53
|
+
store: default
|
|
54
|
+
# Upstream execution feature: backend config passed into the selected backend adapter.
|
|
55
|
+
# Prefer a reusable backend preset via `ref` so backend topology stays declarative and reusable in YAML.
|
|
56
|
+
# The default preset keeps DeepAgent execution semantics upstream-owned:
|
|
57
|
+
# - workspace execution uses a lightweight VFS sandbox
|
|
58
|
+
# - long-term memory under `/memories/*` uses `StoreBackend`
|
|
59
|
+
# - `CompositeBackend` composes those backend instances together
|
|
60
|
+
# The harness injects the resolved store/checkpointer instances, but the backend topology itself stays upstream-shaped.
|
|
61
|
+
# Upstream execution feature: no extra declarative HITL rules by default.
|
|
62
|
+
interruptOn: {}
|
|
63
|
+
# Upstream execution feature: no extra declarative middleware beyond upstream deepagents defaults by default.
|
|
64
|
+
# Common upstream middleware kinds that this harness can compile directly from YAML:
|
|
65
|
+
# - `patchToolCalls`
|
|
66
|
+
# - `summarization`
|
|
67
|
+
# - `dynamicSystemPrompt`
|
|
68
|
+
# - `humanInTheLoop`
|
|
69
|
+
# - `todoList`
|
|
70
|
+
# - `pii`, `piiRedaction`
|
|
71
|
+
#
|
|
72
|
+
# DeepAgents already includes its own filesystem, planning, subagent, and memory semantics.
|
|
73
|
+
# Keep this list empty unless you are intentionally adding extra upstream middleware on top.
|
|
74
|
+
middleware: []
|
|
75
|
+
config:
|
|
58
76
|
# Upstream execution feature: backend config passed into the selected backend adapter.
|
|
59
|
-
#
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
interruptOn: {}
|
|
67
|
-
# Upstream execution feature: no extra declarative middleware beyond upstream deepagents defaults by default.
|
|
68
|
-
# Common upstream middleware kinds that this harness can compile directly from YAML:
|
|
69
|
-
# - `patchToolCalls`
|
|
70
|
-
# - `summarization`
|
|
71
|
-
# - `dynamicSystemPrompt`
|
|
72
|
-
# - `humanInTheLoop`
|
|
73
|
-
# - `todoList`
|
|
74
|
-
# - `pii`, `piiRedaction`
|
|
75
|
-
#
|
|
76
|
-
# DeepAgents already includes its own filesystem, planning, subagent, and memory semantics.
|
|
77
|
-
# Keep this list empty unless you are intentionally adding extra upstream middleware on top.
|
|
78
|
-
middleware: []
|
|
79
|
-
config:
|
|
80
|
-
# Upstream execution feature: backend config passed into the selected backend adapter.
|
|
81
|
-
# Keep this nested under `config` because `execution.backend` already selects the adapter mode.
|
|
82
|
-
backend:
|
|
83
|
-
ref: backend/default
|
|
84
|
-
# Upstream execution feature: system prompt for the orchestration host.
|
|
85
|
-
# This becomes the top-level instruction block for the selected execution backend and should hold the
|
|
86
|
-
# agent's durable role, priorities, and behavioral guardrails rather than bulky project facts.
|
|
87
|
-
systemPrompt: |-
|
|
88
|
-
You are the orchestra agent.
|
|
77
|
+
# Keep this nested under `config` because `backend` already selects the adapter mode.
|
|
78
|
+
backend: default
|
|
79
|
+
# Upstream execution feature: system prompt for the orchestration host.
|
|
80
|
+
# This becomes the top-level instruction block for the selected execution backend and should hold the
|
|
81
|
+
# agent's durable role, priorities, and behavioral guardrails rather than bulky project facts.
|
|
82
|
+
systemPrompt: |-
|
|
83
|
+
You are the orchestra agent.
|
|
89
84
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
85
|
+
You are the default execution host.
|
|
86
|
+
Try to finish the request yourself before delegating.
|
|
87
|
+
Use your own tools first when they are sufficient.
|
|
88
|
+
Use your own skills first when they are sufficient.
|
|
89
|
+
Delegate only when a subagent is a clearly better fit or when your own tools and skills are not enough.
|
|
90
|
+
If neither you nor any suitable subagent can do the work, say so plainly.
|
|
96
91
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
92
|
+
Do not delegate by reflex.
|
|
93
|
+
Do not delegate just because a task has multiple steps.
|
|
94
|
+
Do not delegate when a direct answer or a short local tool pass is enough.
|
|
95
|
+
Keep the critical path local when immediate progress depends on it; otherwise delegate bounded sidecar work to
|
|
96
|
+
the most appropriate subagent.
|
|
102
97
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
98
|
+
Use your own tools for lightweight discovery, inventory, and context gathering.
|
|
99
|
+
Prefer the structured checkout, indexing, retrieval, and inventory tools that are already attached to you over
|
|
100
|
+
ad hoc shell work when those tools are sufficient.
|
|
101
|
+
Use the attached subagent descriptions as the source of truth for what each subagent is for.
|
|
102
|
+
Do not delegate to a subagent whose description does not clearly match the task.
|
|
103
|
+
Integrate subagent results into one coherent answer and do not claim checks or evidence you did not obtain.
|
|
109
104
|
|
|
110
|
-
|
|
111
|
-
|
|
105
|
+
When the user asks about available tools, skills, or agents, use the attached inventory tools instead of
|
|
106
|
+
inferring from memory.
|
|
112
107
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
108
|
+
Write to `/memories/*` only when the information is durable, reusable across future runs or threads, and likely
|
|
109
|
+
to matter again: user preferences, project conventions, confirmed decisions, reusable summaries, and stable
|
|
110
|
+
ownership facts are good candidates.
|
|
111
|
+
Do not store transient reasoning, temporary plans, scratch work, one-off search results, or intermediate
|
|
112
|
+
outputs that can be cheaply recomputed.
|
|
@@ -249,7 +249,7 @@ export type CompiledAgentBinding = {
|
|
|
249
249
|
};
|
|
250
250
|
export type WorkspaceBundle = {
|
|
251
251
|
workspaceRoot: string;
|
|
252
|
-
|
|
252
|
+
resources: string[];
|
|
253
253
|
refs: Map<string, WorkspaceObject | ParsedAgentObject>;
|
|
254
254
|
models: Map<string, ParsedModelObject>;
|
|
255
255
|
embeddings: Map<string, ParsedEmbeddingModelObject>;
|
|
@@ -267,5 +267,5 @@ export type WorkspaceLoadOptions = {
|
|
|
267
267
|
* Later values always override earlier values.
|
|
268
268
|
*/
|
|
269
269
|
overlayRoots?: string[];
|
|
270
|
-
|
|
270
|
+
resources?: string[];
|
|
271
271
|
};
|
package/dist/extensions.js
CHANGED
|
@@ -15,7 +15,7 @@ function resolveToolRefId(ref) {
|
|
|
15
15
|
if (ref.startsWith("builtin/")) {
|
|
16
16
|
return ref;
|
|
17
17
|
}
|
|
18
|
-
return ref.split("/").slice(1).join("/");
|
|
18
|
+
return ref.includes("/") ? ref.split("/").slice(1).join("/") : ref;
|
|
19
19
|
}
|
|
20
20
|
export function resolveToolTargets(tools, ref) {
|
|
21
21
|
const resolved = resolveToolRefId(ref);
|
|
@@ -28,6 +28,10 @@ export function resolveToolTargets(tools, ref) {
|
|
|
28
28
|
if (exact) {
|
|
29
29
|
return [exact];
|
|
30
30
|
}
|
|
31
|
+
const byName = Array.from(tools.values()).filter((tool) => tool.name === resolved);
|
|
32
|
+
if (byName.length > 0) {
|
|
33
|
+
return byName;
|
|
34
|
+
}
|
|
31
35
|
if (!resolved.startsWith("builtin/")) {
|
|
32
36
|
return [];
|
|
33
37
|
}
|
package/dist/init-project.js
CHANGED
|
@@ -171,15 +171,14 @@ spec:
|
|
|
171
171
|
function renderToolsYaml(options) {
|
|
172
172
|
if (!options.withWebSearch) {
|
|
173
173
|
return `apiVersion: agent-harness/v1alpha1
|
|
174
|
-
kind:
|
|
174
|
+
kind: ToolSets
|
|
175
175
|
spec: []
|
|
176
176
|
`;
|
|
177
177
|
}
|
|
178
178
|
return `apiVersion: agent-harness/v1alpha1
|
|
179
|
-
kind:
|
|
179
|
+
kind: ToolSets
|
|
180
180
|
spec:
|
|
181
|
-
-
|
|
182
|
-
name: web-search
|
|
181
|
+
- name: web-search
|
|
183
182
|
type: provider
|
|
184
183
|
description: ${options.provider} web search tool for current research.
|
|
185
184
|
providerTool:
|
|
@@ -190,8 +189,8 @@ spec:
|
|
|
190
189
|
`;
|
|
191
190
|
}
|
|
192
191
|
function renderResearchAgentYaml(options) {
|
|
193
|
-
const toolsBlock = options.withWebSearch ? "
|
|
194
|
-
const subagentsBlock = options.template === "deep-research" ? "
|
|
192
|
+
const toolsBlock = options.withWebSearch ? " tools:\n - web-search\n" : " tools: []\n";
|
|
193
|
+
const subagentsBlock = options.template === "deep-research" ? " subagents:\n - research-analyst\n" : " subagents: []\n";
|
|
195
194
|
const prompt = options.withWebSearch
|
|
196
195
|
? "Break complex research requests into a clear plan, use web search when current information matters, and return a concise synthesis with sources and explicit uncertainty."
|
|
197
196
|
: "Break complex research requests into a clear plan and return a concise synthesis with explicit assumptions and uncertainty.";
|
|
@@ -205,21 +204,18 @@ metadata:
|
|
|
205
204
|
description: Host-facing research agent for investigating and synthesizing answers.
|
|
206
205
|
spec:
|
|
207
206
|
runtime:
|
|
208
|
-
runtimeMemory:
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
${
|
|
214
|
-
|
|
215
|
-
${
|
|
216
|
-
backend:
|
|
217
|
-
ref: backend/default
|
|
218
|
-
systemPrompt: ${prompt}${delegationLine}
|
|
207
|
+
runtimeMemory: default
|
|
208
|
+
backend: deepagent
|
|
209
|
+
modelRef: model/default
|
|
210
|
+
${toolsBlock} skills:
|
|
211
|
+
- deep-research
|
|
212
|
+
${subagentsBlock} config:
|
|
213
|
+
backend: default
|
|
214
|
+
systemPrompt: ${prompt}${delegationLine}
|
|
219
215
|
`;
|
|
220
216
|
}
|
|
221
217
|
function renderResearchAnalystYaml(options) {
|
|
222
|
-
const toolsBlock = options.withWebSearch ? "
|
|
218
|
+
const toolsBlock = options.withWebSearch ? " tools:\n - web-search\n" : " tools: []\n";
|
|
223
219
|
const prompt = options.withWebSearch
|
|
224
220
|
? "Gather current sources, compare claims carefully, extract the most decision-relevant facts, and return clean notes the host agent can synthesize."
|
|
225
221
|
: "Break down the problem, compare alternatives carefully, extract the most decision-relevant facts, and return clean notes the host agent can synthesize.";
|
|
@@ -230,17 +226,14 @@ metadata:
|
|
|
230
226
|
description: Analyst subagent for source gathering, comparison, and evidence extraction.
|
|
231
227
|
spec:
|
|
232
228
|
runtime:
|
|
233
|
-
runtimeMemory:
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
backend:
|
|
242
|
-
ref: backend/default
|
|
243
|
-
systemPrompt: ${prompt}
|
|
229
|
+
runtimeMemory: default
|
|
230
|
+
backend: deepagent
|
|
231
|
+
modelRef: model/default
|
|
232
|
+
${toolsBlock} skills:
|
|
233
|
+
- deep-research
|
|
234
|
+
config:
|
|
235
|
+
backend: default
|
|
236
|
+
systemPrompt: ${prompt}
|
|
244
237
|
`;
|
|
245
238
|
}
|
|
246
239
|
function renderResourcePackageJson(projectSlug) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const AGENT_HARNESS_VERSION = "0.0.
|
|
1
|
+
export declare const AGENT_HARNESS_VERSION = "0.0.136";
|
package/dist/package-version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const AGENT_HARNESS_VERSION = "0.0.
|
|
1
|
+
export const AGENT_HARNESS_VERSION = "0.0.136";
|
|
@@ -266,7 +266,7 @@ function readStringRecord(value) {
|
|
|
266
266
|
return entries.length > 0 ? Object.fromEntries(entries) : undefined;
|
|
267
267
|
}
|
|
268
268
|
function listWorkspaceResourceProviders(workspace) {
|
|
269
|
-
return (workspace.
|
|
269
|
+
return (workspace.resources ?? [])
|
|
270
270
|
.map((source) => remoteResourceCache.get(source))
|
|
271
271
|
.filter((provider) => Boolean(provider));
|
|
272
272
|
}
|
package/dist/tool-modules.d.ts
CHANGED
|
@@ -7,7 +7,12 @@ export type LoadedToolModule = {
|
|
|
7
7
|
description: string;
|
|
8
8
|
retryable?: boolean;
|
|
9
9
|
};
|
|
10
|
+
export type LoadedSkillModule = {
|
|
11
|
+
name: string;
|
|
12
|
+
description?: string;
|
|
13
|
+
};
|
|
10
14
|
export declare function isSupportedToolModulePath(filePath: string): boolean;
|
|
11
15
|
export declare function discoverToolModuleDefinitions(_sourceText: string, imported: ImportedToolModule): LoadedToolModule[];
|
|
12
16
|
export declare function loadToolModuleDefinition(imported: ImportedToolModule, implementationName: string): LoadedToolModule;
|
|
17
|
+
export declare function discoverSkillDefinitions(skillRoots: string[]): LoadedSkillModule[];
|
|
13
18
|
export {};
|
package/dist/tool-modules.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
|
+
import { readSkillMetadata } from "./runtime/support/skill-metadata.js";
|
|
2
3
|
import { TOOL_DEFINITION_MARKER, normalizeToolSchema } from "./tools.js";
|
|
3
4
|
const TOOL_MODULE_EXTENSIONS = new Set([".mjs", ".js", ".cjs"]);
|
|
4
5
|
function isToolDefinitionObject(value) {
|
|
@@ -40,3 +41,12 @@ export function loadToolModuleDefinition(imported, implementationName) {
|
|
|
40
41
|
}
|
|
41
42
|
throw new Error(`Tool module must export a tool({...}) definition named ${implementationName}.`);
|
|
42
43
|
}
|
|
44
|
+
export function discoverSkillDefinitions(skillRoots) {
|
|
45
|
+
return skillRoots.map((skillRoot) => {
|
|
46
|
+
const metadata = readSkillMetadata(skillRoot);
|
|
47
|
+
return {
|
|
48
|
+
name: metadata.name || path.basename(skillRoot),
|
|
49
|
+
description: metadata.description,
|
|
50
|
+
};
|
|
51
|
+
});
|
|
52
|
+
}
|
|
@@ -131,7 +131,15 @@ function applyToolBindingOverrides(tool, binding) {
|
|
|
131
131
|
...(typeof overrides.description === "string" ? { description: overrides.description } : {}),
|
|
132
132
|
...(typeof overrides.implementationName === "string" ? { implementationName: overrides.implementationName } : {}),
|
|
133
133
|
...(typeof overrides.inputSchemaRef === "string" ? { inputSchemaRef: overrides.inputSchemaRef } : {}),
|
|
134
|
-
...(typeof overrides.embeddingModelRef === "string"
|
|
134
|
+
...(typeof overrides.embeddingModelRef === "string"
|
|
135
|
+
? { embeddingModelRef: overrides.embeddingModelRef }
|
|
136
|
+
: typeof overrides.embeddingModel === "string"
|
|
137
|
+
? {
|
|
138
|
+
embeddingModelRef: overrides.embeddingModel.startsWith("embedding-model/")
|
|
139
|
+
? overrides.embeddingModel
|
|
140
|
+
: `embedding-model/${overrides.embeddingModel}`,
|
|
141
|
+
}
|
|
142
|
+
: {}),
|
|
135
143
|
...(typeof overrides.backendOperation === "string" ? { backendOperation: overrides.backendOperation } : {}),
|
|
136
144
|
...(typeof overrides.mcpRef === "string" ? { mcpRef: overrides.mcpRef } : {}),
|
|
137
145
|
...(typeof overrides.subprocess === "boolean" ? { subprocess: overrides.subprocess } : {}),
|
|
@@ -1,11 +1,16 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { existsSync } from "node:fs";
|
|
3
|
+
import { readdir } from "node:fs/promises";
|
|
1
4
|
import { ensureResourceSources } from "../resource/resource.js";
|
|
2
|
-
import {
|
|
5
|
+
import { ensureExternalResourceSource, isExternalSourceLocator, resolveResourcePackageRoot } from "../resource/sources.js";
|
|
6
|
+
import { loadWorkspaceObjects, readToolModuleItems, readYamlItems } from "./object-loader.js";
|
|
7
|
+
import { validateSkillMetadata } from "../runtime/support/skill-metadata.js";
|
|
3
8
|
import { parseEmbeddingModelObject, parseMcpServerObject, parseModelObject, parseToolObject, parseVectorStoreObject, validateEmbeddingModelObject, validateMcpServerObject, validateModelObject, validateToolObject, validateVectorStoreObject, } from "./resource-compilers.js";
|
|
4
9
|
import { validateAgent, validateTopology } from "./validate.js";
|
|
5
10
|
import { compileBinding } from "./agent-binding-compiler.js";
|
|
6
11
|
import { discoverSubagents, ensureDiscoverySources } from "./support/discovery.js";
|
|
7
12
|
import { collectAgentDiscoverySourceRefs, collectToolSourceRefs } from "./support/source-collectors.js";
|
|
8
|
-
import { getRoutingDefaultAgentId, getRoutingRules, resolveRefId, } from "./support/workspace-ref-utils.js";
|
|
13
|
+
import { getRoutingDefaultAgentId, getRuntimeResources, getRoutingRules, resolveRefId, } from "./support/workspace-ref-utils.js";
|
|
9
14
|
import { hydrateAgentMcpTools, hydrateResourceAndExternalTools } from "./tool-hydration.js";
|
|
10
15
|
function collectParsedResources(refs) {
|
|
11
16
|
const embeddings = new Map();
|
|
@@ -93,6 +98,110 @@ function validateRoutingTargets(refs, agentsList) {
|
|
|
93
98
|
}
|
|
94
99
|
}
|
|
95
100
|
}
|
|
101
|
+
function resolveLocalResourceRoot(candidate, workspaceRoot) {
|
|
102
|
+
const resolved = path.resolve(workspaceRoot, candidate);
|
|
103
|
+
const resourceRoot = resolveResourcePackageRoot(resolved);
|
|
104
|
+
if (!resourceRoot) {
|
|
105
|
+
throw new Error(`Workspace resource ${candidate} is missing resources/package.json.`);
|
|
106
|
+
}
|
|
107
|
+
return resourceRoot;
|
|
108
|
+
}
|
|
109
|
+
async function resolveConfiguredResources(entries, workspaceRoot) {
|
|
110
|
+
const resolved = [];
|
|
111
|
+
for (const entry of entries) {
|
|
112
|
+
const key = entry.trim();
|
|
113
|
+
if (!key) {
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
const root = isExternalSourceLocator(key)
|
|
117
|
+
? await ensureExternalResourceSource(key, workspaceRoot)
|
|
118
|
+
: resolveLocalResourceRoot(key, workspaceRoot);
|
|
119
|
+
resolved.push({ key, root });
|
|
120
|
+
}
|
|
121
|
+
return resolved;
|
|
122
|
+
}
|
|
123
|
+
async function registerAttachedResourceTools(tools, resourceRoot) {
|
|
124
|
+
const toolsRoot = path.join(resourceRoot, "tools");
|
|
125
|
+
for (const { item, sourcePath } of await readYamlItems(toolsRoot, undefined, { recursive: true })) {
|
|
126
|
+
const parsed = parseToolObject({
|
|
127
|
+
id: typeof item.id === "string" ? item.id : path.basename(sourcePath).replace(/\.(yaml|yml|json)$/i, ""),
|
|
128
|
+
kind: "tool",
|
|
129
|
+
sourcePath,
|
|
130
|
+
value: item,
|
|
131
|
+
});
|
|
132
|
+
tools.set(parsed.id, parsed);
|
|
133
|
+
}
|
|
134
|
+
for (const { item, sourcePath } of await readToolModuleItems(toolsRoot)) {
|
|
135
|
+
const parsed = parseToolObject({
|
|
136
|
+
id: String(item.id),
|
|
137
|
+
kind: "tool",
|
|
138
|
+
sourcePath,
|
|
139
|
+
value: item,
|
|
140
|
+
});
|
|
141
|
+
tools.set(parsed.id, parsed);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
async function collectSkillRoots(root) {
|
|
145
|
+
try {
|
|
146
|
+
const entries = await readdir(root, { withFileTypes: true });
|
|
147
|
+
const skillDocument = entries.find((entry) => entry.isFile() && entry.name === "SKILL.md");
|
|
148
|
+
if (skillDocument) {
|
|
149
|
+
return [root];
|
|
150
|
+
}
|
|
151
|
+
const nested = await Promise.all(entries
|
|
152
|
+
.filter((entry) => entry.isDirectory())
|
|
153
|
+
.map((entry) => collectSkillRoots(path.join(root, entry.name))));
|
|
154
|
+
return nested.flat();
|
|
155
|
+
}
|
|
156
|
+
catch {
|
|
157
|
+
return [];
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
async function registerWorkspaceSkillRegistry(skillCollectionRoots) {
|
|
161
|
+
const registry = new Map();
|
|
162
|
+
for (const skillsRoot of skillCollectionRoots) {
|
|
163
|
+
for (const skillRoot of await collectSkillRoots(skillsRoot)) {
|
|
164
|
+
const metadata = validateSkillMetadata(skillRoot);
|
|
165
|
+
const existing = registry.get(metadata.name);
|
|
166
|
+
if (existing && existing !== skillRoot) {
|
|
167
|
+
throw new Error(`Duplicate skill name ${metadata.name} found in ${existing} and ${skillRoot}`);
|
|
168
|
+
}
|
|
169
|
+
registry.set(metadata.name, skillRoot);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
return registry;
|
|
173
|
+
}
|
|
174
|
+
function validateToolNameConflicts(tools) {
|
|
175
|
+
const seen = new Map();
|
|
176
|
+
for (const tool of tools.values()) {
|
|
177
|
+
const existing = seen.get(tool.name);
|
|
178
|
+
if (existing && existing !== tool.sourcePath) {
|
|
179
|
+
throw new Error(`Duplicate tool name ${tool.name} found in ${existing} and ${tool.sourcePath}`);
|
|
180
|
+
}
|
|
181
|
+
seen.set(tool.name, tool.sourcePath);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
function resolveAgentSkillNames(agents, skillRegistry, skillCollectionRoots) {
|
|
185
|
+
for (const agent of agents) {
|
|
186
|
+
agent.skillPathRefs = agent.skillPathRefs.map((entry) => {
|
|
187
|
+
const normalizedEntry = entry.startsWith("skill/") ? entry.slice("skill/".length) : entry;
|
|
188
|
+
const registered = skillRegistry.get(normalizedEntry);
|
|
189
|
+
if (registered) {
|
|
190
|
+
return registered;
|
|
191
|
+
}
|
|
192
|
+
if (path.isAbsolute(entry) || entry.startsWith("builtin://") || entry.startsWith("resource://")) {
|
|
193
|
+
return entry;
|
|
194
|
+
}
|
|
195
|
+
for (const skillsRoot of skillCollectionRoots) {
|
|
196
|
+
const candidate = path.join(skillsRoot, normalizedEntry);
|
|
197
|
+
if (existsSync(path.join(candidate, "SKILL.md"))) {
|
|
198
|
+
return candidate;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
return normalizedEntry;
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
}
|
|
96
205
|
export async function loadWorkspace(workspaceRoot, options = {}) {
|
|
97
206
|
const loaded = await loadWorkspaceObjects(workspaceRoot, options);
|
|
98
207
|
loaded.agents = await discoverSubagents(loaded.agents, workspaceRoot);
|
|
@@ -102,20 +211,40 @@ export async function loadWorkspace(workspaceRoot, options = {}) {
|
|
|
102
211
|
loaded.refs.set(`agent/${agent.id}`, agent);
|
|
103
212
|
}
|
|
104
213
|
const { embeddings, mcpServers, models, vectorStores, tools } = collectParsedResources(loaded.refs);
|
|
105
|
-
for (const agent of loaded.agents) {
|
|
106
|
-
for (const tool of agent.inlineTools ?? []) {
|
|
107
|
-
tools.set(tool.id, tool);
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
214
|
await hydrateAgentMcpTools(loaded.agents, mcpServers, tools);
|
|
111
|
-
const
|
|
112
|
-
|
|
113
|
-
|
|
215
|
+
const configuredResources = Array.from(new Set([
|
|
216
|
+
...getRuntimeResources(loaded.refs),
|
|
217
|
+
...(options.resources ?? []),
|
|
218
|
+
]));
|
|
219
|
+
const resolvedConfiguredResources = await resolveConfiguredResources(configuredResources, workspaceRoot);
|
|
220
|
+
for (const resource of resolvedConfiguredResources) {
|
|
221
|
+
await registerAttachedResourceTools(tools, resource.root);
|
|
222
|
+
}
|
|
223
|
+
const localResourceRoot = resolveResourcePackageRoot(workspaceRoot);
|
|
224
|
+
const skillCollectionRoots = [
|
|
225
|
+
path.join(workspaceRoot, "modules", "skills"),
|
|
226
|
+
...(localResourceRoot ? [path.join(localResourceRoot, "skills")] : []),
|
|
227
|
+
...resolvedConfiguredResources.map((resource) => path.join(resource.root, "skills")),
|
|
228
|
+
];
|
|
229
|
+
const skillRegistry = await registerWorkspaceSkillRegistry(skillCollectionRoots);
|
|
230
|
+
resolveAgentSkillNames(loaded.agents, skillRegistry, skillCollectionRoots);
|
|
231
|
+
const collectedResources = collectToolSourceRefs(tools, loaded.agents, {
|
|
232
|
+
...options,
|
|
233
|
+
resources: configuredResources,
|
|
234
|
+
});
|
|
235
|
+
const externalResources = collectedResources.filter((resource) => isExternalSourceLocator(resource));
|
|
236
|
+
await ensureResourceSources(externalResources, workspaceRoot);
|
|
237
|
+
await hydrateResourceAndExternalTools(tools, externalResources, workspaceRoot);
|
|
238
|
+
validateToolNameConflicts(tools);
|
|
239
|
+
const resources = Array.from(new Set([
|
|
240
|
+
...(localResourceRoot ? [localResourceRoot] : []),
|
|
241
|
+
...collectedResources,
|
|
242
|
+
]));
|
|
114
243
|
validateWorkspaceResources(embeddings, mcpServers, models, vectorStores, tools, loaded.agents);
|
|
115
244
|
validateRoutingTargets(loaded.refs, loaded.agents);
|
|
116
245
|
return {
|
|
117
246
|
workspaceRoot,
|
|
118
|
-
|
|
247
|
+
resources,
|
|
119
248
|
refs: loaded.refs,
|
|
120
249
|
embeddings,
|
|
121
250
|
mcpServers,
|