@dexto/agent-management 1.5.2 → 1.5.3
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 +6 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/runtime/AgentPool.cjs +181 -0
- package/dist/runtime/AgentPool.d.ts +76 -0
- package/dist/runtime/AgentPool.d.ts.map +1 -0
- package/dist/runtime/AgentPool.js +160 -0
- package/dist/runtime/AgentRuntime.cjs +225 -0
- package/dist/runtime/AgentRuntime.d.ts +77 -0
- package/dist/runtime/AgentRuntime.d.ts.map +1 -0
- package/dist/runtime/AgentRuntime.js +201 -0
- package/dist/runtime/approval-delegation.cjs +97 -0
- package/dist/runtime/approval-delegation.d.ts +30 -0
- package/dist/runtime/approval-delegation.d.ts.map +1 -0
- package/dist/runtime/approval-delegation.js +73 -0
- package/dist/runtime/error-codes.cjs +40 -0
- package/dist/runtime/error-codes.d.ts +17 -0
- package/dist/runtime/error-codes.d.ts.map +1 -0
- package/dist/runtime/error-codes.js +16 -0
- package/dist/runtime/errors.cjs +135 -0
- package/dist/runtime/errors.d.ts +40 -0
- package/dist/runtime/errors.d.ts.map +1 -0
- package/dist/runtime/errors.js +111 -0
- package/dist/runtime/index.cjs +53 -0
- package/dist/runtime/index.d.ts +19 -0
- package/dist/runtime/index.d.ts.map +1 -0
- package/dist/runtime/index.js +26 -0
- package/dist/runtime/schemas.cjs +64 -0
- package/dist/runtime/schemas.d.ts +69 -0
- package/dist/runtime/schemas.d.ts.map +1 -0
- package/dist/runtime/schemas.js +35 -0
- package/dist/runtime/types.cjs +16 -0
- package/dist/runtime/types.d.ts +94 -0
- package/dist/runtime/types.d.ts.map +1 -0
- package/dist/runtime/types.js +0 -0
- package/dist/tool-provider/error-codes.cjs +35 -0
- package/dist/tool-provider/error-codes.d.ts +11 -0
- package/dist/tool-provider/error-codes.d.ts.map +1 -0
- package/dist/tool-provider/error-codes.js +11 -0
- package/dist/tool-provider/errors.cjs +81 -0
- package/dist/tool-provider/errors.d.ts +19 -0
- package/dist/tool-provider/errors.d.ts.map +1 -0
- package/dist/tool-provider/errors.js +57 -0
- package/dist/tool-provider/index.cjs +46 -0
- package/dist/tool-provider/index.d.ts +16 -0
- package/dist/tool-provider/index.d.ts.map +1 -0
- package/dist/tool-provider/index.js +16 -0
- package/dist/tool-provider/runtime-service.cjs +370 -0
- package/dist/tool-provider/runtime-service.d.ts +83 -0
- package/dist/tool-provider/runtime-service.d.ts.map +1 -0
- package/dist/tool-provider/runtime-service.js +346 -0
- package/dist/tool-provider/schemas.cjs +73 -0
- package/dist/tool-provider/schemas.d.ts +83 -0
- package/dist/tool-provider/schemas.d.ts.map +1 -0
- package/dist/tool-provider/schemas.js +48 -0
- package/dist/tool-provider/spawn-agent-tool.cjs +89 -0
- package/dist/tool-provider/spawn-agent-tool.d.ts +10 -0
- package/dist/tool-provider/spawn-agent-tool.d.ts.map +1 -0
- package/dist/tool-provider/spawn-agent-tool.js +65 -0
- package/dist/tool-provider/tool-provider.cjs +44 -0
- package/dist/tool-provider/tool-provider.d.ts +24 -0
- package/dist/tool-provider/tool-provider.d.ts.map +1 -0
- package/dist/tool-provider/tool-provider.js +20 -0
- package/dist/tool-provider/types.cjs +16 -0
- package/dist/tool-provider/types.d.ts +17 -0
- package/dist/tool-provider/types.d.ts.map +1 -0
- package/dist/tool-provider/types.js +0 -0
- package/package.json +2 -2
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
var AgentRuntime_exports = {};
|
|
20
|
+
__export(AgentRuntime_exports, {
|
|
21
|
+
AgentRuntime: () => AgentRuntime
|
|
22
|
+
});
|
|
23
|
+
module.exports = __toCommonJS(AgentRuntime_exports);
|
|
24
|
+
var import_crypto = require("crypto");
|
|
25
|
+
var import_core = require("@dexto/core");
|
|
26
|
+
var import_config = require("../config/index.js");
|
|
27
|
+
var import_AgentPool = require("./AgentPool.js");
|
|
28
|
+
var import_errors = require("./errors.js");
|
|
29
|
+
var import_schemas = require("./schemas.js");
|
|
30
|
+
class AgentRuntime {
|
|
31
|
+
pool;
|
|
32
|
+
config;
|
|
33
|
+
logger;
|
|
34
|
+
constructor(options) {
|
|
35
|
+
this.config = import_schemas.AgentRuntimeConfigSchema.parse(options.config ?? {});
|
|
36
|
+
this.logger = options.logger;
|
|
37
|
+
this.pool = new import_AgentPool.AgentPool(this.config, this.logger);
|
|
38
|
+
this.logger.debug("AgentRuntime initialized", {
|
|
39
|
+
maxAgents: this.config.maxAgents,
|
|
40
|
+
defaultTaskTimeout: this.config.defaultTaskTimeout
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Spawn a new agent
|
|
45
|
+
*
|
|
46
|
+
* @param config - Configuration for the agent
|
|
47
|
+
* @returns Handle to the spawned agent
|
|
48
|
+
*/
|
|
49
|
+
async spawnAgent(config) {
|
|
50
|
+
if (!this.pool.canSpawn()) {
|
|
51
|
+
throw import_errors.RuntimeError.maxAgentsExceeded(this.pool.size, this.config.maxAgents);
|
|
52
|
+
}
|
|
53
|
+
const agentId = config.agentId ?? `agent-${(0, import_crypto.randomUUID)().slice(0, 8)}`;
|
|
54
|
+
if (this.pool.has(agentId)) {
|
|
55
|
+
throw import_errors.RuntimeError.agentAlreadyExists(agentId);
|
|
56
|
+
}
|
|
57
|
+
try {
|
|
58
|
+
const enrichedConfig = (0, import_config.enrichAgentConfig)(
|
|
59
|
+
config.agentConfig,
|
|
60
|
+
void 0,
|
|
61
|
+
// No config path
|
|
62
|
+
false
|
|
63
|
+
// Not interactive CLI
|
|
64
|
+
);
|
|
65
|
+
enrichedConfig.agentId = agentId;
|
|
66
|
+
const agent = new import_core.DextoAgent(enrichedConfig);
|
|
67
|
+
const sessionId = `session-${(0, import_crypto.randomUUID)().slice(0, 8)}`;
|
|
68
|
+
const handle = {
|
|
69
|
+
agentId,
|
|
70
|
+
agent,
|
|
71
|
+
status: "starting",
|
|
72
|
+
ephemeral: config.ephemeral ?? true,
|
|
73
|
+
createdAt: /* @__PURE__ */ new Date(),
|
|
74
|
+
sessionId
|
|
75
|
+
};
|
|
76
|
+
if (config.group !== void 0) {
|
|
77
|
+
handle.group = config.group;
|
|
78
|
+
}
|
|
79
|
+
if (config.metadata !== void 0) {
|
|
80
|
+
handle.metadata = config.metadata;
|
|
81
|
+
}
|
|
82
|
+
this.pool.add(handle);
|
|
83
|
+
if (config.onBeforeStart) {
|
|
84
|
+
await config.onBeforeStart(agent);
|
|
85
|
+
}
|
|
86
|
+
await agent.start();
|
|
87
|
+
this.pool.updateStatus(agentId, "idle");
|
|
88
|
+
this.logger.info(
|
|
89
|
+
`Spawned agent '${agentId}'${handle.group ? ` (group: ${handle.group})` : ""} (ephemeral: ${handle.ephemeral})`
|
|
90
|
+
);
|
|
91
|
+
return handle;
|
|
92
|
+
} catch (error) {
|
|
93
|
+
this.pool.remove(agentId);
|
|
94
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
95
|
+
throw import_errors.RuntimeError.spawnFailed(errorMessage, agentId);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Execute a task on an agent
|
|
100
|
+
*
|
|
101
|
+
* @param agentId - ID of the agent
|
|
102
|
+
* @param task - Task description to execute
|
|
103
|
+
* @param timeout - Optional timeout in milliseconds
|
|
104
|
+
* @returns Task result with response or error
|
|
105
|
+
*/
|
|
106
|
+
async executeTask(agentId, task, timeout) {
|
|
107
|
+
const handle = this.pool.get(agentId);
|
|
108
|
+
if (!handle) {
|
|
109
|
+
throw import_errors.RuntimeError.agentNotFound(agentId);
|
|
110
|
+
}
|
|
111
|
+
if (handle.status === "stopped" || handle.status === "error") {
|
|
112
|
+
throw import_errors.RuntimeError.agentAlreadyStopped(agentId);
|
|
113
|
+
}
|
|
114
|
+
const taskTimeout = timeout ?? this.config.defaultTaskTimeout;
|
|
115
|
+
this.pool.updateStatus(agentId, "running");
|
|
116
|
+
try {
|
|
117
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
118
|
+
setTimeout(() => {
|
|
119
|
+
reject(import_errors.RuntimeError.taskTimeout(agentId, taskTimeout));
|
|
120
|
+
}, taskTimeout);
|
|
121
|
+
});
|
|
122
|
+
const generatePromise = handle.agent.generate(task, handle.sessionId);
|
|
123
|
+
const response = await Promise.race([
|
|
124
|
+
generatePromise,
|
|
125
|
+
timeoutPromise
|
|
126
|
+
]);
|
|
127
|
+
this.pool.updateStatus(agentId, "idle");
|
|
128
|
+
const result = {
|
|
129
|
+
success: true,
|
|
130
|
+
response: response.content,
|
|
131
|
+
agentId,
|
|
132
|
+
tokenUsage: {
|
|
133
|
+
input: response.usage.inputTokens,
|
|
134
|
+
output: response.usage.outputTokens,
|
|
135
|
+
total: response.usage.totalTokens
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
this.logger.debug(`Task completed for agent '${agentId}'`);
|
|
139
|
+
return result;
|
|
140
|
+
} catch (error) {
|
|
141
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
142
|
+
this.pool.updateStatus(agentId, "error", errorMessage);
|
|
143
|
+
if (error instanceof Error && error.message.includes("Task execution timed out")) {
|
|
144
|
+
return {
|
|
145
|
+
success: false,
|
|
146
|
+
error: errorMessage,
|
|
147
|
+
agentId
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
throw import_errors.RuntimeError.taskFailed(agentId, errorMessage);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Get an agent handle by ID
|
|
155
|
+
*/
|
|
156
|
+
getAgent(agentId) {
|
|
157
|
+
return this.pool.get(agentId);
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* List agents matching the given filter
|
|
161
|
+
*/
|
|
162
|
+
listAgents(filter) {
|
|
163
|
+
return this.pool.list(filter);
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Stop a specific agent
|
|
167
|
+
*/
|
|
168
|
+
async stopAgent(agentId) {
|
|
169
|
+
const handle = this.pool.get(agentId);
|
|
170
|
+
if (!handle) {
|
|
171
|
+
throw import_errors.RuntimeError.agentNotFound(agentId);
|
|
172
|
+
}
|
|
173
|
+
if (handle.status === "stopped") {
|
|
174
|
+
this.logger.debug(`Agent '${agentId}' already stopped`);
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
this.pool.updateStatus(agentId, "stopping");
|
|
178
|
+
try {
|
|
179
|
+
handle.agent.services.approvalManager.cancelAllApprovals();
|
|
180
|
+
await handle.agent.stop();
|
|
181
|
+
this.pool.updateStatus(agentId, "stopped");
|
|
182
|
+
this.logger.debug(`Stopped agent '${agentId}'`);
|
|
183
|
+
if (handle.ephemeral) {
|
|
184
|
+
this.pool.remove(agentId);
|
|
185
|
+
this.logger.debug(`Removed ephemeral agent '${agentId}' from pool`);
|
|
186
|
+
}
|
|
187
|
+
} catch (error) {
|
|
188
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
189
|
+
this.pool.updateStatus(agentId, "error", errorMessage);
|
|
190
|
+
this.logger.error(`Failed to stop agent '${agentId}': ${errorMessage}`);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Stop all agents matching the given filter
|
|
195
|
+
*/
|
|
196
|
+
async stopAll(filter) {
|
|
197
|
+
const agents = this.pool.list(filter);
|
|
198
|
+
this.logger.debug(`Stopping ${agents.length} agents`);
|
|
199
|
+
await Promise.allSettled(agents.map((handle) => this.stopAgent(handle.agentId)));
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Get the runtime configuration
|
|
203
|
+
*/
|
|
204
|
+
getConfig() {
|
|
205
|
+
return { ...this.config };
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Get statistics about the runtime
|
|
209
|
+
*/
|
|
210
|
+
getStats() {
|
|
211
|
+
const agents = this.pool.getAll();
|
|
212
|
+
const byStatus = {};
|
|
213
|
+
for (const agent of agents) {
|
|
214
|
+
byStatus[agent.status] = (byStatus[agent.status] ?? 0) + 1;
|
|
215
|
+
}
|
|
216
|
+
return {
|
|
217
|
+
totalAgents: agents.length,
|
|
218
|
+
byStatus
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
223
|
+
0 && (module.exports = {
|
|
224
|
+
AgentRuntime
|
|
225
|
+
});
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AgentRuntime - General-Purpose Agent Lifecycle Manager
|
|
3
|
+
*
|
|
4
|
+
* Manages the lifecycle of multiple agent instances. Can be used for:
|
|
5
|
+
* - Dashboard spawning multiple independent agents
|
|
6
|
+
* - Agent task delegation (parent spawns sub-agents)
|
|
7
|
+
* - Test harnesses managing multiple agents
|
|
8
|
+
* - Any scenario requiring multiple concurrent agents
|
|
9
|
+
*
|
|
10
|
+
* Key responsibilities:
|
|
11
|
+
* - Spawn and manage agents with configurable limits
|
|
12
|
+
* - Execute tasks on agents with timeout handling
|
|
13
|
+
* - Track agent status and lifecycle
|
|
14
|
+
* - Clean up agents when no longer needed
|
|
15
|
+
*/
|
|
16
|
+
import { type IDextoLogger } from '@dexto/core';
|
|
17
|
+
import type { AgentRuntimeConfig, SpawnConfig, AgentHandle, TaskResult, AgentFilter } from './types.js';
|
|
18
|
+
import { type ValidatedAgentRuntimeConfig } from './schemas.js';
|
|
19
|
+
/**
|
|
20
|
+
* Options for creating an AgentRuntime
|
|
21
|
+
*/
|
|
22
|
+
export interface AgentRuntimeOptions {
|
|
23
|
+
/** Runtime configuration */
|
|
24
|
+
config?: AgentRuntimeConfig;
|
|
25
|
+
/** Logger instance */
|
|
26
|
+
logger: IDextoLogger;
|
|
27
|
+
}
|
|
28
|
+
export declare class AgentRuntime {
|
|
29
|
+
private pool;
|
|
30
|
+
private config;
|
|
31
|
+
private logger;
|
|
32
|
+
constructor(options: AgentRuntimeOptions);
|
|
33
|
+
/**
|
|
34
|
+
* Spawn a new agent
|
|
35
|
+
*
|
|
36
|
+
* @param config - Configuration for the agent
|
|
37
|
+
* @returns Handle to the spawned agent
|
|
38
|
+
*/
|
|
39
|
+
spawnAgent(config: SpawnConfig): Promise<AgentHandle>;
|
|
40
|
+
/**
|
|
41
|
+
* Execute a task on an agent
|
|
42
|
+
*
|
|
43
|
+
* @param agentId - ID of the agent
|
|
44
|
+
* @param task - Task description to execute
|
|
45
|
+
* @param timeout - Optional timeout in milliseconds
|
|
46
|
+
* @returns Task result with response or error
|
|
47
|
+
*/
|
|
48
|
+
executeTask(agentId: string, task: string, timeout?: number): Promise<TaskResult>;
|
|
49
|
+
/**
|
|
50
|
+
* Get an agent handle by ID
|
|
51
|
+
*/
|
|
52
|
+
getAgent(agentId: string): AgentHandle | undefined;
|
|
53
|
+
/**
|
|
54
|
+
* List agents matching the given filter
|
|
55
|
+
*/
|
|
56
|
+
listAgents(filter?: AgentFilter): AgentHandle[];
|
|
57
|
+
/**
|
|
58
|
+
* Stop a specific agent
|
|
59
|
+
*/
|
|
60
|
+
stopAgent(agentId: string): Promise<void>;
|
|
61
|
+
/**
|
|
62
|
+
* Stop all agents matching the given filter
|
|
63
|
+
*/
|
|
64
|
+
stopAll(filter?: AgentFilter): Promise<void>;
|
|
65
|
+
/**
|
|
66
|
+
* Get the runtime configuration
|
|
67
|
+
*/
|
|
68
|
+
getConfig(): ValidatedAgentRuntimeConfig;
|
|
69
|
+
/**
|
|
70
|
+
* Get statistics about the runtime
|
|
71
|
+
*/
|
|
72
|
+
getStats(): {
|
|
73
|
+
totalAgents: number;
|
|
74
|
+
byStatus: Record<string, number>;
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=AgentRuntime.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AgentRuntime.d.ts","sourceRoot":"","sources":["../../src/runtime/AgentRuntime.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAGH,OAAO,EAAc,KAAK,YAAY,EAAyB,MAAM,aAAa,CAAC;AAInF,OAAO,KAAK,EACR,kBAAkB,EAClB,WAAW,EACX,WAAW,EACX,UAAU,EACV,WAAW,EACd,MAAM,YAAY,CAAC;AACpB,OAAO,EAA4B,KAAK,2BAA2B,EAAE,MAAM,cAAc,CAAC;AAE1F;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAChC,4BAA4B;IAC5B,MAAM,CAAC,EAAE,kBAAkB,CAAC;IAC5B,sBAAsB;IACtB,MAAM,EAAE,YAAY,CAAC;CACxB;AAED,qBAAa,YAAY;IACrB,OAAO,CAAC,IAAI,CAAY;IACxB,OAAO,CAAC,MAAM,CAA8B;IAC5C,OAAO,CAAC,MAAM,CAAe;gBAEjB,OAAO,EAAE,mBAAmB;IAYxC;;;;;OAKG;IACG,UAAU,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC;IA0E3D;;;;;;;OAOG;IACG,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;IAoEvF;;OAEG;IACH,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,WAAW,GAAG,SAAS;IAIlD;;OAEG;IACH,UAAU,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,WAAW,EAAE;IAI/C;;OAEG;IACG,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAsC/C;;OAEG;IACG,OAAO,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IASlD;;OAEG;IACH,SAAS,IAAI,2BAA2B;IAIxC;;OAEG;IACH,QAAQ,IAAI;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;KAAE;CAaxE"}
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import { randomUUID } from "crypto";
|
|
2
|
+
import { DextoAgent } from "@dexto/core";
|
|
3
|
+
import { enrichAgentConfig } from "../config/index.js";
|
|
4
|
+
import { AgentPool } from "./AgentPool.js";
|
|
5
|
+
import { RuntimeError } from "./errors.js";
|
|
6
|
+
import { AgentRuntimeConfigSchema } from "./schemas.js";
|
|
7
|
+
class AgentRuntime {
|
|
8
|
+
pool;
|
|
9
|
+
config;
|
|
10
|
+
logger;
|
|
11
|
+
constructor(options) {
|
|
12
|
+
this.config = AgentRuntimeConfigSchema.parse(options.config ?? {});
|
|
13
|
+
this.logger = options.logger;
|
|
14
|
+
this.pool = new AgentPool(this.config, this.logger);
|
|
15
|
+
this.logger.debug("AgentRuntime initialized", {
|
|
16
|
+
maxAgents: this.config.maxAgents,
|
|
17
|
+
defaultTaskTimeout: this.config.defaultTaskTimeout
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Spawn a new agent
|
|
22
|
+
*
|
|
23
|
+
* @param config - Configuration for the agent
|
|
24
|
+
* @returns Handle to the spawned agent
|
|
25
|
+
*/
|
|
26
|
+
async spawnAgent(config) {
|
|
27
|
+
if (!this.pool.canSpawn()) {
|
|
28
|
+
throw RuntimeError.maxAgentsExceeded(this.pool.size, this.config.maxAgents);
|
|
29
|
+
}
|
|
30
|
+
const agentId = config.agentId ?? `agent-${randomUUID().slice(0, 8)}`;
|
|
31
|
+
if (this.pool.has(agentId)) {
|
|
32
|
+
throw RuntimeError.agentAlreadyExists(agentId);
|
|
33
|
+
}
|
|
34
|
+
try {
|
|
35
|
+
const enrichedConfig = enrichAgentConfig(
|
|
36
|
+
config.agentConfig,
|
|
37
|
+
void 0,
|
|
38
|
+
// No config path
|
|
39
|
+
false
|
|
40
|
+
// Not interactive CLI
|
|
41
|
+
);
|
|
42
|
+
enrichedConfig.agentId = agentId;
|
|
43
|
+
const agent = new DextoAgent(enrichedConfig);
|
|
44
|
+
const sessionId = `session-${randomUUID().slice(0, 8)}`;
|
|
45
|
+
const handle = {
|
|
46
|
+
agentId,
|
|
47
|
+
agent,
|
|
48
|
+
status: "starting",
|
|
49
|
+
ephemeral: config.ephemeral ?? true,
|
|
50
|
+
createdAt: /* @__PURE__ */ new Date(),
|
|
51
|
+
sessionId
|
|
52
|
+
};
|
|
53
|
+
if (config.group !== void 0) {
|
|
54
|
+
handle.group = config.group;
|
|
55
|
+
}
|
|
56
|
+
if (config.metadata !== void 0) {
|
|
57
|
+
handle.metadata = config.metadata;
|
|
58
|
+
}
|
|
59
|
+
this.pool.add(handle);
|
|
60
|
+
if (config.onBeforeStart) {
|
|
61
|
+
await config.onBeforeStart(agent);
|
|
62
|
+
}
|
|
63
|
+
await agent.start();
|
|
64
|
+
this.pool.updateStatus(agentId, "idle");
|
|
65
|
+
this.logger.info(
|
|
66
|
+
`Spawned agent '${agentId}'${handle.group ? ` (group: ${handle.group})` : ""} (ephemeral: ${handle.ephemeral})`
|
|
67
|
+
);
|
|
68
|
+
return handle;
|
|
69
|
+
} catch (error) {
|
|
70
|
+
this.pool.remove(agentId);
|
|
71
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
72
|
+
throw RuntimeError.spawnFailed(errorMessage, agentId);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Execute a task on an agent
|
|
77
|
+
*
|
|
78
|
+
* @param agentId - ID of the agent
|
|
79
|
+
* @param task - Task description to execute
|
|
80
|
+
* @param timeout - Optional timeout in milliseconds
|
|
81
|
+
* @returns Task result with response or error
|
|
82
|
+
*/
|
|
83
|
+
async executeTask(agentId, task, timeout) {
|
|
84
|
+
const handle = this.pool.get(agentId);
|
|
85
|
+
if (!handle) {
|
|
86
|
+
throw RuntimeError.agentNotFound(agentId);
|
|
87
|
+
}
|
|
88
|
+
if (handle.status === "stopped" || handle.status === "error") {
|
|
89
|
+
throw RuntimeError.agentAlreadyStopped(agentId);
|
|
90
|
+
}
|
|
91
|
+
const taskTimeout = timeout ?? this.config.defaultTaskTimeout;
|
|
92
|
+
this.pool.updateStatus(agentId, "running");
|
|
93
|
+
try {
|
|
94
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
95
|
+
setTimeout(() => {
|
|
96
|
+
reject(RuntimeError.taskTimeout(agentId, taskTimeout));
|
|
97
|
+
}, taskTimeout);
|
|
98
|
+
});
|
|
99
|
+
const generatePromise = handle.agent.generate(task, handle.sessionId);
|
|
100
|
+
const response = await Promise.race([
|
|
101
|
+
generatePromise,
|
|
102
|
+
timeoutPromise
|
|
103
|
+
]);
|
|
104
|
+
this.pool.updateStatus(agentId, "idle");
|
|
105
|
+
const result = {
|
|
106
|
+
success: true,
|
|
107
|
+
response: response.content,
|
|
108
|
+
agentId,
|
|
109
|
+
tokenUsage: {
|
|
110
|
+
input: response.usage.inputTokens,
|
|
111
|
+
output: response.usage.outputTokens,
|
|
112
|
+
total: response.usage.totalTokens
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
this.logger.debug(`Task completed for agent '${agentId}'`);
|
|
116
|
+
return result;
|
|
117
|
+
} catch (error) {
|
|
118
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
119
|
+
this.pool.updateStatus(agentId, "error", errorMessage);
|
|
120
|
+
if (error instanceof Error && error.message.includes("Task execution timed out")) {
|
|
121
|
+
return {
|
|
122
|
+
success: false,
|
|
123
|
+
error: errorMessage,
|
|
124
|
+
agentId
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
throw RuntimeError.taskFailed(agentId, errorMessage);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Get an agent handle by ID
|
|
132
|
+
*/
|
|
133
|
+
getAgent(agentId) {
|
|
134
|
+
return this.pool.get(agentId);
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* List agents matching the given filter
|
|
138
|
+
*/
|
|
139
|
+
listAgents(filter) {
|
|
140
|
+
return this.pool.list(filter);
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Stop a specific agent
|
|
144
|
+
*/
|
|
145
|
+
async stopAgent(agentId) {
|
|
146
|
+
const handle = this.pool.get(agentId);
|
|
147
|
+
if (!handle) {
|
|
148
|
+
throw RuntimeError.agentNotFound(agentId);
|
|
149
|
+
}
|
|
150
|
+
if (handle.status === "stopped") {
|
|
151
|
+
this.logger.debug(`Agent '${agentId}' already stopped`);
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
this.pool.updateStatus(agentId, "stopping");
|
|
155
|
+
try {
|
|
156
|
+
handle.agent.services.approvalManager.cancelAllApprovals();
|
|
157
|
+
await handle.agent.stop();
|
|
158
|
+
this.pool.updateStatus(agentId, "stopped");
|
|
159
|
+
this.logger.debug(`Stopped agent '${agentId}'`);
|
|
160
|
+
if (handle.ephemeral) {
|
|
161
|
+
this.pool.remove(agentId);
|
|
162
|
+
this.logger.debug(`Removed ephemeral agent '${agentId}' from pool`);
|
|
163
|
+
}
|
|
164
|
+
} catch (error) {
|
|
165
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
166
|
+
this.pool.updateStatus(agentId, "error", errorMessage);
|
|
167
|
+
this.logger.error(`Failed to stop agent '${agentId}': ${errorMessage}`);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Stop all agents matching the given filter
|
|
172
|
+
*/
|
|
173
|
+
async stopAll(filter) {
|
|
174
|
+
const agents = this.pool.list(filter);
|
|
175
|
+
this.logger.debug(`Stopping ${agents.length} agents`);
|
|
176
|
+
await Promise.allSettled(agents.map((handle) => this.stopAgent(handle.agentId)));
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Get the runtime configuration
|
|
180
|
+
*/
|
|
181
|
+
getConfig() {
|
|
182
|
+
return { ...this.config };
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Get statistics about the runtime
|
|
186
|
+
*/
|
|
187
|
+
getStats() {
|
|
188
|
+
const agents = this.pool.getAll();
|
|
189
|
+
const byStatus = {};
|
|
190
|
+
for (const agent of agents) {
|
|
191
|
+
byStatus[agent.status] = (byStatus[agent.status] ?? 0) + 1;
|
|
192
|
+
}
|
|
193
|
+
return {
|
|
194
|
+
totalAgents: agents.length,
|
|
195
|
+
byStatus
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
export {
|
|
200
|
+
AgentRuntime
|
|
201
|
+
};
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
var approval_delegation_exports = {};
|
|
20
|
+
__export(approval_delegation_exports, {
|
|
21
|
+
createDelegatingApprovalHandler: () => createDelegatingApprovalHandler
|
|
22
|
+
});
|
|
23
|
+
module.exports = __toCommonJS(approval_delegation_exports);
|
|
24
|
+
function createDelegatingApprovalHandler(parentApprovalManager, subAgentId, logger) {
|
|
25
|
+
const pendingApprovalIds = /* @__PURE__ */ new Set();
|
|
26
|
+
const handler = Object.assign(
|
|
27
|
+
async (request) => {
|
|
28
|
+
logger.debug(
|
|
29
|
+
`Delegating approval '${request.approvalId}' (type: ${request.type}) from sub-agent '${subAgentId}'`
|
|
30
|
+
);
|
|
31
|
+
pendingApprovalIds.add(request.approvalId);
|
|
32
|
+
try {
|
|
33
|
+
const delegatedDetails = {
|
|
34
|
+
type: request.type,
|
|
35
|
+
timeout: request.timeout,
|
|
36
|
+
sessionId: request.sessionId,
|
|
37
|
+
metadata: {
|
|
38
|
+
...request.metadata,
|
|
39
|
+
delegatedFromAgent: subAgentId
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
const response = await parentApprovalManager.requestApproval(delegatedDetails);
|
|
43
|
+
logger.debug(
|
|
44
|
+
`Approval '${request.approvalId}' delegated response: ${response.status}`
|
|
45
|
+
);
|
|
46
|
+
return response;
|
|
47
|
+
} finally {
|
|
48
|
+
pendingApprovalIds.delete(request.approvalId);
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
/**
|
|
53
|
+
* Cancel a specific pending approval request
|
|
54
|
+
*/
|
|
55
|
+
cancel: (approvalId) => {
|
|
56
|
+
if (pendingApprovalIds.has(approvalId)) {
|
|
57
|
+
logger.debug(
|
|
58
|
+
`Cancelling delegated approval '${approvalId}' for sub-agent '${subAgentId}'`
|
|
59
|
+
);
|
|
60
|
+
parentApprovalManager.cancelApproval(approvalId);
|
|
61
|
+
pendingApprovalIds.delete(approvalId);
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
/**
|
|
65
|
+
* Cancel all pending approval requests for this sub-agent
|
|
66
|
+
*/
|
|
67
|
+
cancelAll: () => {
|
|
68
|
+
if (pendingApprovalIds.size > 0) {
|
|
69
|
+
logger.debug(
|
|
70
|
+
`Cancelling all ${pendingApprovalIds.size} delegated approvals for sub-agent '${subAgentId}'`
|
|
71
|
+
);
|
|
72
|
+
for (const approvalId of pendingApprovalIds) {
|
|
73
|
+
parentApprovalManager.cancelApproval(approvalId);
|
|
74
|
+
}
|
|
75
|
+
pendingApprovalIds.clear();
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
/**
|
|
79
|
+
* Get list of pending approval IDs for this sub-agent
|
|
80
|
+
*/
|
|
81
|
+
getPending: () => {
|
|
82
|
+
return Array.from(pendingApprovalIds);
|
|
83
|
+
},
|
|
84
|
+
/**
|
|
85
|
+
* Get pending approval requests (not available for delegated handlers)
|
|
86
|
+
*/
|
|
87
|
+
getPendingRequests: () => {
|
|
88
|
+
return [];
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
);
|
|
92
|
+
return handler;
|
|
93
|
+
}
|
|
94
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
95
|
+
0 && (module.exports = {
|
|
96
|
+
createDelegatingApprovalHandler
|
|
97
|
+
});
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Approval Delegation for Sub-Agents
|
|
3
|
+
*
|
|
4
|
+
* Creates ApprovalHandler instances that delegate approval requests
|
|
5
|
+
* from sub-agents to the parent agent's approval system.
|
|
6
|
+
*/
|
|
7
|
+
import type { ApprovalHandler, ApprovalManager, IDextoLogger } from '@dexto/core';
|
|
8
|
+
/**
|
|
9
|
+
* Creates an ApprovalHandler that delegates requests to a parent's ApprovalManager.
|
|
10
|
+
*
|
|
11
|
+
* This allows sub-agent tool approvals to flow through the parent's approval system,
|
|
12
|
+
* ensuring the user sees a unified approval UI regardless of which agent made the request.
|
|
13
|
+
*
|
|
14
|
+
* @param parentApprovalManager - The parent agent's ApprovalManager
|
|
15
|
+
* @param subAgentId - ID of the sub-agent for tracking
|
|
16
|
+
* @param logger - Logger for debugging
|
|
17
|
+
* @returns An ApprovalHandler that can be set on the sub-agent's ApprovalManager
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```typescript
|
|
21
|
+
* const delegatingHandler = createDelegatingApprovalHandler(
|
|
22
|
+
* parentAgent.services.approvalManager,
|
|
23
|
+
* subAgentId,
|
|
24
|
+
* logger
|
|
25
|
+
* );
|
|
26
|
+
* subAgent.services.approvalManager.setHandler(delegatingHandler);
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
export declare function createDelegatingApprovalHandler(parentApprovalManager: ApprovalManager, subAgentId: string, logger: IDextoLogger): ApprovalHandler;
|
|
30
|
+
//# sourceMappingURL=approval-delegation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"approval-delegation.d.ts","sourceRoot":"","sources":["../../src/runtime/approval-delegation.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EACR,eAAe,EAIf,eAAe,EACf,YAAY,EACf,MAAM,aAAa,CAAC;AAYrB;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,+BAA+B,CAC3C,qBAAqB,EAAE,eAAe,EACtC,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,YAAY,GACrB,eAAe,CAqFjB"}
|