@dexto/agent-management 1.6.14 → 1.6.16
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/config/config-enrichment.cjs +24 -7
- package/dist/config/config-enrichment.d.ts +5 -0
- package/dist/config/config-enrichment.d.ts.map +1 -1
- package/dist/config/config-enrichment.js +24 -7
- package/dist/config/discover-prompts.cjs +7 -7
- package/dist/config/discover-prompts.d.ts +5 -3
- package/dist/config/discover-prompts.d.ts.map +1 -1
- package/dist/config/discover-prompts.js +7 -7
- package/dist/config/error-codes.cjs +1 -0
- package/dist/config/error-codes.d.ts +1 -0
- package/dist/config/error-codes.d.ts.map +1 -1
- package/dist/config/error-codes.js +1 -0
- package/dist/config/errors.cjs +12 -2
- package/dist/config/errors.d.ts +5 -0
- package/dist/config/errors.d.ts.map +1 -1
- package/dist/config/errors.js +12 -2
- package/dist/index.cjs +37 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +38 -0
- package/dist/plugins/discover-skills.cjs +2 -0
- package/dist/plugins/discover-skills.d.ts +7 -5
- package/dist/plugins/discover-skills.d.ts.map +1 -1
- package/dist/plugins/discover-skills.js +2 -0
- package/dist/project-registry.cjs +380 -0
- package/dist/project-registry.d.ts +153 -0
- package/dist/project-registry.d.ts.map +1 -0
- package/dist/project-registry.js +329 -0
- package/dist/resolver.cjs +23 -2
- package/dist/resolver.d.ts.map +1 -1
- package/dist/resolver.js +26 -2
- package/dist/tool-factories/agent-spawner/factory.cjs +12 -0
- package/dist/tool-factories/agent-spawner/factory.d.ts.map +1 -1
- package/dist/tool-factories/agent-spawner/factory.js +12 -0
- package/dist/tool-factories/agent-spawner/runtime.cjs +189 -27
- package/dist/tool-factories/agent-spawner/runtime.d.ts +7 -1
- package/dist/tool-factories/agent-spawner/runtime.d.ts.map +1 -1
- package/dist/tool-factories/agent-spawner/runtime.js +189 -27
- package/dist/tool-factories/agent-spawner/schemas.cjs +6 -3
- package/dist/tool-factories/agent-spawner/schemas.d.ts +3 -2
- package/dist/tool-factories/agent-spawner/schemas.d.ts.map +1 -1
- package/dist/tool-factories/agent-spawner/schemas.js +6 -3
- package/dist/tool-factories/agent-spawner/spawn-agent-tool.cjs +4 -3
- package/dist/tool-factories/agent-spawner/spawn-agent-tool.d.ts.map +1 -1
- package/dist/tool-factories/agent-spawner/spawn-agent-tool.js +4 -3
- package/dist/utils/execution-context.cjs +61 -16
- package/dist/utils/execution-context.d.ts.map +1 -1
- package/dist/utils/execution-context.js +62 -17
- package/package.json +5 -5
|
@@ -41,6 +41,8 @@ var import_types = require("../../registry/types.js");
|
|
|
41
41
|
var import_path = require("../../utils/path.js");
|
|
42
42
|
var path = __toESM(require("path"), 1);
|
|
43
43
|
var import_llm_resolution = require("./llm-resolution.js");
|
|
44
|
+
var import_project_registry = require("../../project-registry.js");
|
|
45
|
+
var import_execution_context = require("../../utils/execution-context.js");
|
|
44
46
|
const REASONING_VARIANT_FALLBACK_ORDER = [
|
|
45
47
|
"disabled",
|
|
46
48
|
"none",
|
|
@@ -58,6 +60,7 @@ class AgentSpawnerRuntime {
|
|
|
58
60
|
parentAgent;
|
|
59
61
|
config;
|
|
60
62
|
logger;
|
|
63
|
+
workspaceRootHint;
|
|
61
64
|
selectLowestReasoningVariant(provider, model, preferredVariant) {
|
|
62
65
|
const profile = (0, import_core.getReasoningProfile)(provider, model);
|
|
63
66
|
if (!profile.capable || profile.supportedVariants.length === 0) {
|
|
@@ -124,6 +127,148 @@ class AgentSpawnerRuntime {
|
|
|
124
127
|
type: "custom"
|
|
125
128
|
};
|
|
126
129
|
}
|
|
130
|
+
isWorkspaceEntryAvailableToCurrentParent(entry) {
|
|
131
|
+
if (!entry.parentAgentId) {
|
|
132
|
+
return true;
|
|
133
|
+
}
|
|
134
|
+
return entry.parentAgentId === this.parentAgent.config.agentId;
|
|
135
|
+
}
|
|
136
|
+
setWorkspaceRootHint(workspaceRootHint) {
|
|
137
|
+
const trimmed = workspaceRootHint?.trim();
|
|
138
|
+
this.workspaceRootHint = trimmed ? trimmed : void 0;
|
|
139
|
+
}
|
|
140
|
+
async getProjectRootCandidates() {
|
|
141
|
+
const candidates = /* @__PURE__ */ new Set();
|
|
142
|
+
const seedPaths = [];
|
|
143
|
+
if (this.workspaceRootHint) {
|
|
144
|
+
seedPaths.push(this.workspaceRootHint);
|
|
145
|
+
}
|
|
146
|
+
try {
|
|
147
|
+
const workspace = await this.parentAgent.getWorkspace();
|
|
148
|
+
if (workspace?.path) {
|
|
149
|
+
seedPaths.push(workspace.path);
|
|
150
|
+
}
|
|
151
|
+
} catch (error) {
|
|
152
|
+
this.logger.warn(
|
|
153
|
+
`Failed to read parent workspace while resolving workspace agents: ${error instanceof Error ? error.message : String(error)}`
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
seedPaths.push(process.cwd());
|
|
157
|
+
for (const seedPath of seedPaths) {
|
|
158
|
+
candidates.add(seedPath);
|
|
159
|
+
const detectedProjectRoot = (0, import_execution_context.findDextoProjectRoot)(seedPath);
|
|
160
|
+
if (detectedProjectRoot) {
|
|
161
|
+
candidates.add(detectedProjectRoot);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
return Array.from(candidates);
|
|
165
|
+
}
|
|
166
|
+
async getWorkspaceAvailableAgentsResult() {
|
|
167
|
+
for (const projectRoot of await this.getProjectRootCandidates()) {
|
|
168
|
+
try {
|
|
169
|
+
const loaded = await (0, import_project_registry.loadProjectRegistry)(projectRoot);
|
|
170
|
+
if (!loaded) {
|
|
171
|
+
continue;
|
|
172
|
+
}
|
|
173
|
+
return {
|
|
174
|
+
registryFound: true,
|
|
175
|
+
allowGlobalAgents: loaded.registry.allowGlobalAgents,
|
|
176
|
+
agents: Object.fromEntries(
|
|
177
|
+
loaded.registry.agents.filter(
|
|
178
|
+
(entry) => entry.id !== this.parentAgent.config.agentId && this.isWorkspaceEntryAvailableToCurrentParent(entry)
|
|
179
|
+
).map((entry) => [
|
|
180
|
+
entry.id,
|
|
181
|
+
{
|
|
182
|
+
id: entry.id,
|
|
183
|
+
name: entry.name,
|
|
184
|
+
description: entry.description,
|
|
185
|
+
author: entry.author ?? "workspace",
|
|
186
|
+
tags: entry.tags ?? [],
|
|
187
|
+
source: entry.configPath,
|
|
188
|
+
type: "custom"
|
|
189
|
+
}
|
|
190
|
+
])
|
|
191
|
+
)
|
|
192
|
+
};
|
|
193
|
+
} catch (error) {
|
|
194
|
+
this.logger.warn(
|
|
195
|
+
`Failed to load workspace registry agents from ${projectRoot}: ${error instanceof Error ? error.message : String(error)}`
|
|
196
|
+
);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
return { agents: {}, registryFound: false, allowGlobalAgents: false };
|
|
200
|
+
}
|
|
201
|
+
async resolveWorkspaceRegistryAgentConfig(agentId) {
|
|
202
|
+
let registryFound = false;
|
|
203
|
+
let allowGlobalAgents = false;
|
|
204
|
+
for (const projectRoot of await this.getProjectRootCandidates()) {
|
|
205
|
+
try {
|
|
206
|
+
const loaded = await (0, import_project_registry.loadProjectRegistry)(projectRoot);
|
|
207
|
+
if (!loaded) {
|
|
208
|
+
continue;
|
|
209
|
+
}
|
|
210
|
+
registryFound = true;
|
|
211
|
+
allowGlobalAgents = loaded.registry.allowGlobalAgents;
|
|
212
|
+
const entry = loaded.registry.agents.find((candidate) => candidate.id === agentId);
|
|
213
|
+
if (!entry) {
|
|
214
|
+
continue;
|
|
215
|
+
}
|
|
216
|
+
if (entry.id === this.parentAgent.config.agentId) {
|
|
217
|
+
return {
|
|
218
|
+
configPath: null,
|
|
219
|
+
blocked: true,
|
|
220
|
+
blockedReason: `Workspace agent '${agentId}' cannot spawn itself.`,
|
|
221
|
+
registryFound: true,
|
|
222
|
+
allowGlobalAgents
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
if (!this.isWorkspaceEntryAvailableToCurrentParent(entry)) {
|
|
226
|
+
return {
|
|
227
|
+
configPath: null,
|
|
228
|
+
blocked: true,
|
|
229
|
+
blockedReason: `Workspace agent '${agentId}' is linked to a different parent and is not available to '${this.parentAgent.config.agentId ?? this.parentId}'.`,
|
|
230
|
+
registryFound: true,
|
|
231
|
+
allowGlobalAgents
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
try {
|
|
235
|
+
const configPath = await (0, import_project_registry.resolveProjectRegistryAgentPath)(projectRoot, agentId);
|
|
236
|
+
if (configPath) {
|
|
237
|
+
return {
|
|
238
|
+
configPath,
|
|
239
|
+
blocked: false,
|
|
240
|
+
registryFound: true,
|
|
241
|
+
allowGlobalAgents
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
return {
|
|
245
|
+
configPath: null,
|
|
246
|
+
blocked: true,
|
|
247
|
+
blockedReason: `Workspace agent '${agentId}' is declared in ${loaded.registryPath} but its config path could not be resolved.`,
|
|
248
|
+
registryFound: true,
|
|
249
|
+
allowGlobalAgents
|
|
250
|
+
};
|
|
251
|
+
} catch (error) {
|
|
252
|
+
const blockedReason = error instanceof Error ? error.message : `Workspace agent '${agentId}' could not be resolved from ${loaded.registryPath}.`;
|
|
253
|
+
this.logger.warn(
|
|
254
|
+
`Failed to resolve workspace registry agent '${agentId}' from ${projectRoot}: ${blockedReason}`
|
|
255
|
+
);
|
|
256
|
+
return {
|
|
257
|
+
configPath: null,
|
|
258
|
+
blocked: true,
|
|
259
|
+
blockedReason,
|
|
260
|
+
registryFound: true,
|
|
261
|
+
allowGlobalAgents
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
} catch (error) {
|
|
265
|
+
this.logger.warn(
|
|
266
|
+
`Failed to resolve workspace registry agent '${agentId}' from ${projectRoot}: ${error instanceof Error ? error.message : String(error)}`
|
|
267
|
+
);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
return { configPath: null, blocked: false, registryFound, allowGlobalAgents };
|
|
271
|
+
}
|
|
127
272
|
constructor(parentAgent, config, logger) {
|
|
128
273
|
this.parentAgent = parentAgent;
|
|
129
274
|
this.config = config;
|
|
@@ -608,21 +753,31 @@ class AgentSpawnerRuntime {
|
|
|
608
753
|
})();
|
|
609
754
|
if (agentId) {
|
|
610
755
|
let configPath = null;
|
|
756
|
+
const workspaceRegistryResolution = await this.resolveWorkspaceRegistryAgentConfig(agentId);
|
|
757
|
+
configPath = workspaceRegistryResolution.configPath;
|
|
758
|
+
const canUseGlobalAgents = !workspaceRegistryResolution.registryFound || workspaceRegistryResolution.allowGlobalAgents;
|
|
759
|
+
if (workspaceRegistryResolution.blocked) {
|
|
760
|
+
throw new Error(
|
|
761
|
+
workspaceRegistryResolution.blockedReason ?? `Workspace agent '${agentId}' is linked to a different parent and is not available to '${this.parentAgent.config.agentId ?? this.parentId}'.`
|
|
762
|
+
);
|
|
763
|
+
}
|
|
611
764
|
try {
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
765
|
+
if (!configPath && !workspaceRegistryResolution.blocked && canUseGlobalAgents) {
|
|
766
|
+
const registry = (0, import_registry.getAgentRegistry)();
|
|
767
|
+
if (!registry.hasAgent(agentId)) {
|
|
768
|
+
this.logger.warn(
|
|
769
|
+
`Agent '${agentId}' not found in installed registry. Trying bundled config paths.`
|
|
770
|
+
);
|
|
771
|
+
} else {
|
|
772
|
+
configPath = await registry.resolveAgent(agentId);
|
|
773
|
+
}
|
|
619
774
|
}
|
|
620
775
|
} catch (error) {
|
|
621
776
|
this.logger.warn(
|
|
622
777
|
`Failed to load agent registry for '${agentId}'. Trying bundled config paths. (${error instanceof Error ? error.message : String(error)})`
|
|
623
778
|
);
|
|
624
779
|
}
|
|
625
|
-
if (!configPath) {
|
|
780
|
+
if (!configPath && !workspaceRegistryResolution.blocked && canUseGlobalAgents) {
|
|
626
781
|
configPath = this.resolveBundledAgentConfig(agentId);
|
|
627
782
|
}
|
|
628
783
|
if (configPath) {
|
|
@@ -677,29 +832,36 @@ class AgentSpawnerRuntime {
|
|
|
677
832
|
* Get information about available agents for tool description.
|
|
678
833
|
* Returns agent metadata from registry, filtered by allowedAgents if configured.
|
|
679
834
|
*/
|
|
680
|
-
getAvailableAgents() {
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
835
|
+
async getAvailableAgents() {
|
|
836
|
+
const workspaceAgents = await this.getWorkspaceAvailableAgentsResult();
|
|
837
|
+
const canUseGlobalAgents = !workspaceAgents.registryFound || workspaceAgents.allowGlobalAgents;
|
|
838
|
+
let scopedAgents = { ...workspaceAgents.agents };
|
|
839
|
+
if (canUseGlobalAgents) {
|
|
840
|
+
try {
|
|
841
|
+
const registry = (0, import_registry.getAgentRegistry)();
|
|
842
|
+
scopedAgents = {
|
|
843
|
+
...registry.getAvailableAgents(),
|
|
844
|
+
...workspaceAgents.agents
|
|
845
|
+
};
|
|
846
|
+
} catch (error) {
|
|
847
|
+
this.logger.warn(
|
|
848
|
+
`Failed to load agent registry for spawn_agent description: ${error instanceof Error ? error.message : String(error)}`
|
|
849
|
+
);
|
|
691
850
|
}
|
|
692
|
-
return [];
|
|
693
851
|
}
|
|
694
|
-
if (this.config.allowedAgents
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
852
|
+
if (!this.config.allowedAgents || this.config.allowedAgents.length === 0) {
|
|
853
|
+
return Object.values(scopedAgents);
|
|
854
|
+
}
|
|
855
|
+
const result = [];
|
|
856
|
+
for (const id of this.config.allowedAgents) {
|
|
857
|
+
const agent = scopedAgents[id];
|
|
858
|
+
if (agent) {
|
|
859
|
+
result.push(agent);
|
|
860
|
+
} else if (canUseGlobalAgents) {
|
|
861
|
+
result.push(this.createFallbackRegistryEntry(id));
|
|
699
862
|
}
|
|
700
|
-
return result;
|
|
701
863
|
}
|
|
702
|
-
return
|
|
864
|
+
return result;
|
|
703
865
|
}
|
|
704
866
|
/**
|
|
705
867
|
* Clean up all sub-agents (called when parent stops)
|
|
@@ -20,10 +20,16 @@ export declare class AgentSpawnerRuntime implements TaskForker {
|
|
|
20
20
|
private parentAgent;
|
|
21
21
|
private config;
|
|
22
22
|
private logger;
|
|
23
|
+
private workspaceRootHint;
|
|
23
24
|
private selectLowestReasoningVariant;
|
|
24
25
|
private applySubAgentLlmPolicy;
|
|
25
26
|
private resolveBundledAgentConfig;
|
|
26
27
|
private createFallbackRegistryEntry;
|
|
28
|
+
private isWorkspaceEntryAvailableToCurrentParent;
|
|
29
|
+
setWorkspaceRootHint(workspaceRootHint?: string): void;
|
|
30
|
+
private getProjectRootCandidates;
|
|
31
|
+
private getWorkspaceAvailableAgentsResult;
|
|
32
|
+
private resolveWorkspaceRegistryAgentConfig;
|
|
27
33
|
constructor(parentAgent: DextoAgent, config: AgentSpawnerConfig, logger: Logger);
|
|
28
34
|
/**
|
|
29
35
|
* Get count of sub-agents belonging to this parent
|
|
@@ -113,7 +119,7 @@ export declare class AgentSpawnerRuntime implements TaskForker {
|
|
|
113
119
|
* Get information about available agents for tool description.
|
|
114
120
|
* Returns agent metadata from registry, filtered by allowedAgents if configured.
|
|
115
121
|
*/
|
|
116
|
-
getAvailableAgents(): AgentRegistryEntry[]
|
|
122
|
+
getAvailableAgents(): Promise<AgentRegistryEntry[]>;
|
|
117
123
|
/**
|
|
118
124
|
* Clean up all sub-agents (called when parent stops)
|
|
119
125
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../../../src/tool-factories/agent-spawner/runtime.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAIH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAWlE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAIlE,OAAO,EAAE,KAAK,kBAAkB,EAAE,MAAM,cAAc,CAAC;AACvD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../../../src/tool-factories/agent-spawner/runtime.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAIH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAWlE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAIlE,OAAO,EAAE,KAAK,kBAAkB,EAAE,MAAM,cAAc,CAAC;AACvD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAiBnD,qBAAa,mBAAoB,YAAW,UAAU;IAClD,OAAO,CAAC,OAAO,CAAe;IAC9B,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,WAAW,CAAa;IAChC,OAAO,CAAC,MAAM,CAAqB;IACnC,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,iBAAiB,CAAqB;IAE9C,OAAO,CAAC,4BAA4B;IAuBpC,OAAO,CAAC,sBAAsB;IA8B9B,OAAO,CAAC,yBAAyB;IAyBjC,OAAO,CAAC,2BAA2B;IAYnC,OAAO,CAAC,wCAAwC;IAUhD,oBAAoB,CAAC,iBAAiB,CAAC,EAAE,MAAM,GAAG,IAAI;YAKxC,wBAAwB;YAgCxB,iCAAiC;YA8CjC,mCAAmC;gBAyFrC,WAAW,EAAE,UAAU,EAAE,MAAM,EAAE,kBAAkB,EAAE,MAAM,EAAE,MAAM;IAuB/E;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAIxB;;OAEG;IACH,OAAO,CAAC,QAAQ;IAIhB;;;;;;;;;;;;;;OAcG;IACG,eAAe,CAAC,KAAK,EAAE;QACzB,IAAI,EAAE,MAAM,CAAC;QACb,YAAY,EAAE,MAAM,CAAC;QACrB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,WAAW,CAAC,EAAE,OAAO,CAAC;QACtB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,SAAS,CAAC,EAAE,MAAM,CAAC;KACtB,GAAG,OAAO,CAAC,gBAAgB,CAAC;IA8C7B;;;;;;;;;;OAUG;IACG,IAAI,CAAC,OAAO,EAAE;QAChB,IAAI,EAAE,MAAM,CAAC;QACb,YAAY,EAAE,MAAM,CAAC;QACrB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,WAAW,CAAC,EAAE,OAAO,CAAC;QACtB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,SAAS,CAAC,EAAE,MAAM,CAAC;KACtB,GAAG,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAIpE;;;;;OAKG;IACH,OAAO,CAAC,qBAAqB;IA+G7B;;OAEG;YACW,oBAAoB;IA2BlC,OAAO,CAAC,sBAAsB;IAuB9B;;OAEG;IACH,OAAO,CAAC,UAAU;IAsBlB;;OAEG;YACW,oBAAoB;IA4RlC;;;;;;;OAOG;YACW,mBAAmB;IAkLjC;;;OAGG;IACG,kBAAkB,IAAI,OAAO,CAAC,kBAAkB,EAAE,CAAC;IAqCzD;;OAEG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;CAIjC"}
|
|
@@ -13,6 +13,8 @@ import { deriveDisplayName } from "../../registry/types.js";
|
|
|
13
13
|
import { getDextoPath, resolveBundledScript } from "../../utils/path.js";
|
|
14
14
|
import * as path from "path";
|
|
15
15
|
import { resolveSubAgentLLM } from "./llm-resolution.js";
|
|
16
|
+
import { loadProjectRegistry, resolveProjectRegistryAgentPath } from "../../project-registry.js";
|
|
17
|
+
import { findDextoProjectRoot } from "../../utils/execution-context.js";
|
|
16
18
|
const REASONING_VARIANT_FALLBACK_ORDER = [
|
|
17
19
|
"disabled",
|
|
18
20
|
"none",
|
|
@@ -30,6 +32,7 @@ class AgentSpawnerRuntime {
|
|
|
30
32
|
parentAgent;
|
|
31
33
|
config;
|
|
32
34
|
logger;
|
|
35
|
+
workspaceRootHint;
|
|
33
36
|
selectLowestReasoningVariant(provider, model, preferredVariant) {
|
|
34
37
|
const profile = getReasoningProfile(provider, model);
|
|
35
38
|
if (!profile.capable || profile.supportedVariants.length === 0) {
|
|
@@ -96,6 +99,148 @@ class AgentSpawnerRuntime {
|
|
|
96
99
|
type: "custom"
|
|
97
100
|
};
|
|
98
101
|
}
|
|
102
|
+
isWorkspaceEntryAvailableToCurrentParent(entry) {
|
|
103
|
+
if (!entry.parentAgentId) {
|
|
104
|
+
return true;
|
|
105
|
+
}
|
|
106
|
+
return entry.parentAgentId === this.parentAgent.config.agentId;
|
|
107
|
+
}
|
|
108
|
+
setWorkspaceRootHint(workspaceRootHint) {
|
|
109
|
+
const trimmed = workspaceRootHint?.trim();
|
|
110
|
+
this.workspaceRootHint = trimmed ? trimmed : void 0;
|
|
111
|
+
}
|
|
112
|
+
async getProjectRootCandidates() {
|
|
113
|
+
const candidates = /* @__PURE__ */ new Set();
|
|
114
|
+
const seedPaths = [];
|
|
115
|
+
if (this.workspaceRootHint) {
|
|
116
|
+
seedPaths.push(this.workspaceRootHint);
|
|
117
|
+
}
|
|
118
|
+
try {
|
|
119
|
+
const workspace = await this.parentAgent.getWorkspace();
|
|
120
|
+
if (workspace?.path) {
|
|
121
|
+
seedPaths.push(workspace.path);
|
|
122
|
+
}
|
|
123
|
+
} catch (error) {
|
|
124
|
+
this.logger.warn(
|
|
125
|
+
`Failed to read parent workspace while resolving workspace agents: ${error instanceof Error ? error.message : String(error)}`
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
seedPaths.push(process.cwd());
|
|
129
|
+
for (const seedPath of seedPaths) {
|
|
130
|
+
candidates.add(seedPath);
|
|
131
|
+
const detectedProjectRoot = findDextoProjectRoot(seedPath);
|
|
132
|
+
if (detectedProjectRoot) {
|
|
133
|
+
candidates.add(detectedProjectRoot);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
return Array.from(candidates);
|
|
137
|
+
}
|
|
138
|
+
async getWorkspaceAvailableAgentsResult() {
|
|
139
|
+
for (const projectRoot of await this.getProjectRootCandidates()) {
|
|
140
|
+
try {
|
|
141
|
+
const loaded = await loadProjectRegistry(projectRoot);
|
|
142
|
+
if (!loaded) {
|
|
143
|
+
continue;
|
|
144
|
+
}
|
|
145
|
+
return {
|
|
146
|
+
registryFound: true,
|
|
147
|
+
allowGlobalAgents: loaded.registry.allowGlobalAgents,
|
|
148
|
+
agents: Object.fromEntries(
|
|
149
|
+
loaded.registry.agents.filter(
|
|
150
|
+
(entry) => entry.id !== this.parentAgent.config.agentId && this.isWorkspaceEntryAvailableToCurrentParent(entry)
|
|
151
|
+
).map((entry) => [
|
|
152
|
+
entry.id,
|
|
153
|
+
{
|
|
154
|
+
id: entry.id,
|
|
155
|
+
name: entry.name,
|
|
156
|
+
description: entry.description,
|
|
157
|
+
author: entry.author ?? "workspace",
|
|
158
|
+
tags: entry.tags ?? [],
|
|
159
|
+
source: entry.configPath,
|
|
160
|
+
type: "custom"
|
|
161
|
+
}
|
|
162
|
+
])
|
|
163
|
+
)
|
|
164
|
+
};
|
|
165
|
+
} catch (error) {
|
|
166
|
+
this.logger.warn(
|
|
167
|
+
`Failed to load workspace registry agents from ${projectRoot}: ${error instanceof Error ? error.message : String(error)}`
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
return { agents: {}, registryFound: false, allowGlobalAgents: false };
|
|
172
|
+
}
|
|
173
|
+
async resolveWorkspaceRegistryAgentConfig(agentId) {
|
|
174
|
+
let registryFound = false;
|
|
175
|
+
let allowGlobalAgents = false;
|
|
176
|
+
for (const projectRoot of await this.getProjectRootCandidates()) {
|
|
177
|
+
try {
|
|
178
|
+
const loaded = await loadProjectRegistry(projectRoot);
|
|
179
|
+
if (!loaded) {
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
182
|
+
registryFound = true;
|
|
183
|
+
allowGlobalAgents = loaded.registry.allowGlobalAgents;
|
|
184
|
+
const entry = loaded.registry.agents.find((candidate) => candidate.id === agentId);
|
|
185
|
+
if (!entry) {
|
|
186
|
+
continue;
|
|
187
|
+
}
|
|
188
|
+
if (entry.id === this.parentAgent.config.agentId) {
|
|
189
|
+
return {
|
|
190
|
+
configPath: null,
|
|
191
|
+
blocked: true,
|
|
192
|
+
blockedReason: `Workspace agent '${agentId}' cannot spawn itself.`,
|
|
193
|
+
registryFound: true,
|
|
194
|
+
allowGlobalAgents
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
if (!this.isWorkspaceEntryAvailableToCurrentParent(entry)) {
|
|
198
|
+
return {
|
|
199
|
+
configPath: null,
|
|
200
|
+
blocked: true,
|
|
201
|
+
blockedReason: `Workspace agent '${agentId}' is linked to a different parent and is not available to '${this.parentAgent.config.agentId ?? this.parentId}'.`,
|
|
202
|
+
registryFound: true,
|
|
203
|
+
allowGlobalAgents
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
try {
|
|
207
|
+
const configPath = await resolveProjectRegistryAgentPath(projectRoot, agentId);
|
|
208
|
+
if (configPath) {
|
|
209
|
+
return {
|
|
210
|
+
configPath,
|
|
211
|
+
blocked: false,
|
|
212
|
+
registryFound: true,
|
|
213
|
+
allowGlobalAgents
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
return {
|
|
217
|
+
configPath: null,
|
|
218
|
+
blocked: true,
|
|
219
|
+
blockedReason: `Workspace agent '${agentId}' is declared in ${loaded.registryPath} but its config path could not be resolved.`,
|
|
220
|
+
registryFound: true,
|
|
221
|
+
allowGlobalAgents
|
|
222
|
+
};
|
|
223
|
+
} catch (error) {
|
|
224
|
+
const blockedReason = error instanceof Error ? error.message : `Workspace agent '${agentId}' could not be resolved from ${loaded.registryPath}.`;
|
|
225
|
+
this.logger.warn(
|
|
226
|
+
`Failed to resolve workspace registry agent '${agentId}' from ${projectRoot}: ${blockedReason}`
|
|
227
|
+
);
|
|
228
|
+
return {
|
|
229
|
+
configPath: null,
|
|
230
|
+
blocked: true,
|
|
231
|
+
blockedReason,
|
|
232
|
+
registryFound: true,
|
|
233
|
+
allowGlobalAgents
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
} catch (error) {
|
|
237
|
+
this.logger.warn(
|
|
238
|
+
`Failed to resolve workspace registry agent '${agentId}' from ${projectRoot}: ${error instanceof Error ? error.message : String(error)}`
|
|
239
|
+
);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
return { configPath: null, blocked: false, registryFound, allowGlobalAgents };
|
|
243
|
+
}
|
|
99
244
|
constructor(parentAgent, config, logger) {
|
|
100
245
|
this.parentAgent = parentAgent;
|
|
101
246
|
this.config = config;
|
|
@@ -580,21 +725,31 @@ class AgentSpawnerRuntime {
|
|
|
580
725
|
})();
|
|
581
726
|
if (agentId) {
|
|
582
727
|
let configPath = null;
|
|
728
|
+
const workspaceRegistryResolution = await this.resolveWorkspaceRegistryAgentConfig(agentId);
|
|
729
|
+
configPath = workspaceRegistryResolution.configPath;
|
|
730
|
+
const canUseGlobalAgents = !workspaceRegistryResolution.registryFound || workspaceRegistryResolution.allowGlobalAgents;
|
|
731
|
+
if (workspaceRegistryResolution.blocked) {
|
|
732
|
+
throw new Error(
|
|
733
|
+
workspaceRegistryResolution.blockedReason ?? `Workspace agent '${agentId}' is linked to a different parent and is not available to '${this.parentAgent.config.agentId ?? this.parentId}'.`
|
|
734
|
+
);
|
|
735
|
+
}
|
|
583
736
|
try {
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
737
|
+
if (!configPath && !workspaceRegistryResolution.blocked && canUseGlobalAgents) {
|
|
738
|
+
const registry = getAgentRegistry();
|
|
739
|
+
if (!registry.hasAgent(agentId)) {
|
|
740
|
+
this.logger.warn(
|
|
741
|
+
`Agent '${agentId}' not found in installed registry. Trying bundled config paths.`
|
|
742
|
+
);
|
|
743
|
+
} else {
|
|
744
|
+
configPath = await registry.resolveAgent(agentId);
|
|
745
|
+
}
|
|
591
746
|
}
|
|
592
747
|
} catch (error) {
|
|
593
748
|
this.logger.warn(
|
|
594
749
|
`Failed to load agent registry for '${agentId}'. Trying bundled config paths. (${error instanceof Error ? error.message : String(error)})`
|
|
595
750
|
);
|
|
596
751
|
}
|
|
597
|
-
if (!configPath) {
|
|
752
|
+
if (!configPath && !workspaceRegistryResolution.blocked && canUseGlobalAgents) {
|
|
598
753
|
configPath = this.resolveBundledAgentConfig(agentId);
|
|
599
754
|
}
|
|
600
755
|
if (configPath) {
|
|
@@ -649,29 +804,36 @@ class AgentSpawnerRuntime {
|
|
|
649
804
|
* Get information about available agents for tool description.
|
|
650
805
|
* Returns agent metadata from registry, filtered by allowedAgents if configured.
|
|
651
806
|
*/
|
|
652
|
-
getAvailableAgents() {
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
807
|
+
async getAvailableAgents() {
|
|
808
|
+
const workspaceAgents = await this.getWorkspaceAvailableAgentsResult();
|
|
809
|
+
const canUseGlobalAgents = !workspaceAgents.registryFound || workspaceAgents.allowGlobalAgents;
|
|
810
|
+
let scopedAgents = { ...workspaceAgents.agents };
|
|
811
|
+
if (canUseGlobalAgents) {
|
|
812
|
+
try {
|
|
813
|
+
const registry = getAgentRegistry();
|
|
814
|
+
scopedAgents = {
|
|
815
|
+
...registry.getAvailableAgents(),
|
|
816
|
+
...workspaceAgents.agents
|
|
817
|
+
};
|
|
818
|
+
} catch (error) {
|
|
819
|
+
this.logger.warn(
|
|
820
|
+
`Failed to load agent registry for spawn_agent description: ${error instanceof Error ? error.message : String(error)}`
|
|
821
|
+
);
|
|
663
822
|
}
|
|
664
|
-
return [];
|
|
665
823
|
}
|
|
666
|
-
if (this.config.allowedAgents
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
824
|
+
if (!this.config.allowedAgents || this.config.allowedAgents.length === 0) {
|
|
825
|
+
return Object.values(scopedAgents);
|
|
826
|
+
}
|
|
827
|
+
const result = [];
|
|
828
|
+
for (const id of this.config.allowedAgents) {
|
|
829
|
+
const agent = scopedAgents[id];
|
|
830
|
+
if (agent) {
|
|
831
|
+
result.push(agent);
|
|
832
|
+
} else if (canUseGlobalAgents) {
|
|
833
|
+
result.push(this.createFallbackRegistryEntry(id));
|
|
671
834
|
}
|
|
672
|
-
return result;
|
|
673
835
|
}
|
|
674
|
-
return
|
|
836
|
+
return result;
|
|
675
837
|
}
|
|
676
838
|
/**
|
|
677
839
|
* Clean up all sub-agents (called when parent stops)
|
|
@@ -43,8 +43,9 @@ const AgentSpawnerConfigSchema = import_zod.z.object({
|
|
|
43
43
|
/** Whether spawning is enabled (default: true) */
|
|
44
44
|
allowSpawning: import_zod.z.boolean().default(true).describe("Whether agent spawning is enabled"),
|
|
45
45
|
/**
|
|
46
|
-
* List of agent IDs
|
|
47
|
-
*
|
|
46
|
+
* List of agent IDs that this parent can spawn from the current inventory.
|
|
47
|
+
* The current inventory is workspace agents by default, plus global agents
|
|
48
|
+
* only when the workspace registry opts into them.
|
|
48
49
|
*
|
|
49
50
|
* Example:
|
|
50
51
|
* ```yaml
|
|
@@ -53,7 +54,9 @@ const AgentSpawnerConfigSchema = import_zod.z.object({
|
|
|
53
54
|
* allowedAgents: ["explore-agent", "research-agent"]
|
|
54
55
|
* ```
|
|
55
56
|
*/
|
|
56
|
-
allowedAgents: import_zod.z.array(import_zod.z.string().min(1)).optional().describe(
|
|
57
|
+
allowedAgents: import_zod.z.array(import_zod.z.string().min(1)).optional().describe(
|
|
58
|
+
"Agent IDs from the resolved inventory that can be spawned (omit to allow all in-scope agents)"
|
|
59
|
+
),
|
|
57
60
|
/**
|
|
58
61
|
* Agent IDs that should have their tools auto-approved.
|
|
59
62
|
* Use for agents with only read-only/safe tools (e.g., explore-agent).
|
|
@@ -22,8 +22,9 @@ export declare const AgentSpawnerConfigSchema: z.ZodObject<{
|
|
|
22
22
|
/** Whether spawning is enabled (default: true) */
|
|
23
23
|
allowSpawning: z.ZodDefault<z.ZodBoolean>;
|
|
24
24
|
/**
|
|
25
|
-
* List of agent IDs
|
|
26
|
-
*
|
|
25
|
+
* List of agent IDs that this parent can spawn from the current inventory.
|
|
26
|
+
* The current inventory is workspace agents by default, plus global agents
|
|
27
|
+
* only when the workspace registry opts into them.
|
|
27
28
|
*
|
|
28
29
|
* Example:
|
|
29
30
|
* ```yaml
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schemas.d.ts","sourceRoot":"","sources":["../../../src/tool-factories/agent-spawner/schemas.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAMxB,eAAO,MAAM,gCAAgC,MAAM,CAAC;AACpD,eAAO,MAAM,mCAAmC,EAAE,gBAA6B,CAAC;AAEhF;;GAEG;AACH,eAAO,MAAM,wBAAwB;IAE7B,yCAAyC;;IAGzC,uEAAuE;;IAQvE,qFAAqF;;;;IA4BrF,kDAAkD;;IAGlD
|
|
1
|
+
{"version":3,"file":"schemas.d.ts","sourceRoot":"","sources":["../../../src/tool-factories/agent-spawner/schemas.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAMxB,eAAO,MAAM,gCAAgC,MAAM,CAAC;AACpD,eAAO,MAAM,mCAAmC,EAAE,gBAA6B,CAAC;AAEhF;;GAEG;AACH,eAAO,MAAM,wBAAwB;IAE7B,yCAAyC;;IAGzC,uEAAuE;;IAQvE,qFAAqF;;;;IA4BrF,kDAAkD;;IAGlD;;;;;;;;;;;OAWG;;IAQH;;;;;;;;;;;OAWG;;;;;;;;;;;;;;;;;;;;EAOuD,CAAC;AAEnE,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,wBAAwB,CAAC,CAAC;AAM3E;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB;IAE1B,gDAAgD;;IAGhD,8CAA8C;;IAM9C,qFAAqF;;;;;;;;;;EAGhF,CAAC;AAEd,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,qBAAqB,CAAC,CAAC"}
|
|
@@ -17,8 +17,9 @@ const AgentSpawnerConfigSchema = z.object({
|
|
|
17
17
|
/** Whether spawning is enabled (default: true) */
|
|
18
18
|
allowSpawning: z.boolean().default(true).describe("Whether agent spawning is enabled"),
|
|
19
19
|
/**
|
|
20
|
-
* List of agent IDs
|
|
21
|
-
*
|
|
20
|
+
* List of agent IDs that this parent can spawn from the current inventory.
|
|
21
|
+
* The current inventory is workspace agents by default, plus global agents
|
|
22
|
+
* only when the workspace registry opts into them.
|
|
22
23
|
*
|
|
23
24
|
* Example:
|
|
24
25
|
* ```yaml
|
|
@@ -27,7 +28,9 @@ const AgentSpawnerConfigSchema = z.object({
|
|
|
27
28
|
* allowedAgents: ["explore-agent", "research-agent"]
|
|
28
29
|
* ```
|
|
29
30
|
*/
|
|
30
|
-
allowedAgents: z.array(z.string().min(1)).optional().describe(
|
|
31
|
+
allowedAgents: z.array(z.string().min(1)).optional().describe(
|
|
32
|
+
"Agent IDs from the resolved inventory that can be spawned (omit to allow all in-scope agents)"
|
|
33
|
+
),
|
|
31
34
|
/**
|
|
32
35
|
* Agent IDs that should have their tools auto-approved.
|
|
33
36
|
* Use for agents with only read-only/safe tools (e.g., explore-agent).
|
|
@@ -23,8 +23,8 @@ __export(spawn_agent_tool_exports, {
|
|
|
23
23
|
module.exports = __toCommonJS(spawn_agent_tool_exports);
|
|
24
24
|
var import_core = require("@dexto/core");
|
|
25
25
|
var import_schemas = require("./schemas.js");
|
|
26
|
-
function buildDescription(service) {
|
|
27
|
-
const availableAgents = service.getAvailableAgents();
|
|
26
|
+
async function buildDescription(service) {
|
|
27
|
+
const availableAgents = await service.getAvailableAgents();
|
|
28
28
|
if (availableAgents.length === 0) {
|
|
29
29
|
return `Spawn a sub-agent to handle a specific task. The sub-agent executes the task and returns the result.
|
|
30
30
|
|
|
@@ -59,7 +59,8 @@ function createSpawnAgentTool(service) {
|
|
|
59
59
|
return {
|
|
60
60
|
id: "spawn_agent",
|
|
61
61
|
aliases: ["task"],
|
|
62
|
-
description:
|
|
62
|
+
description: "Spawn a sub-agent to handle a task and return its result.",
|
|
63
|
+
getDescription: () => buildDescription(service),
|
|
63
64
|
presentation: {
|
|
64
65
|
describeHeader: (input) => {
|
|
65
66
|
const agentLabel = input.agentId ? input.agentId.replace(/-agent$/, "") : null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"spawn-agent-tool.d.ts","sourceRoot":"","sources":["../../../src/tool-factories/agent-spawner/spawn-agent-tool.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,IAAI,EAAwB,MAAM,aAAa,CAAC;AAC9D,OAAO,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAC;AACrD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AA4CxD,wBAAgB,oBAAoB,CAChC,OAAO,EAAE,mBAAmB,GAC7B,IAAI,CAAC,OAAO,qBAAqB,CAAC,
|
|
1
|
+
{"version":3,"file":"spawn-agent-tool.d.ts","sourceRoot":"","sources":["../../../src/tool-factories/agent-spawner/spawn-agent-tool.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,IAAI,EAAwB,MAAM,aAAa,CAAC;AAC9D,OAAO,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAC;AACrD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AA4CxD,wBAAgB,oBAAoB,CAChC,OAAO,EAAE,mBAAmB,GAC7B,IAAI,CAAC,OAAO,qBAAqB,CAAC,CAwDpC"}
|