@igniter-js/agents 0.1.0
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/AGENTS.md +2913 -0
- package/CHANGELOG.md +5 -0
- package/README.md +1585 -0
- package/dist/adapters/index.d.mts +2 -0
- package/dist/adapters/index.d.ts +2 -0
- package/dist/adapters/index.js +1024 -0
- package/dist/adapters/index.js.map +1 -0
- package/dist/adapters/index.mjs +1021 -0
- package/dist/adapters/index.mjs.map +1 -0
- package/dist/index-CX4IgrRt.d.mts +2181 -0
- package/dist/index-CX4IgrRt.d.ts +2181 -0
- package/dist/index.d.mts +3170 -0
- package/dist/index.d.ts +3170 -0
- package/dist/index.js +4045 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +4011 -0
- package/dist/index.mjs.map +1 -0
- package/dist/shim.d.mts +72 -0
- package/dist/shim.d.ts +72 -0
- package/dist/shim.js +105 -0
- package/dist/shim.js.map +1 -0
- package/dist/shim.mjs +89 -0
- package/dist/shim.mjs.map +1 -0
- package/dist/telemetry/index.d.mts +340 -0
- package/dist/telemetry/index.d.ts +340 -0
- package/dist/telemetry/index.js +157 -0
- package/dist/telemetry/index.js.map +1 -0
- package/dist/telemetry/index.mjs +155 -0
- package/dist/telemetry/index.mjs.map +1 -0
- package/package.json +116 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,4045 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var core = require('@igniter-js/core');
|
|
4
|
+
var mcp = require('@ai-sdk/mcp');
|
|
5
|
+
var stdio_js = require('@modelcontextprotocol/sdk/client/stdio.js');
|
|
6
|
+
var streamableHttp_js = require('@modelcontextprotocol/sdk/client/streamableHttp.js');
|
|
7
|
+
var ai = require('ai');
|
|
8
|
+
var telemetry = require('@igniter-js/telemetry');
|
|
9
|
+
var zod = require('zod');
|
|
10
|
+
var promises = require('fs/promises');
|
|
11
|
+
var path = require('path');
|
|
12
|
+
|
|
13
|
+
// src/errors/agent.error.ts
|
|
14
|
+
var IgniterAgentErrorCode = /* @__PURE__ */ ((IgniterAgentErrorCode3) => {
|
|
15
|
+
IgniterAgentErrorCode3["UNKNOWN"] = "IGNITER_AGENT_UNKNOWN_ERROR";
|
|
16
|
+
IgniterAgentErrorCode3["INVALID_CONFIG"] = "IGNITER_AGENT_INVALID_CONFIG";
|
|
17
|
+
IgniterAgentErrorCode3["MISSING_REQUIRED"] = "IGNITER_AGENT_MISSING_REQUIRED";
|
|
18
|
+
IgniterAgentErrorCode3["AGENT_NOT_INITIALIZED"] = "IGNITER_AGENT_NOT_INITIALIZED";
|
|
19
|
+
IgniterAgentErrorCode3["AGENT_MODEL_MISSING"] = "IGNITER_AGENT_MODEL_MISSING";
|
|
20
|
+
IgniterAgentErrorCode3["AGENT_BUILD_FAILED"] = "IGNITER_AGENT_BUILD_FAILED";
|
|
21
|
+
IgniterAgentErrorCode3["MCP_CONNECTION_FAILED"] = "IGNITER_AGENT_MCP_CONNECTION_FAILED";
|
|
22
|
+
IgniterAgentErrorCode3["MCP_CLIENT_NOT_FOUND"] = "IGNITER_AGENT_MCP_CLIENT_NOT_FOUND";
|
|
23
|
+
IgniterAgentErrorCode3["MCP_TOOL_ERROR"] = "IGNITER_AGENT_MCP_TOOL_ERROR";
|
|
24
|
+
IgniterAgentErrorCode3["MCP_INVALID_CONFIG"] = "IGNITER_AGENT_MCP_INVALID_CONFIG";
|
|
25
|
+
IgniterAgentErrorCode3["TOOL_EXECUTION_FAILED"] = "IGNITER_AGENT_TOOL_EXECUTION_FAILED";
|
|
26
|
+
IgniterAgentErrorCode3["TOOL_NOT_FOUND"] = "IGNITER_AGENT_TOOL_NOT_FOUND";
|
|
27
|
+
IgniterAgentErrorCode3["TOOL_VALIDATION_FAILED"] = "IGNITER_AGENT_TOOL_VALIDATION_FAILED";
|
|
28
|
+
IgniterAgentErrorCode3["AGENT_CONTEXT_SCHEMA_INVALID"] = "IGNITER_AGENT_CONTEXT_SCHEMA_INVALID";
|
|
29
|
+
IgniterAgentErrorCode3["MEMORY_PROVIDER_ERROR"] = "IGNITER_AGENT_MEMORY_PROVIDER_ERROR";
|
|
30
|
+
IgniterAgentErrorCode3["MEMORY_NOT_FOUND"] = "IGNITER_AGENT_MEMORY_NOT_FOUND";
|
|
31
|
+
IgniterAgentErrorCode3["MEMORY_UPDATE_FAILED"] = "IGNITER_AGENT_MEMORY_UPDATE_FAILED";
|
|
32
|
+
IgniterAgentErrorCode3["ADAPTER_CONNECTION_FAILED"] = "IGNITER_AGENT_ADAPTER_CONNECTION_FAILED";
|
|
33
|
+
IgniterAgentErrorCode3["ADAPTER_OPERATION_FAILED"] = "IGNITER_AGENT_ADAPTER_OPERATION_FAILED";
|
|
34
|
+
IgniterAgentErrorCode3["ADAPTER_NOT_INITIALIZED"] = "IGNITER_AGENT_ADAPTER_NOT_INITIALIZED";
|
|
35
|
+
return IgniterAgentErrorCode3;
|
|
36
|
+
})(IgniterAgentErrorCode || {});
|
|
37
|
+
var IgniterAgentError = class extends core.IgniterError {
|
|
38
|
+
/**
|
|
39
|
+
* Creates a new IgniterAgentError.
|
|
40
|
+
*
|
|
41
|
+
* @param options - Error configuration options
|
|
42
|
+
*/
|
|
43
|
+
constructor(options) {
|
|
44
|
+
super({
|
|
45
|
+
message: options.message,
|
|
46
|
+
code: options.code,
|
|
47
|
+
statusCode: options.statusCode ?? 500,
|
|
48
|
+
causer: options.causer,
|
|
49
|
+
details: options.details,
|
|
50
|
+
metadata: options.metadata,
|
|
51
|
+
logger: options.logger,
|
|
52
|
+
cause: options.cause
|
|
53
|
+
});
|
|
54
|
+
this.name = "IgniterAgentError";
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
var IgniterAgentConfigError = class extends IgniterAgentError {
|
|
58
|
+
constructor(options) {
|
|
59
|
+
super({
|
|
60
|
+
...options,
|
|
61
|
+
code: "IGNITER_AGENT_INVALID_CONFIG" /* INVALID_CONFIG */,
|
|
62
|
+
metadata: { ...options.metadata, field: options.field }
|
|
63
|
+
});
|
|
64
|
+
this.name = "IgniterAgentConfigError";
|
|
65
|
+
this.field = options.field;
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
var IgniterAgentMCPError = class extends IgniterAgentError {
|
|
69
|
+
constructor(options) {
|
|
70
|
+
super({
|
|
71
|
+
...options,
|
|
72
|
+
metadata: { ...options.metadata, mcpName: options.mcpName }
|
|
73
|
+
});
|
|
74
|
+
this.name = "IgniterAgentMCPError";
|
|
75
|
+
this.mcpName = options.mcpName;
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
var IgniterAgentToolError = class extends IgniterAgentError {
|
|
79
|
+
constructor(options) {
|
|
80
|
+
super({
|
|
81
|
+
...options,
|
|
82
|
+
code: "IGNITER_AGENT_TOOL_EXECUTION_FAILED" /* TOOL_EXECUTION_FAILED */,
|
|
83
|
+
metadata: { ...options.metadata, toolName: options.toolName }
|
|
84
|
+
});
|
|
85
|
+
this.name = "IgniterAgentToolError";
|
|
86
|
+
this.toolName = options.toolName;
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
var IgniterAgentMemoryError = class extends IgniterAgentError {
|
|
90
|
+
constructor(options) {
|
|
91
|
+
super(options);
|
|
92
|
+
this.name = "IgniterAgentMemoryError";
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
var IgniterAgentAdapterError = class extends IgniterAgentError {
|
|
96
|
+
constructor(options) {
|
|
97
|
+
super({
|
|
98
|
+
...options,
|
|
99
|
+
metadata: { ...options.metadata, adapterName: options.adapterName }
|
|
100
|
+
});
|
|
101
|
+
this.name = "IgniterAgentAdapterError";
|
|
102
|
+
this.adapterName = options.adapterName;
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
function isIgniterAgentError(error) {
|
|
106
|
+
return error instanceof IgniterAgentError;
|
|
107
|
+
}
|
|
108
|
+
function isIgniterAgentMCPError(error) {
|
|
109
|
+
return error instanceof IgniterAgentMCPError;
|
|
110
|
+
}
|
|
111
|
+
function isIgniterAgentToolError(error) {
|
|
112
|
+
return error instanceof IgniterAgentToolError;
|
|
113
|
+
}
|
|
114
|
+
function wrapError(error, options = {}) {
|
|
115
|
+
if (isIgniterAgentError(error)) {
|
|
116
|
+
return error;
|
|
117
|
+
}
|
|
118
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
119
|
+
const cause = error instanceof Error ? error : void 0;
|
|
120
|
+
return new IgniterAgentError({
|
|
121
|
+
message,
|
|
122
|
+
code: "IGNITER_AGENT_UNKNOWN_ERROR" /* UNKNOWN */,
|
|
123
|
+
...options,
|
|
124
|
+
cause
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// src/core/manager.ts
|
|
129
|
+
var IgniterAgentManagerCore = class _IgniterAgentManagerCore {
|
|
130
|
+
/**
|
|
131
|
+
* Creates a new IgniterAgentManager.
|
|
132
|
+
*
|
|
133
|
+
* @param options - Manager configuration options
|
|
134
|
+
*
|
|
135
|
+
* @example
|
|
136
|
+
* ```typescript
|
|
137
|
+
* const manager = new IgniterAgentManager({
|
|
138
|
+
* autoStart: false,
|
|
139
|
+
* continueOnError: true
|
|
140
|
+
* });
|
|
141
|
+
* ```
|
|
142
|
+
*/
|
|
143
|
+
constructor(options) {
|
|
144
|
+
/**
|
|
145
|
+
* Agent statuses.
|
|
146
|
+
* @internal
|
|
147
|
+
*/
|
|
148
|
+
this._statuses = /* @__PURE__ */ new Map();
|
|
149
|
+
this._options = {
|
|
150
|
+
logger: options.logger,
|
|
151
|
+
telemetry: options.telemetry,
|
|
152
|
+
agents: options.agents ?? {},
|
|
153
|
+
autoStart: options.autoStart ?? false,
|
|
154
|
+
continueOnError: options.continueOnError ?? true,
|
|
155
|
+
onAgentStart: options.onAgentStart ?? (() => {
|
|
156
|
+
}),
|
|
157
|
+
onAgentError: options.onAgentError ?? (() => {
|
|
158
|
+
}),
|
|
159
|
+
onToolCallStart: options.onToolCallStart ?? (() => {
|
|
160
|
+
}),
|
|
161
|
+
onToolCallEnd: options.onToolCallEnd ?? (() => {
|
|
162
|
+
}),
|
|
163
|
+
onToolCallError: options.onToolCallError ?? (() => {
|
|
164
|
+
}),
|
|
165
|
+
onMCPStart: options.onMCPStart ?? (() => {
|
|
166
|
+
}),
|
|
167
|
+
onMCPError: options.onMCPError ?? (() => {
|
|
168
|
+
})
|
|
169
|
+
};
|
|
170
|
+
for (const [name, agent] of Object.entries(this._options.agents)) {
|
|
171
|
+
this.applyManagerContext(name, agent);
|
|
172
|
+
this._statuses.set(name, {
|
|
173
|
+
name,
|
|
174
|
+
status: "idle",
|
|
175
|
+
registeredAt: /* @__PURE__ */ new Date(),
|
|
176
|
+
toolsetCount: Object.keys(agent.getToolsets()).length
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
applyManagerContext(name, agent) {
|
|
181
|
+
const logger = this._options.logger;
|
|
182
|
+
const telemetry = this._options.telemetry;
|
|
183
|
+
if (logger) {
|
|
184
|
+
const scopedLogger = logger.child?.("IgniterAgent", { agent: name }) ?? logger;
|
|
185
|
+
agent.attachLogger?.(scopedLogger);
|
|
186
|
+
}
|
|
187
|
+
if (telemetry) {
|
|
188
|
+
agent.attachTelemetry?.(telemetry);
|
|
189
|
+
}
|
|
190
|
+
agent.attachHooks?.({
|
|
191
|
+
onToolCallStart: this._options.onToolCallStart,
|
|
192
|
+
onToolCallEnd: this._options.onToolCallEnd,
|
|
193
|
+
onToolCallError: this._options.onToolCallError,
|
|
194
|
+
onMCPStart: this._options.onMCPStart,
|
|
195
|
+
onMCPError: this._options.onMCPError
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Creates a new IgniterAgentManager with default options.
|
|
200
|
+
* @param options - Manager configuration options
|
|
201
|
+
* @returns A new IgniterAgentManager instance
|
|
202
|
+
* @example
|
|
203
|
+
* ```typescript
|
|
204
|
+
* const manager = IgniterAgentManager.create({
|
|
205
|
+
* autoStart: true
|
|
206
|
+
* });
|
|
207
|
+
* ```
|
|
208
|
+
*/
|
|
209
|
+
static create() {
|
|
210
|
+
return new _IgniterAgentManagerCore({
|
|
211
|
+
agents: {}
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
/* ---------------------------------------------------------------------------
|
|
215
|
+
* REGISTRATION
|
|
216
|
+
* --------------------------------------------------------------------------- */
|
|
217
|
+
/**
|
|
218
|
+
* Registers an agent with the manager.
|
|
219
|
+
*
|
|
220
|
+
* @description
|
|
221
|
+
* Adds an agent to the manager's registry. If `autoStart` is enabled,
|
|
222
|
+
* the agent will be started immediately after registration.
|
|
223
|
+
*
|
|
224
|
+
* @param name - Unique name for the agent
|
|
225
|
+
* @param agent - The built agent instance
|
|
226
|
+
* @returns This manager for chaining
|
|
227
|
+
* @throws {IgniterAgentError} If an agent with the name already exists
|
|
228
|
+
*
|
|
229
|
+
* @example
|
|
230
|
+
* ```typescript
|
|
231
|
+
* manager
|
|
232
|
+
* .register('support', supportAgent)
|
|
233
|
+
* .register('sales', salesAgent);
|
|
234
|
+
* ```
|
|
235
|
+
*/
|
|
236
|
+
register(name, agent) {
|
|
237
|
+
if (this._options.agents[name]) {
|
|
238
|
+
throw new IgniterAgentError({
|
|
239
|
+
message: `Agent '${name}' is already registered`,
|
|
240
|
+
code: "IGNITER_AGENT_INVALID_CONFIG" /* INVALID_CONFIG */,
|
|
241
|
+
causer: "IgniterAgentManager",
|
|
242
|
+
metadata: { operation: "register" }
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
this._options.agents[name] = agent;
|
|
246
|
+
this.applyManagerContext(name, agent);
|
|
247
|
+
this._statuses.set(name, {
|
|
248
|
+
name,
|
|
249
|
+
status: "idle",
|
|
250
|
+
registeredAt: /* @__PURE__ */ new Date(),
|
|
251
|
+
toolsetCount: Object.keys(agent.getToolsets()).length
|
|
252
|
+
});
|
|
253
|
+
if (this._options.autoStart) {
|
|
254
|
+
this.start(name).catch((err) => {
|
|
255
|
+
this._options.onAgentError(name, err);
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
return this;
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* Unregisters an agent from the manager.
|
|
262
|
+
*
|
|
263
|
+
* @param name - The agent name to unregister
|
|
264
|
+
* @returns True if the agent was unregistered
|
|
265
|
+
*
|
|
266
|
+
* @example
|
|
267
|
+
* ```typescript
|
|
268
|
+
* manager.unregister('old-agent');
|
|
269
|
+
* ```
|
|
270
|
+
*/
|
|
271
|
+
unregister(name) {
|
|
272
|
+
const removed = delete this._options.agents[name];
|
|
273
|
+
this._statuses.delete(name);
|
|
274
|
+
return removed;
|
|
275
|
+
}
|
|
276
|
+
/**
|
|
277
|
+
* Checks if an agent is registered.
|
|
278
|
+
*
|
|
279
|
+
* @param name - The agent name to check
|
|
280
|
+
* @returns True if the agent is registered
|
|
281
|
+
*/
|
|
282
|
+
has(name) {
|
|
283
|
+
return Boolean(this._options.agents[name]);
|
|
284
|
+
}
|
|
285
|
+
/* ---------------------------------------------------------------------------
|
|
286
|
+
* LIFECYCLE
|
|
287
|
+
* --------------------------------------------------------------------------- */
|
|
288
|
+
/**
|
|
289
|
+
* Starts a specific agent.
|
|
290
|
+
*
|
|
291
|
+
* @description
|
|
292
|
+
* Initializes the agent's MCP connections and prepares it for use.
|
|
293
|
+
*
|
|
294
|
+
* @param name - The agent name to start
|
|
295
|
+
* @returns The agent's toolsets after initialization
|
|
296
|
+
* @throws {IgniterAgentError} If the agent is not found
|
|
297
|
+
*
|
|
298
|
+
* @example
|
|
299
|
+
* ```typescript
|
|
300
|
+
* const toolsets = await manager.start('support');
|
|
301
|
+
* console.log('Connected toolsets:', Object.keys(toolsets));
|
|
302
|
+
* ```
|
|
303
|
+
*/
|
|
304
|
+
async start(name) {
|
|
305
|
+
const agent = this._options.agents[name];
|
|
306
|
+
const info = this._statuses.get(name);
|
|
307
|
+
if (!agent || !info) {
|
|
308
|
+
throw new IgniterAgentError({
|
|
309
|
+
message: `Agent '${name}' is not registered`,
|
|
310
|
+
code: "IGNITER_AGENT_NOT_INITIALIZED" /* AGENT_NOT_INITIALIZED */,
|
|
311
|
+
causer: "IgniterAgentManager",
|
|
312
|
+
metadata: { operation: "start" }
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
try {
|
|
316
|
+
info.status = "starting";
|
|
317
|
+
this._options.logger?.debug("IgniterAgentManager.start started", {
|
|
318
|
+
agent: name
|
|
319
|
+
});
|
|
320
|
+
await agent.start();
|
|
321
|
+
info.status = "running";
|
|
322
|
+
info.startedAt = /* @__PURE__ */ new Date();
|
|
323
|
+
info.toolsetCount = Object.keys(agent.getToolsets()).length;
|
|
324
|
+
this._options.onAgentStart?.(name);
|
|
325
|
+
this._options.logger?.success?.("IgniterAgentManager.start success", {
|
|
326
|
+
agent: name
|
|
327
|
+
});
|
|
328
|
+
return agent;
|
|
329
|
+
} catch (error) {
|
|
330
|
+
info.status = "error";
|
|
331
|
+
info.error = error instanceof Error ? error : new Error(String(error));
|
|
332
|
+
this._options.onAgentError?.(name, info.error);
|
|
333
|
+
this._options.logger?.error("IgniterAgentManager.start failed", info.error);
|
|
334
|
+
throw error;
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* Starts all registered agents.
|
|
339
|
+
*
|
|
340
|
+
* @description
|
|
341
|
+
* Initializes all agents in parallel. If `continueOnError` is true,
|
|
342
|
+
* failed agents won't prevent others from starting.
|
|
343
|
+
*
|
|
344
|
+
* @returns Map of agent names to their results (toolsets or errors)
|
|
345
|
+
*
|
|
346
|
+
* @example
|
|
347
|
+
* ```typescript
|
|
348
|
+
* const results = await manager.startAll();
|
|
349
|
+
*
|
|
350
|
+
* for (const [name, result] of results) {
|
|
351
|
+
* if (result instanceof Error) {
|
|
352
|
+
* console.error(`${name} failed:`, result.message);
|
|
353
|
+
* } else {
|
|
354
|
+
* console.log(`${name} started with ${Object.keys(result).length} toolsets`);
|
|
355
|
+
* }
|
|
356
|
+
* }
|
|
357
|
+
* ```
|
|
358
|
+
*/
|
|
359
|
+
async startAll() {
|
|
360
|
+
const results = /* @__PURE__ */ new Map();
|
|
361
|
+
const promises = Object.keys(this._options.agents).map(async (name) => {
|
|
362
|
+
try {
|
|
363
|
+
const builtAgent = await this.start(name);
|
|
364
|
+
results.set(name, builtAgent.getToolsets());
|
|
365
|
+
} catch (error) {
|
|
366
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
367
|
+
results.set(name, err);
|
|
368
|
+
if (!this._options.continueOnError) {
|
|
369
|
+
throw error;
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
});
|
|
373
|
+
await Promise.all(promises);
|
|
374
|
+
this._options.logger?.info("IgniterAgentManager.startAll completed", {
|
|
375
|
+
agents: this.getNames().length,
|
|
376
|
+
failed: this.getFailedAgents().length
|
|
377
|
+
});
|
|
378
|
+
return results;
|
|
379
|
+
}
|
|
380
|
+
/* ---------------------------------------------------------------------------
|
|
381
|
+
* ACCESS
|
|
382
|
+
* --------------------------------------------------------------------------- */
|
|
383
|
+
/**
|
|
384
|
+
* Gets a registered agent by name.
|
|
385
|
+
*
|
|
386
|
+
* @param name - The agent name
|
|
387
|
+
* @returns The agent instance
|
|
388
|
+
* @throws {IgniterAgentError} If the agent is not found
|
|
389
|
+
*
|
|
390
|
+
* @example
|
|
391
|
+
* ```typescript
|
|
392
|
+
* const agent = manager.get('support');
|
|
393
|
+
* const response = await agent.generate({ messages: [...] });
|
|
394
|
+
* ```
|
|
395
|
+
*/
|
|
396
|
+
get(name) {
|
|
397
|
+
const agent = this._options.agents[name];
|
|
398
|
+
if (!agent) {
|
|
399
|
+
throw new IgniterAgentError({
|
|
400
|
+
message: `Agent '${name}' is not registered`,
|
|
401
|
+
code: "IGNITER_AGENT_NOT_INITIALIZED" /* AGENT_NOT_INITIALIZED */,
|
|
402
|
+
causer: "IgniterAgentManager",
|
|
403
|
+
metadata: { operation: "get" }
|
|
404
|
+
});
|
|
405
|
+
}
|
|
406
|
+
return agent;
|
|
407
|
+
}
|
|
408
|
+
/**
|
|
409
|
+
* Gets an agent if it exists, undefined otherwise.
|
|
410
|
+
*
|
|
411
|
+
* @param name - The agent name
|
|
412
|
+
* @returns The agent instance or undefined
|
|
413
|
+
*/
|
|
414
|
+
tryGet(name) {
|
|
415
|
+
return this._options.agents[name];
|
|
416
|
+
}
|
|
417
|
+
/**
|
|
418
|
+
* Gets all registered agent names.
|
|
419
|
+
*
|
|
420
|
+
* @returns Array of agent names
|
|
421
|
+
*/
|
|
422
|
+
getNames() {
|
|
423
|
+
return Object.keys(this._options.agents);
|
|
424
|
+
}
|
|
425
|
+
/**
|
|
426
|
+
* Gets the number of registered agents.
|
|
427
|
+
*
|
|
428
|
+
* @returns Agent count
|
|
429
|
+
*/
|
|
430
|
+
get size() {
|
|
431
|
+
return Object.keys(this._options.agents).length;
|
|
432
|
+
}
|
|
433
|
+
/* ---------------------------------------------------------------------------
|
|
434
|
+
* STATUS
|
|
435
|
+
* --------------------------------------------------------------------------- */
|
|
436
|
+
/**
|
|
437
|
+
* Gets information about a specific agent.
|
|
438
|
+
*
|
|
439
|
+
* @param name - The agent name
|
|
440
|
+
* @returns Agent information or undefined
|
|
441
|
+
*/
|
|
442
|
+
getInfo(name) {
|
|
443
|
+
return this._statuses.get(name);
|
|
444
|
+
}
|
|
445
|
+
/**
|
|
446
|
+
* Gets status information for all agents.
|
|
447
|
+
*
|
|
448
|
+
* @returns Array of agent information objects
|
|
449
|
+
*
|
|
450
|
+
* @example
|
|
451
|
+
* ```typescript
|
|
452
|
+
* const status = manager.getStatus();
|
|
453
|
+
*
|
|
454
|
+
* for (const info of status) {
|
|
455
|
+
* console.log(`${info.name}: ${info.status}`);
|
|
456
|
+
* }
|
|
457
|
+
* ```
|
|
458
|
+
*/
|
|
459
|
+
getStatus() {
|
|
460
|
+
return Array.from(this._statuses.values());
|
|
461
|
+
}
|
|
462
|
+
/**
|
|
463
|
+
* Checks if all agents are running.
|
|
464
|
+
*
|
|
465
|
+
* @returns True if all agents are in 'running' status
|
|
466
|
+
*/
|
|
467
|
+
isAllRunning() {
|
|
468
|
+
for (const info of this._statuses.values()) {
|
|
469
|
+
if (info.status !== "running") {
|
|
470
|
+
return false;
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
return Object.keys(this._options.agents).length > 0;
|
|
474
|
+
}
|
|
475
|
+
/**
|
|
476
|
+
* Gets agents that are in error state.
|
|
477
|
+
*
|
|
478
|
+
* @returns Array of agent info for failed agents
|
|
479
|
+
*/
|
|
480
|
+
getFailedAgents() {
|
|
481
|
+
return Array.from(this._statuses.values()).filter(
|
|
482
|
+
(info) => info.status === "error"
|
|
483
|
+
);
|
|
484
|
+
}
|
|
485
|
+
};
|
|
486
|
+
var BaseAgentAttributesSchema = zod.z.object({
|
|
487
|
+
/**
|
|
488
|
+
* The agent name.
|
|
489
|
+
*/
|
|
490
|
+
"ctx.agent.name": zod.z.string(),
|
|
491
|
+
/**
|
|
492
|
+
* The current scope (if scoped).
|
|
493
|
+
*/
|
|
494
|
+
"ctx.agent.scope": zod.z.string().optional(),
|
|
495
|
+
/**
|
|
496
|
+
* The scope identifier value.
|
|
497
|
+
*/
|
|
498
|
+
"ctx.agent.scopeId": zod.z.string().optional()
|
|
499
|
+
});
|
|
500
|
+
var LifecycleAttributesSchema = BaseAgentAttributesSchema.extend({
|
|
501
|
+
/**
|
|
502
|
+
* Number of toolsets registered.
|
|
503
|
+
*/
|
|
504
|
+
"ctx.lifecycle.toolsetCount": zod.z.number().optional(),
|
|
505
|
+
/**
|
|
506
|
+
* Number of MCP connections configured.
|
|
507
|
+
*/
|
|
508
|
+
"ctx.lifecycle.mcpCount": zod.z.number().optional(),
|
|
509
|
+
/**
|
|
510
|
+
* Whether memory adapter is configured.
|
|
511
|
+
*/
|
|
512
|
+
"ctx.lifecycle.hasMemory": zod.z.boolean().optional()
|
|
513
|
+
});
|
|
514
|
+
var GenerationAttributesSchema = BaseAgentAttributesSchema.extend({
|
|
515
|
+
/**
|
|
516
|
+
* The model used for generation.
|
|
517
|
+
*/
|
|
518
|
+
"ctx.generation.model": zod.z.string().optional(),
|
|
519
|
+
/**
|
|
520
|
+
* Number of input messages.
|
|
521
|
+
*/
|
|
522
|
+
"ctx.generation.inputMessages": zod.z.number().optional(),
|
|
523
|
+
/**
|
|
524
|
+
* Number of input tokens (if available).
|
|
525
|
+
*/
|
|
526
|
+
"ctx.generation.inputTokens": zod.z.number().optional(),
|
|
527
|
+
/**
|
|
528
|
+
* Number of output tokens (if available).
|
|
529
|
+
*/
|
|
530
|
+
"ctx.generation.outputTokens": zod.z.number().optional(),
|
|
531
|
+
/**
|
|
532
|
+
* Total tokens used (if available).
|
|
533
|
+
*/
|
|
534
|
+
"ctx.generation.totalTokens": zod.z.number().optional(),
|
|
535
|
+
/**
|
|
536
|
+
* Generation duration in milliseconds.
|
|
537
|
+
*/
|
|
538
|
+
"ctx.generation.durationMs": zod.z.number().optional(),
|
|
539
|
+
/**
|
|
540
|
+
* Number of tool calls made during generation.
|
|
541
|
+
*/
|
|
542
|
+
"ctx.generation.toolCalls": zod.z.number().optional(),
|
|
543
|
+
/**
|
|
544
|
+
* Whether the response was streamed.
|
|
545
|
+
*/
|
|
546
|
+
"ctx.generation.streamed": zod.z.boolean().optional()
|
|
547
|
+
});
|
|
548
|
+
var ToolAttributesSchema = BaseAgentAttributesSchema.extend({
|
|
549
|
+
/**
|
|
550
|
+
* The toolset name.
|
|
551
|
+
*/
|
|
552
|
+
"ctx.tool.toolset": zod.z.string(),
|
|
553
|
+
/**
|
|
554
|
+
* The tool name (within the toolset).
|
|
555
|
+
*/
|
|
556
|
+
"ctx.tool.name": zod.z.string(),
|
|
557
|
+
/**
|
|
558
|
+
* The full tool identifier (toolset_name).
|
|
559
|
+
*/
|
|
560
|
+
"ctx.tool.fullName": zod.z.string(),
|
|
561
|
+
/**
|
|
562
|
+
* Tool execution duration in milliseconds.
|
|
563
|
+
*/
|
|
564
|
+
"ctx.tool.durationMs": zod.z.number().optional()
|
|
565
|
+
});
|
|
566
|
+
var MCPAttributesSchema = BaseAgentAttributesSchema.extend({
|
|
567
|
+
/**
|
|
568
|
+
* The MCP configuration name.
|
|
569
|
+
*/
|
|
570
|
+
"ctx.mcp.name": zod.z.string(),
|
|
571
|
+
/**
|
|
572
|
+
* The MCP transport type (stdio, http).
|
|
573
|
+
*/
|
|
574
|
+
"ctx.mcp.type": zod.z.enum(["stdio", "http"]),
|
|
575
|
+
/**
|
|
576
|
+
* Number of tools provided by this MCP.
|
|
577
|
+
*/
|
|
578
|
+
"ctx.mcp.toolCount": zod.z.number().optional(),
|
|
579
|
+
/**
|
|
580
|
+
* Connection duration in milliseconds.
|
|
581
|
+
*/
|
|
582
|
+
"ctx.mcp.durationMs": zod.z.number().optional()
|
|
583
|
+
});
|
|
584
|
+
var MemoryAttributesSchema = BaseAgentAttributesSchema.extend({
|
|
585
|
+
/**
|
|
586
|
+
* The memory operation type.
|
|
587
|
+
*/
|
|
588
|
+
"ctx.memory.operation": zod.z.enum([
|
|
589
|
+
"getWorkingMemory",
|
|
590
|
+
"updateWorkingMemory",
|
|
591
|
+
"getMessages",
|
|
592
|
+
"saveMessage",
|
|
593
|
+
"getChats",
|
|
594
|
+
"saveChat",
|
|
595
|
+
"getChat",
|
|
596
|
+
"updateChatTitle",
|
|
597
|
+
"deleteChat"
|
|
598
|
+
]),
|
|
599
|
+
/**
|
|
600
|
+
* The memory scope (user, chat, global).
|
|
601
|
+
*/
|
|
602
|
+
"ctx.memory.scope": zod.z.string().optional(),
|
|
603
|
+
/**
|
|
604
|
+
* Number of items affected.
|
|
605
|
+
*/
|
|
606
|
+
"ctx.memory.count": zod.z.number().optional(),
|
|
607
|
+
/**
|
|
608
|
+
* Operation duration in milliseconds.
|
|
609
|
+
*/
|
|
610
|
+
"ctx.memory.durationMs": zod.z.number().optional()
|
|
611
|
+
});
|
|
612
|
+
var ErrorAttributesSchema = BaseAgentAttributesSchema.extend({
|
|
613
|
+
/**
|
|
614
|
+
* The error code.
|
|
615
|
+
*/
|
|
616
|
+
"ctx.error.code": zod.z.string(),
|
|
617
|
+
/**
|
|
618
|
+
* The sanitized error message (no sensitive data).
|
|
619
|
+
*/
|
|
620
|
+
"ctx.error.message": zod.z.string().optional(),
|
|
621
|
+
/**
|
|
622
|
+
* The operation that failed.
|
|
623
|
+
*/
|
|
624
|
+
"ctx.error.operation": zod.z.string().optional(),
|
|
625
|
+
/**
|
|
626
|
+
* The component that threw the error.
|
|
627
|
+
*/
|
|
628
|
+
"ctx.error.component": zod.z.string().optional()
|
|
629
|
+
});
|
|
630
|
+
var IgniterAgentTelemetryEvents = telemetry.IgniterTelemetryEvents.namespace(
|
|
631
|
+
"igniter.agent"
|
|
632
|
+
).event("lifecycle.start.started", LifecycleAttributesSchema).event("lifecycle.start.success", LifecycleAttributesSchema).event("lifecycle.start.error", ErrorAttributesSchema).event("lifecycle.stop.started", LifecycleAttributesSchema).event("lifecycle.stop.success", LifecycleAttributesSchema).event("lifecycle.stop.error", ErrorAttributesSchema).event("generation.generate.started", GenerationAttributesSchema).event("generation.generate.success", GenerationAttributesSchema).event("generation.generate.error", ErrorAttributesSchema).event("generation.stream.started", GenerationAttributesSchema).event("generation.stream.chunk", GenerationAttributesSchema).event("generation.stream.success", GenerationAttributesSchema).event("generation.stream.error", ErrorAttributesSchema).event("tool.execute.started", ToolAttributesSchema).event("tool.execute.success", ToolAttributesSchema).event("tool.execute.error", ErrorAttributesSchema).event("mcp.connect.started", MCPAttributesSchema).event("mcp.connect.success", MCPAttributesSchema).event("mcp.connect.error", ErrorAttributesSchema).event("mcp.disconnect.started", MCPAttributesSchema).event("mcp.disconnect.success", MCPAttributesSchema).event("mcp.disconnect.error", ErrorAttributesSchema).event("memory.operation.started", MemoryAttributesSchema).event("memory.operation.success", MemoryAttributesSchema).event("memory.operation.error", ErrorAttributesSchema).build();
|
|
633
|
+
|
|
634
|
+
// src/core/memory.ts
|
|
635
|
+
var MEMORY_ERROR_CODE = "IGNITER_AGENT_MEMORY_PROVIDER_ERROR" /* MEMORY_PROVIDER_ERROR */;
|
|
636
|
+
var IgniterAgentMemoryCore = class {
|
|
637
|
+
constructor(config, agentName, logger, telemetry) {
|
|
638
|
+
this.provider = config.provider;
|
|
639
|
+
this.agentName = agentName;
|
|
640
|
+
this.logger = logger;
|
|
641
|
+
this.telemetry = telemetry;
|
|
642
|
+
}
|
|
643
|
+
getBaseAttributes(operation, scope) {
|
|
644
|
+
const attributes = {
|
|
645
|
+
"ctx.agent.name": this.agentName,
|
|
646
|
+
"ctx.memory.operation": operation
|
|
647
|
+
};
|
|
648
|
+
if (scope) {
|
|
649
|
+
attributes["ctx.memory.scope"] = scope;
|
|
650
|
+
}
|
|
651
|
+
return attributes;
|
|
652
|
+
}
|
|
653
|
+
emitStart(operation, scope) {
|
|
654
|
+
this.telemetry?.emit(IgniterAgentTelemetryEvents.get.key("memory.operation.started"), {
|
|
655
|
+
level: "debug",
|
|
656
|
+
attributes: this.getBaseAttributes(operation, scope)
|
|
657
|
+
});
|
|
658
|
+
}
|
|
659
|
+
emitSuccess(operation, durationMs, scope, count) {
|
|
660
|
+
const attributes = {
|
|
661
|
+
...this.getBaseAttributes(operation, scope),
|
|
662
|
+
"ctx.memory.durationMs": durationMs
|
|
663
|
+
};
|
|
664
|
+
if (count !== void 0) {
|
|
665
|
+
attributes["ctx.memory.count"] = count;
|
|
666
|
+
}
|
|
667
|
+
this.telemetry?.emit(IgniterAgentTelemetryEvents.get.key("memory.operation.success"), {
|
|
668
|
+
level: "debug",
|
|
669
|
+
attributes
|
|
670
|
+
});
|
|
671
|
+
}
|
|
672
|
+
emitError(operation, error, scope) {
|
|
673
|
+
const attributes = {
|
|
674
|
+
...this.getBaseAttributes(operation, scope),
|
|
675
|
+
"ctx.error.code": MEMORY_ERROR_CODE,
|
|
676
|
+
"ctx.error.message": error.message,
|
|
677
|
+
"ctx.error.operation": operation,
|
|
678
|
+
"ctx.error.component": "memory"
|
|
679
|
+
};
|
|
680
|
+
this.telemetry?.emit(IgniterAgentTelemetryEvents.get.key("memory.operation.error"), {
|
|
681
|
+
level: "error",
|
|
682
|
+
attributes
|
|
683
|
+
});
|
|
684
|
+
}
|
|
685
|
+
async runOperation(operation, scope, handler, count) {
|
|
686
|
+
const start = Date.now();
|
|
687
|
+
this.emitStart(operation, scope);
|
|
688
|
+
this.logger?.debug(`IgniterAgent.memory.${operation} started`, {
|
|
689
|
+
agent: this.agentName,
|
|
690
|
+
scope
|
|
691
|
+
});
|
|
692
|
+
try {
|
|
693
|
+
const result = await handler();
|
|
694
|
+
const durationMs = Date.now() - start;
|
|
695
|
+
this.emitSuccess(operation, durationMs, scope, count?.(result));
|
|
696
|
+
this.logger?.success?.(
|
|
697
|
+
`IgniterAgent.memory.${operation} success`,
|
|
698
|
+
{ agent: this.agentName, scope, durationMs }
|
|
699
|
+
);
|
|
700
|
+
return result;
|
|
701
|
+
} catch (err) {
|
|
702
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
703
|
+
this.emitError(operation, error, scope);
|
|
704
|
+
this.logger?.error(`IgniterAgent.memory.${operation} failed`, error);
|
|
705
|
+
throw new IgniterAgentMemoryError({
|
|
706
|
+
message: error.message,
|
|
707
|
+
code: MEMORY_ERROR_CODE,
|
|
708
|
+
cause: error,
|
|
709
|
+
metadata: { operation }
|
|
710
|
+
});
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
async getWorkingMemory(params) {
|
|
714
|
+
return this.runOperation(
|
|
715
|
+
"getWorkingMemory",
|
|
716
|
+
params.scope,
|
|
717
|
+
() => this.provider.getWorkingMemory(params)
|
|
718
|
+
);
|
|
719
|
+
}
|
|
720
|
+
async updateWorkingMemory(params) {
|
|
721
|
+
await this.runOperation(
|
|
722
|
+
"updateWorkingMemory",
|
|
723
|
+
params.scope,
|
|
724
|
+
() => this.provider.updateWorkingMemory(params)
|
|
725
|
+
);
|
|
726
|
+
}
|
|
727
|
+
async saveMessage(message) {
|
|
728
|
+
await this.runOperation(
|
|
729
|
+
"saveMessage",
|
|
730
|
+
void 0,
|
|
731
|
+
async () => {
|
|
732
|
+
if (!this.provider.saveMessage) {
|
|
733
|
+
throw new Error("saveMessage is not supported by the provider");
|
|
734
|
+
}
|
|
735
|
+
await this.provider.saveMessage(message);
|
|
736
|
+
}
|
|
737
|
+
);
|
|
738
|
+
}
|
|
739
|
+
async getMessages(params) {
|
|
740
|
+
return this.runOperation(
|
|
741
|
+
"getMessages",
|
|
742
|
+
void 0,
|
|
743
|
+
async () => {
|
|
744
|
+
if (!this.provider.getMessages) {
|
|
745
|
+
throw new Error("getMessages is not supported by the provider");
|
|
746
|
+
}
|
|
747
|
+
return this.provider.getMessages(params);
|
|
748
|
+
},
|
|
749
|
+
(result) => result.length
|
|
750
|
+
);
|
|
751
|
+
}
|
|
752
|
+
async saveChat(chat) {
|
|
753
|
+
await this.runOperation(
|
|
754
|
+
"saveChat",
|
|
755
|
+
void 0,
|
|
756
|
+
async () => {
|
|
757
|
+
if (!this.provider.saveChat) {
|
|
758
|
+
throw new Error("saveChat is not supported by the provider");
|
|
759
|
+
}
|
|
760
|
+
await this.provider.saveChat(chat);
|
|
761
|
+
}
|
|
762
|
+
);
|
|
763
|
+
}
|
|
764
|
+
async getChats(params) {
|
|
765
|
+
return this.runOperation(
|
|
766
|
+
"getChats",
|
|
767
|
+
void 0,
|
|
768
|
+
async () => {
|
|
769
|
+
if (!this.provider.getChats) {
|
|
770
|
+
throw new Error("getChats is not supported by the provider");
|
|
771
|
+
}
|
|
772
|
+
return this.provider.getChats(params);
|
|
773
|
+
},
|
|
774
|
+
(result) => result.length
|
|
775
|
+
);
|
|
776
|
+
}
|
|
777
|
+
async getChat(chatId) {
|
|
778
|
+
return this.runOperation(
|
|
779
|
+
"getChat",
|
|
780
|
+
void 0,
|
|
781
|
+
async () => {
|
|
782
|
+
if (!this.provider.getChat) {
|
|
783
|
+
throw new Error("getChat is not supported by the provider");
|
|
784
|
+
}
|
|
785
|
+
return this.provider.getChat(chatId);
|
|
786
|
+
}
|
|
787
|
+
);
|
|
788
|
+
}
|
|
789
|
+
async updateChatTitle(chatId, title) {
|
|
790
|
+
await this.runOperation(
|
|
791
|
+
"updateChatTitle",
|
|
792
|
+
void 0,
|
|
793
|
+
async () => {
|
|
794
|
+
if (!this.provider.updateChatTitle) {
|
|
795
|
+
throw new Error("updateChatTitle is not supported by the provider");
|
|
796
|
+
}
|
|
797
|
+
await this.provider.updateChatTitle(chatId, title);
|
|
798
|
+
}
|
|
799
|
+
);
|
|
800
|
+
}
|
|
801
|
+
async deleteChat(chatId) {
|
|
802
|
+
await this.runOperation(
|
|
803
|
+
"deleteChat",
|
|
804
|
+
void 0,
|
|
805
|
+
async () => {
|
|
806
|
+
if (!this.provider.deleteChat) {
|
|
807
|
+
throw new Error("deleteChat is not supported by the provider");
|
|
808
|
+
}
|
|
809
|
+
await this.provider.deleteChat(chatId);
|
|
810
|
+
}
|
|
811
|
+
);
|
|
812
|
+
}
|
|
813
|
+
};
|
|
814
|
+
|
|
815
|
+
// src/core/agent.ts
|
|
816
|
+
var IgniterAgentCore = class {
|
|
817
|
+
constructor(agent) {
|
|
818
|
+
this._agent = agent;
|
|
819
|
+
this.logger = agent.logger;
|
|
820
|
+
this.telemetry = agent.telemetry;
|
|
821
|
+
this.hooks = agent.hooks ?? {};
|
|
822
|
+
if (agent.memory) {
|
|
823
|
+
this.memory = new IgniterAgentMemoryCore(agent.memory, String(agent.name), this.logger, this.telemetry);
|
|
824
|
+
}
|
|
825
|
+
}
|
|
826
|
+
/**
|
|
827
|
+
* Attaches a logger instance to the agent.
|
|
828
|
+
*/
|
|
829
|
+
attachLogger(logger) {
|
|
830
|
+
if (!logger) return;
|
|
831
|
+
if (!this.logger) {
|
|
832
|
+
this.logger = logger;
|
|
833
|
+
if (this._agent.memory) {
|
|
834
|
+
this.memory = new IgniterAgentMemoryCore(
|
|
835
|
+
this._agent.memory,
|
|
836
|
+
this.getName(),
|
|
837
|
+
this.logger,
|
|
838
|
+
this.telemetry
|
|
839
|
+
);
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
/**
|
|
844
|
+
* Attaches a telemetry manager to the agent.
|
|
845
|
+
*/
|
|
846
|
+
attachTelemetry(telemetry) {
|
|
847
|
+
if (!telemetry) return;
|
|
848
|
+
if (!this.telemetry) {
|
|
849
|
+
this.telemetry = telemetry;
|
|
850
|
+
if (this._agent.memory) {
|
|
851
|
+
this.memory = new IgniterAgentMemoryCore(
|
|
852
|
+
this._agent.memory,
|
|
853
|
+
this.getName(),
|
|
854
|
+
this.logger,
|
|
855
|
+
this.telemetry
|
|
856
|
+
);
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
/**
|
|
861
|
+
* Attaches hook callbacks to the agent.
|
|
862
|
+
*/
|
|
863
|
+
attachHooks(hooks) {
|
|
864
|
+
if (!hooks) return;
|
|
865
|
+
const merge = (current, incoming) => {
|
|
866
|
+
if (!current) return incoming;
|
|
867
|
+
if (!incoming) return current;
|
|
868
|
+
return ((...args) => {
|
|
869
|
+
current(...args);
|
|
870
|
+
incoming(...args);
|
|
871
|
+
});
|
|
872
|
+
};
|
|
873
|
+
this.hooks = {
|
|
874
|
+
onAgentStart: merge(this.hooks.onAgentStart, hooks.onAgentStart),
|
|
875
|
+
onAgentError: merge(this.hooks.onAgentError, hooks.onAgentError),
|
|
876
|
+
onToolCallStart: merge(this.hooks.onToolCallStart, hooks.onToolCallStart),
|
|
877
|
+
onToolCallEnd: merge(this.hooks.onToolCallEnd, hooks.onToolCallEnd),
|
|
878
|
+
onToolCallError: merge(this.hooks.onToolCallError, hooks.onToolCallError),
|
|
879
|
+
onMCPStart: merge(this.hooks.onMCPStart, hooks.onMCPStart),
|
|
880
|
+
onMCPError: merge(this.hooks.onMCPError, hooks.onMCPError)
|
|
881
|
+
};
|
|
882
|
+
}
|
|
883
|
+
/**
|
|
884
|
+
* Returns the agent name.
|
|
885
|
+
*/
|
|
886
|
+
getName() {
|
|
887
|
+
return String(this._agent.name);
|
|
888
|
+
}
|
|
889
|
+
/**
|
|
890
|
+
* Starts the agent by initializing all MCP connections.
|
|
891
|
+
*/
|
|
892
|
+
async start() {
|
|
893
|
+
const startTime = Date.now();
|
|
894
|
+
const toolsets = this._agent.toolsets;
|
|
895
|
+
const mcpConfigs = Object.values(this._agent.configs || {});
|
|
896
|
+
const lifecycleAttributes = {
|
|
897
|
+
"ctx.agent.name": this.getName(),
|
|
898
|
+
"ctx.lifecycle.toolsetCount": Object.keys(toolsets || {}).length,
|
|
899
|
+
"ctx.lifecycle.mcpCount": mcpConfigs.length,
|
|
900
|
+
"ctx.lifecycle.hasMemory": Boolean(this.memory)
|
|
901
|
+
};
|
|
902
|
+
this.logger?.debug("IgniterAgent.start started", lifecycleAttributes);
|
|
903
|
+
this.telemetry?.emit(IgniterAgentTelemetryEvents.get.key("lifecycle.start.started"), {
|
|
904
|
+
level: "debug",
|
|
905
|
+
attributes: lifecycleAttributes
|
|
906
|
+
});
|
|
907
|
+
try {
|
|
908
|
+
for (const mcpConfig of mcpConfigs) {
|
|
909
|
+
const mcpStart = Date.now();
|
|
910
|
+
this.hooks.onMCPStart?.(this.getName(), mcpConfig.name);
|
|
911
|
+
this.telemetry?.emit(IgniterAgentTelemetryEvents.get.key("mcp.connect.started"), {
|
|
912
|
+
level: "debug",
|
|
913
|
+
attributes: {
|
|
914
|
+
"ctx.agent.name": this.getName(),
|
|
915
|
+
"ctx.mcp.name": mcpConfig.name,
|
|
916
|
+
"ctx.mcp.type": mcpConfig.type
|
|
917
|
+
}
|
|
918
|
+
});
|
|
919
|
+
try {
|
|
920
|
+
const mcpToolset = await this.initializeMCPClient(mcpConfig);
|
|
921
|
+
toolsets[mcpConfig.name] = mcpToolset;
|
|
922
|
+
this.telemetry?.emit(IgniterAgentTelemetryEvents.get.key("mcp.connect.success"), {
|
|
923
|
+
level: "debug",
|
|
924
|
+
attributes: {
|
|
925
|
+
"ctx.agent.name": this.getName(),
|
|
926
|
+
"ctx.mcp.name": mcpConfig.name,
|
|
927
|
+
"ctx.mcp.type": mcpConfig.type,
|
|
928
|
+
"ctx.mcp.toolCount": Object.keys(mcpToolset.tools || {}).length,
|
|
929
|
+
"ctx.mcp.durationMs": Date.now() - mcpStart
|
|
930
|
+
}
|
|
931
|
+
});
|
|
932
|
+
} catch (error) {
|
|
933
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
934
|
+
this.telemetry?.emit(IgniterAgentTelemetryEvents.get.key("mcp.connect.error"), {
|
|
935
|
+
level: "error",
|
|
936
|
+
attributes: {
|
|
937
|
+
"ctx.agent.name": this.getName(),
|
|
938
|
+
"ctx.mcp.name": mcpConfig.name,
|
|
939
|
+
"ctx.mcp.type": mcpConfig.type,
|
|
940
|
+
"ctx.mcp.durationMs": Date.now() - mcpStart,
|
|
941
|
+
...this.getErrorAttributes(err, "mcp.connect")
|
|
942
|
+
}
|
|
943
|
+
});
|
|
944
|
+
this.logger?.error("IgniterAgent.mcp.connect failed", err);
|
|
945
|
+
throw err;
|
|
946
|
+
}
|
|
947
|
+
}
|
|
948
|
+
const durationMs = Date.now() - startTime;
|
|
949
|
+
this.telemetry?.emit(IgniterAgentTelemetryEvents.get.key("lifecycle.start.success"), {
|
|
950
|
+
level: "debug",
|
|
951
|
+
attributes: {
|
|
952
|
+
...lifecycleAttributes,
|
|
953
|
+
"ctx.lifecycle.toolsetCount": Object.keys(this._agent.toolsets || {}).length,
|
|
954
|
+
"ctx.lifecycle.mcpCount": mcpConfigs.length
|
|
955
|
+
}
|
|
956
|
+
});
|
|
957
|
+
this.logger?.success?.("IgniterAgent.start success", {
|
|
958
|
+
...lifecycleAttributes,
|
|
959
|
+
durationMs
|
|
960
|
+
});
|
|
961
|
+
this.hooks.onAgentStart?.(this.getName());
|
|
962
|
+
} catch (error) {
|
|
963
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
964
|
+
this.telemetry?.emit(IgniterAgentTelemetryEvents.get.key("lifecycle.start.error"), {
|
|
965
|
+
level: "error",
|
|
966
|
+
attributes: {
|
|
967
|
+
...lifecycleAttributes,
|
|
968
|
+
...this.getErrorAttributes(err, "lifecycle.start")
|
|
969
|
+
}
|
|
970
|
+
});
|
|
971
|
+
this.logger?.error("IgniterAgent.start failed", err);
|
|
972
|
+
this.hooks.onAgentError?.(this.getName(), err);
|
|
973
|
+
throw err;
|
|
974
|
+
}
|
|
975
|
+
}
|
|
976
|
+
/**
|
|
977
|
+
* Stops the agent by disconnecting MCP toolsets.
|
|
978
|
+
*/
|
|
979
|
+
async stop() {
|
|
980
|
+
const startTime = Date.now();
|
|
981
|
+
const mcpConfigs = Object.values(this._agent.configs || {});
|
|
982
|
+
const lifecycleAttributes = {
|
|
983
|
+
"ctx.agent.name": this.getName(),
|
|
984
|
+
"ctx.lifecycle.toolsetCount": Object.keys(this._agent.toolsets || {}).length,
|
|
985
|
+
"ctx.lifecycle.mcpCount": mcpConfigs.length,
|
|
986
|
+
"ctx.lifecycle.hasMemory": Boolean(this.memory)
|
|
987
|
+
};
|
|
988
|
+
this.logger?.debug("IgniterAgent.stop started", lifecycleAttributes);
|
|
989
|
+
this.telemetry?.emit(IgniterAgentTelemetryEvents.get.key("lifecycle.stop.started"), {
|
|
990
|
+
level: "debug",
|
|
991
|
+
attributes: lifecycleAttributes
|
|
992
|
+
});
|
|
993
|
+
try {
|
|
994
|
+
for (const mcpConfig of mcpConfigs) {
|
|
995
|
+
const mcpStart = Date.now();
|
|
996
|
+
this.telemetry?.emit(IgniterAgentTelemetryEvents.get.key("mcp.disconnect.started"), {
|
|
997
|
+
level: "debug",
|
|
998
|
+
attributes: {
|
|
999
|
+
"ctx.agent.name": this.getName(),
|
|
1000
|
+
"ctx.mcp.name": mcpConfig.name,
|
|
1001
|
+
"ctx.mcp.type": mcpConfig.type
|
|
1002
|
+
}
|
|
1003
|
+
});
|
|
1004
|
+
try {
|
|
1005
|
+
const existing = this._agent.toolsets[mcpConfig.name];
|
|
1006
|
+
if (existing?.disconnect) {
|
|
1007
|
+
await existing.disconnect();
|
|
1008
|
+
}
|
|
1009
|
+
if (existing) {
|
|
1010
|
+
this._agent.toolsets[mcpConfig.name] = {
|
|
1011
|
+
...existing,
|
|
1012
|
+
status: "disconnected",
|
|
1013
|
+
tools: {}
|
|
1014
|
+
};
|
|
1015
|
+
}
|
|
1016
|
+
this.telemetry?.emit(IgniterAgentTelemetryEvents.get.key("mcp.disconnect.success"), {
|
|
1017
|
+
level: "debug",
|
|
1018
|
+
attributes: {
|
|
1019
|
+
"ctx.agent.name": this.getName(),
|
|
1020
|
+
"ctx.mcp.name": mcpConfig.name,
|
|
1021
|
+
"ctx.mcp.type": mcpConfig.type,
|
|
1022
|
+
"ctx.mcp.durationMs": Date.now() - mcpStart
|
|
1023
|
+
}
|
|
1024
|
+
});
|
|
1025
|
+
} catch (error) {
|
|
1026
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
1027
|
+
this.telemetry?.emit(IgniterAgentTelemetryEvents.get.key("mcp.disconnect.error"), {
|
|
1028
|
+
level: "error",
|
|
1029
|
+
attributes: {
|
|
1030
|
+
"ctx.agent.name": this.getName(),
|
|
1031
|
+
"ctx.mcp.name": mcpConfig.name,
|
|
1032
|
+
"ctx.mcp.type": mcpConfig.type,
|
|
1033
|
+
"ctx.mcp.durationMs": Date.now() - mcpStart,
|
|
1034
|
+
...this.getErrorAttributes(err, "mcp.disconnect")
|
|
1035
|
+
}
|
|
1036
|
+
});
|
|
1037
|
+
this.logger?.error("IgniterAgent.mcp.disconnect failed", err);
|
|
1038
|
+
throw err;
|
|
1039
|
+
}
|
|
1040
|
+
}
|
|
1041
|
+
const durationMs = Date.now() - startTime;
|
|
1042
|
+
this.telemetry?.emit(IgniterAgentTelemetryEvents.get.key("lifecycle.stop.success"), {
|
|
1043
|
+
level: "debug",
|
|
1044
|
+
attributes: {
|
|
1045
|
+
...lifecycleAttributes,
|
|
1046
|
+
"ctx.lifecycle.toolsetCount": Object.keys(this._agent.toolsets || {}).length,
|
|
1047
|
+
"ctx.lifecycle.mcpCount": mcpConfigs.length
|
|
1048
|
+
}
|
|
1049
|
+
});
|
|
1050
|
+
this.logger?.success?.("IgniterAgent.stop success", {
|
|
1051
|
+
...lifecycleAttributes,
|
|
1052
|
+
durationMs
|
|
1053
|
+
});
|
|
1054
|
+
} catch (error) {
|
|
1055
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
1056
|
+
this.telemetry?.emit(IgniterAgentTelemetryEvents.get.key("lifecycle.stop.error"), {
|
|
1057
|
+
level: "error",
|
|
1058
|
+
attributes: {
|
|
1059
|
+
...lifecycleAttributes,
|
|
1060
|
+
...this.getErrorAttributes(err, "lifecycle.stop")
|
|
1061
|
+
}
|
|
1062
|
+
});
|
|
1063
|
+
this.logger?.error("IgniterAgent.stop failed", err);
|
|
1064
|
+
throw err;
|
|
1065
|
+
}
|
|
1066
|
+
}
|
|
1067
|
+
/**
|
|
1068
|
+
* Generates a response from the agent.
|
|
1069
|
+
*/
|
|
1070
|
+
async generate(input) {
|
|
1071
|
+
const startTime = Date.now();
|
|
1072
|
+
const attributes = {
|
|
1073
|
+
"ctx.agent.name": this.getName(),
|
|
1074
|
+
"ctx.generation.inputMessages": Array.isArray(input.messages) ? input.messages.length : void 0,
|
|
1075
|
+
"ctx.generation.streamed": false
|
|
1076
|
+
};
|
|
1077
|
+
this.logger?.debug("IgniterAgent.generate started", attributes);
|
|
1078
|
+
this.telemetry?.emit(IgniterAgentTelemetryEvents.get.key("generation.generate.started"), {
|
|
1079
|
+
level: "debug",
|
|
1080
|
+
attributes
|
|
1081
|
+
});
|
|
1082
|
+
try {
|
|
1083
|
+
const agent = this.getAgentInstanceWithContext(
|
|
1084
|
+
input.options
|
|
1085
|
+
);
|
|
1086
|
+
const result = await agent.generate(input);
|
|
1087
|
+
const durationMs = Date.now() - startTime;
|
|
1088
|
+
this.telemetry?.emit(IgniterAgentTelemetryEvents.get.key("generation.generate.success"), {
|
|
1089
|
+
level: "debug",
|
|
1090
|
+
attributes: {
|
|
1091
|
+
...attributes,
|
|
1092
|
+
"ctx.generation.durationMs": durationMs
|
|
1093
|
+
}
|
|
1094
|
+
});
|
|
1095
|
+
this.logger?.success?.("IgniterAgent.generate success", {
|
|
1096
|
+
...attributes,
|
|
1097
|
+
durationMs
|
|
1098
|
+
});
|
|
1099
|
+
return result;
|
|
1100
|
+
} catch (error) {
|
|
1101
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
1102
|
+
this.telemetry?.emit(IgniterAgentTelemetryEvents.get.key("generation.generate.error"), {
|
|
1103
|
+
level: "error",
|
|
1104
|
+
attributes: {
|
|
1105
|
+
...attributes,
|
|
1106
|
+
...this.getErrorAttributes(err, "generation.generate")
|
|
1107
|
+
}
|
|
1108
|
+
});
|
|
1109
|
+
this.logger?.error("IgniterAgent.generate failed", err);
|
|
1110
|
+
throw err;
|
|
1111
|
+
}
|
|
1112
|
+
}
|
|
1113
|
+
/**
|
|
1114
|
+
* Streams a response from the agent.
|
|
1115
|
+
*/
|
|
1116
|
+
async stream(input) {
|
|
1117
|
+
const startTime = Date.now();
|
|
1118
|
+
const attributes = {
|
|
1119
|
+
"ctx.agent.name": this.getName(),
|
|
1120
|
+
"ctx.generation.inputMessages": Array.isArray(input.messages) ? input.messages.length : void 0,
|
|
1121
|
+
"ctx.generation.streamed": true
|
|
1122
|
+
};
|
|
1123
|
+
this.logger?.debug("IgniterAgent.stream started", attributes);
|
|
1124
|
+
this.telemetry?.emit(IgniterAgentTelemetryEvents.get.key("generation.stream.started"), {
|
|
1125
|
+
level: "debug",
|
|
1126
|
+
attributes
|
|
1127
|
+
});
|
|
1128
|
+
try {
|
|
1129
|
+
const agent = this.getAgentInstanceWithContext(
|
|
1130
|
+
input.options
|
|
1131
|
+
);
|
|
1132
|
+
const result = await agent.stream(input);
|
|
1133
|
+
const durationMs = Date.now() - startTime;
|
|
1134
|
+
const emitChunk = () => {
|
|
1135
|
+
this.telemetry?.emit(IgniterAgentTelemetryEvents.get.key("generation.stream.chunk"), {
|
|
1136
|
+
level: "debug",
|
|
1137
|
+
attributes
|
|
1138
|
+
});
|
|
1139
|
+
};
|
|
1140
|
+
const wrapped = this.wrapStreamResult(result, emitChunk);
|
|
1141
|
+
this.telemetry?.emit(IgniterAgentTelemetryEvents.get.key("generation.stream.success"), {
|
|
1142
|
+
level: "debug",
|
|
1143
|
+
attributes: {
|
|
1144
|
+
...attributes,
|
|
1145
|
+
"ctx.generation.durationMs": durationMs
|
|
1146
|
+
}
|
|
1147
|
+
});
|
|
1148
|
+
this.logger?.success?.("IgniterAgent.stream success", {
|
|
1149
|
+
...attributes,
|
|
1150
|
+
durationMs
|
|
1151
|
+
});
|
|
1152
|
+
return wrapped;
|
|
1153
|
+
} catch (error) {
|
|
1154
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
1155
|
+
this.telemetry?.emit(IgniterAgentTelemetryEvents.get.key("generation.stream.error"), {
|
|
1156
|
+
level: "error",
|
|
1157
|
+
attributes: {
|
|
1158
|
+
...attributes,
|
|
1159
|
+
...this.getErrorAttributes(err, "generation.stream")
|
|
1160
|
+
}
|
|
1161
|
+
});
|
|
1162
|
+
this.logger?.error("IgniterAgent.stream failed", err);
|
|
1163
|
+
throw err;
|
|
1164
|
+
}
|
|
1165
|
+
}
|
|
1166
|
+
/**
|
|
1167
|
+
* Gets all registered toolsets.
|
|
1168
|
+
*/
|
|
1169
|
+
getToolsets() {
|
|
1170
|
+
return this._agent.toolsets;
|
|
1171
|
+
}
|
|
1172
|
+
/**
|
|
1173
|
+
* Gets the configured model.
|
|
1174
|
+
*/
|
|
1175
|
+
getModel() {
|
|
1176
|
+
return this._agent.model;
|
|
1177
|
+
}
|
|
1178
|
+
/**
|
|
1179
|
+
* Gets the configured instructions.
|
|
1180
|
+
*/
|
|
1181
|
+
getInstructions() {
|
|
1182
|
+
return this._agent.instructions;
|
|
1183
|
+
}
|
|
1184
|
+
/**
|
|
1185
|
+
* Gets the context schema.
|
|
1186
|
+
*/
|
|
1187
|
+
getContextSchema() {
|
|
1188
|
+
return this._agent.schema;
|
|
1189
|
+
}
|
|
1190
|
+
/**
|
|
1191
|
+
* Gets all registered tools from all toolsets.
|
|
1192
|
+
*/
|
|
1193
|
+
getTools() {
|
|
1194
|
+
const toolsets = this.getToolsets();
|
|
1195
|
+
const allTools = {};
|
|
1196
|
+
for (const toolset of Object.values(toolsets)) {
|
|
1197
|
+
for (const [toolName, tool2] of Object.entries(toolset.tools)) {
|
|
1198
|
+
const wrapped = this.wrapToolExecution(toolset.name, toolName, tool2);
|
|
1199
|
+
allTools[toolName] = wrapped;
|
|
1200
|
+
}
|
|
1201
|
+
}
|
|
1202
|
+
return allTools;
|
|
1203
|
+
}
|
|
1204
|
+
wrapToolExecution(toolsetName, toolName, tool2) {
|
|
1205
|
+
if (!tool2 || !tool2.execute || typeof tool2.execute !== "function") {
|
|
1206
|
+
return tool2;
|
|
1207
|
+
}
|
|
1208
|
+
const execute = tool2.execute;
|
|
1209
|
+
const fullName = `${toolsetName}.${toolName}`;
|
|
1210
|
+
const agentName = this.getName();
|
|
1211
|
+
return {
|
|
1212
|
+
...tool2,
|
|
1213
|
+
execute: async (input, options) => {
|
|
1214
|
+
const startTime = Date.now();
|
|
1215
|
+
this.hooks.onToolCallStart?.(agentName, fullName, input);
|
|
1216
|
+
this.telemetry?.emit(IgniterAgentTelemetryEvents.get.key("tool.execute.started"), {
|
|
1217
|
+
level: "debug",
|
|
1218
|
+
attributes: {
|
|
1219
|
+
"ctx.agent.name": agentName,
|
|
1220
|
+
"ctx.tool.toolset": toolsetName,
|
|
1221
|
+
"ctx.tool.name": toolName,
|
|
1222
|
+
"ctx.tool.fullName": fullName
|
|
1223
|
+
}
|
|
1224
|
+
});
|
|
1225
|
+
this.logger?.debug("IgniterAgent.tool.execute started", {
|
|
1226
|
+
agent: agentName,
|
|
1227
|
+
tool: fullName
|
|
1228
|
+
});
|
|
1229
|
+
try {
|
|
1230
|
+
const result = await execute(input, options);
|
|
1231
|
+
const durationMs = Date.now() - startTime;
|
|
1232
|
+
this.hooks.onToolCallEnd?.(agentName, fullName, result);
|
|
1233
|
+
this.telemetry?.emit(IgniterAgentTelemetryEvents.get.key("tool.execute.success"), {
|
|
1234
|
+
level: "debug",
|
|
1235
|
+
attributes: {
|
|
1236
|
+
"ctx.agent.name": agentName,
|
|
1237
|
+
"ctx.tool.toolset": toolsetName,
|
|
1238
|
+
"ctx.tool.name": toolName,
|
|
1239
|
+
"ctx.tool.fullName": fullName,
|
|
1240
|
+
"ctx.tool.durationMs": durationMs
|
|
1241
|
+
}
|
|
1242
|
+
});
|
|
1243
|
+
this.logger?.success?.("IgniterAgent.tool.execute success", {
|
|
1244
|
+
agent: agentName,
|
|
1245
|
+
tool: fullName,
|
|
1246
|
+
durationMs
|
|
1247
|
+
});
|
|
1248
|
+
return result;
|
|
1249
|
+
} catch (error) {
|
|
1250
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
1251
|
+
this.hooks.onToolCallError?.(agentName, fullName, err);
|
|
1252
|
+
this.telemetry?.emit(IgniterAgentTelemetryEvents.get.key("tool.execute.error"), {
|
|
1253
|
+
level: "error",
|
|
1254
|
+
attributes: {
|
|
1255
|
+
"ctx.agent.name": agentName,
|
|
1256
|
+
"ctx.tool.toolset": toolsetName,
|
|
1257
|
+
"ctx.tool.name": toolName,
|
|
1258
|
+
"ctx.tool.fullName": fullName,
|
|
1259
|
+
...this.getErrorAttributes(err, "tool.execute")
|
|
1260
|
+
}
|
|
1261
|
+
});
|
|
1262
|
+
this.logger?.error("IgniterAgent.tool.execute failed", err);
|
|
1263
|
+
throw err;
|
|
1264
|
+
}
|
|
1265
|
+
}
|
|
1266
|
+
};
|
|
1267
|
+
}
|
|
1268
|
+
wrapStreamResult(result, onChunk) {
|
|
1269
|
+
if (!result) {
|
|
1270
|
+
return result;
|
|
1271
|
+
}
|
|
1272
|
+
if (typeof result[Symbol.asyncIterator] === "function") {
|
|
1273
|
+
const iterable = result;
|
|
1274
|
+
return {
|
|
1275
|
+
[Symbol.asyncIterator]: async function* () {
|
|
1276
|
+
for await (const chunk of iterable) {
|
|
1277
|
+
onChunk();
|
|
1278
|
+
yield chunk;
|
|
1279
|
+
}
|
|
1280
|
+
}
|
|
1281
|
+
};
|
|
1282
|
+
}
|
|
1283
|
+
const maybeTextStream = result.textStream;
|
|
1284
|
+
if (maybeTextStream && typeof maybeTextStream[Symbol.asyncIterator] === "function") {
|
|
1285
|
+
return {
|
|
1286
|
+
...result,
|
|
1287
|
+
textStream: (async function* () {
|
|
1288
|
+
for await (const chunk of maybeTextStream) {
|
|
1289
|
+
onChunk();
|
|
1290
|
+
yield chunk;
|
|
1291
|
+
}
|
|
1292
|
+
})()
|
|
1293
|
+
};
|
|
1294
|
+
}
|
|
1295
|
+
return result;
|
|
1296
|
+
}
|
|
1297
|
+
getErrorAttributes(error, operation) {
|
|
1298
|
+
return {
|
|
1299
|
+
"ctx.error.code": error.code ?? error.name ?? "IGNITER_AGENT_UNKNOWN_ERROR" /* UNKNOWN */,
|
|
1300
|
+
"ctx.error.message": error.message,
|
|
1301
|
+
"ctx.error.operation": operation,
|
|
1302
|
+
"ctx.error.component": "agent"
|
|
1303
|
+
};
|
|
1304
|
+
}
|
|
1305
|
+
getAgentInstanceWithContext(context) {
|
|
1306
|
+
const tools = this.getTools();
|
|
1307
|
+
if (!this._agent.model) {
|
|
1308
|
+
throw new IgniterAgentConfigError({
|
|
1309
|
+
message: "Model is required. Call withModel() before build()",
|
|
1310
|
+
field: "model"
|
|
1311
|
+
});
|
|
1312
|
+
}
|
|
1313
|
+
if (this._agent.schema !== void 0) {
|
|
1314
|
+
const parseResult = this._agent.schema.safeParse(context);
|
|
1315
|
+
if (parseResult.success) {
|
|
1316
|
+
context = parseResult.data;
|
|
1317
|
+
} else {
|
|
1318
|
+
throw new IgniterAgentError({
|
|
1319
|
+
message: "Invalid context schema",
|
|
1320
|
+
code: "IGNITER_AGENT_CONTEXT_SCHEMA_INVALID" /* AGENT_CONTEXT_SCHEMA_INVALID */
|
|
1321
|
+
});
|
|
1322
|
+
}
|
|
1323
|
+
}
|
|
1324
|
+
return new ai.ToolLoopAgent({
|
|
1325
|
+
model: this._agent.model,
|
|
1326
|
+
instructions: this._agent.instructions ? this._agent.instructions.build(context) : "",
|
|
1327
|
+
tools,
|
|
1328
|
+
callOptionsSchema: this._agent.schema
|
|
1329
|
+
});
|
|
1330
|
+
}
|
|
1331
|
+
async initializeMCPClient(mcpConfig) {
|
|
1332
|
+
if (this._agent.toolsets[mcpConfig.name]) {
|
|
1333
|
+
return this._agent.toolsets[mcpConfig.name];
|
|
1334
|
+
}
|
|
1335
|
+
let client = null;
|
|
1336
|
+
try {
|
|
1337
|
+
if (mcpConfig.type === "stdio") {
|
|
1338
|
+
const stdioConfig = mcpConfig;
|
|
1339
|
+
this.logger?.debug("IgniterAgent.mcp.connect stdio", {
|
|
1340
|
+
command: stdioConfig.command,
|
|
1341
|
+
args: stdioConfig.args
|
|
1342
|
+
});
|
|
1343
|
+
client = await mcp.experimental_createMCPClient({
|
|
1344
|
+
transport: new stdio_js.StdioClientTransport({
|
|
1345
|
+
command: stdioConfig.command,
|
|
1346
|
+
args: stdioConfig.args,
|
|
1347
|
+
env: stdioConfig.env
|
|
1348
|
+
})
|
|
1349
|
+
});
|
|
1350
|
+
}
|
|
1351
|
+
if (mcpConfig.type === "http") {
|
|
1352
|
+
const httpConfig = mcpConfig;
|
|
1353
|
+
this.logger?.debug("IgniterAgent.mcp.connect http", {
|
|
1354
|
+
url: httpConfig.url
|
|
1355
|
+
});
|
|
1356
|
+
const url = new URL(httpConfig.url);
|
|
1357
|
+
client = await mcp.experimental_createMCPClient({
|
|
1358
|
+
transport: new streamableHttp_js.StreamableHTTPClientTransport(url, {
|
|
1359
|
+
requestInit: {
|
|
1360
|
+
headers: httpConfig.headers
|
|
1361
|
+
}
|
|
1362
|
+
})
|
|
1363
|
+
});
|
|
1364
|
+
}
|
|
1365
|
+
if (!client) {
|
|
1366
|
+
throw new IgniterAgentMCPError({
|
|
1367
|
+
message: `Failed to create MCP client for '${mcpConfig.name}'`,
|
|
1368
|
+
code: "IGNITER_AGENT_MCP_CONNECTION_FAILED" /* MCP_CONNECTION_FAILED */,
|
|
1369
|
+
mcpName: mcpConfig.name
|
|
1370
|
+
});
|
|
1371
|
+
}
|
|
1372
|
+
const tools = await client.tools();
|
|
1373
|
+
return {
|
|
1374
|
+
type: mcpConfig.type,
|
|
1375
|
+
status: "connected",
|
|
1376
|
+
name: mcpConfig.name,
|
|
1377
|
+
tools
|
|
1378
|
+
};
|
|
1379
|
+
} catch (error) {
|
|
1380
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
1381
|
+
this.hooks.onMCPError?.(this.getName(), mcpConfig.name, err);
|
|
1382
|
+
throw err;
|
|
1383
|
+
}
|
|
1384
|
+
}
|
|
1385
|
+
};
|
|
1386
|
+
|
|
1387
|
+
// src/builders/main.builder.ts
|
|
1388
|
+
var IgniterAgentManagerBuilder = class _IgniterAgentManagerBuilder {
|
|
1389
|
+
constructor(manager) {
|
|
1390
|
+
this._manager = manager;
|
|
1391
|
+
}
|
|
1392
|
+
addAgent(name, agent) {
|
|
1393
|
+
return new _IgniterAgentManagerBuilder({
|
|
1394
|
+
logger: this._manager.logger,
|
|
1395
|
+
telemetry: this._manager.telemetry,
|
|
1396
|
+
autoStart: this._manager.autoStart,
|
|
1397
|
+
continueOnError: this._manager.continueOnError,
|
|
1398
|
+
onAgentStart: this._manager.onAgentStart,
|
|
1399
|
+
onAgentError: this._manager.onAgentError,
|
|
1400
|
+
onToolCallStart: this._manager.onToolCallStart,
|
|
1401
|
+
onToolCallEnd: this._manager.onToolCallEnd,
|
|
1402
|
+
onToolCallError: this._manager.onToolCallError,
|
|
1403
|
+
onMCPStart: this._manager.onMCPStart,
|
|
1404
|
+
onMCPError: this._manager.onMCPError,
|
|
1405
|
+
agents: {
|
|
1406
|
+
...this._manager.agents,
|
|
1407
|
+
[name]: agent
|
|
1408
|
+
}
|
|
1409
|
+
});
|
|
1410
|
+
}
|
|
1411
|
+
withLogger(logger) {
|
|
1412
|
+
return new _IgniterAgentManagerBuilder({
|
|
1413
|
+
...this._manager,
|
|
1414
|
+
logger
|
|
1415
|
+
});
|
|
1416
|
+
}
|
|
1417
|
+
withTelemetry(telemetry) {
|
|
1418
|
+
return new _IgniterAgentManagerBuilder({
|
|
1419
|
+
...this._manager,
|
|
1420
|
+
telemetry
|
|
1421
|
+
});
|
|
1422
|
+
}
|
|
1423
|
+
withAutoStart(autoStart) {
|
|
1424
|
+
return new _IgniterAgentManagerBuilder({
|
|
1425
|
+
...this._manager,
|
|
1426
|
+
autoStart
|
|
1427
|
+
});
|
|
1428
|
+
}
|
|
1429
|
+
withContinueOnError(continueOnError) {
|
|
1430
|
+
return new _IgniterAgentManagerBuilder({
|
|
1431
|
+
...this._manager,
|
|
1432
|
+
continueOnError
|
|
1433
|
+
});
|
|
1434
|
+
}
|
|
1435
|
+
onAgentStart(callback) {
|
|
1436
|
+
return new _IgniterAgentManagerBuilder({
|
|
1437
|
+
...this._manager,
|
|
1438
|
+
onAgentStart: callback
|
|
1439
|
+
});
|
|
1440
|
+
}
|
|
1441
|
+
onAgentError(callback) {
|
|
1442
|
+
return new _IgniterAgentManagerBuilder({
|
|
1443
|
+
...this._manager,
|
|
1444
|
+
onAgentError: callback
|
|
1445
|
+
});
|
|
1446
|
+
}
|
|
1447
|
+
onToolCallStart(callback) {
|
|
1448
|
+
const manager = new _IgniterAgentManagerBuilder({
|
|
1449
|
+
...this._manager,
|
|
1450
|
+
onToolCallStart: callback
|
|
1451
|
+
});
|
|
1452
|
+
return manager;
|
|
1453
|
+
}
|
|
1454
|
+
onToolCallEnd(callback) {
|
|
1455
|
+
const manager = new _IgniterAgentManagerBuilder({
|
|
1456
|
+
...this._manager,
|
|
1457
|
+
onToolCallEnd: callback
|
|
1458
|
+
});
|
|
1459
|
+
return manager;
|
|
1460
|
+
}
|
|
1461
|
+
onToolCallError(callback) {
|
|
1462
|
+
const manager = new _IgniterAgentManagerBuilder({
|
|
1463
|
+
...this._manager,
|
|
1464
|
+
onToolCallError: callback
|
|
1465
|
+
});
|
|
1466
|
+
return manager;
|
|
1467
|
+
}
|
|
1468
|
+
onMCPStart(callback) {
|
|
1469
|
+
const manager = new _IgniterAgentManagerBuilder({
|
|
1470
|
+
...this._manager,
|
|
1471
|
+
onMCPStart: callback
|
|
1472
|
+
});
|
|
1473
|
+
return manager;
|
|
1474
|
+
}
|
|
1475
|
+
onMCPError(callback) {
|
|
1476
|
+
const manager = new _IgniterAgentManagerBuilder({
|
|
1477
|
+
...this._manager,
|
|
1478
|
+
onMCPError: callback
|
|
1479
|
+
});
|
|
1480
|
+
return manager;
|
|
1481
|
+
}
|
|
1482
|
+
static create() {
|
|
1483
|
+
return new _IgniterAgentManagerBuilder({
|
|
1484
|
+
agents: {},
|
|
1485
|
+
autoStart: false,
|
|
1486
|
+
continueOnError: true
|
|
1487
|
+
});
|
|
1488
|
+
}
|
|
1489
|
+
build() {
|
|
1490
|
+
return new IgniterAgentManagerCore(this._manager);
|
|
1491
|
+
}
|
|
1492
|
+
};
|
|
1493
|
+
var IgniterAgentManager = {
|
|
1494
|
+
create: IgniterAgentManagerBuilder.create
|
|
1495
|
+
};
|
|
1496
|
+
|
|
1497
|
+
// src/builders/prompt.builder.ts
|
|
1498
|
+
var TEMPLATE_PATTERN = /\{\{\s*([^}]+?)\s*\}\}/g;
|
|
1499
|
+
var resolvePath = (value, path) => {
|
|
1500
|
+
if (!path) return void 0;
|
|
1501
|
+
return path.split(".").reduce((acc, key) => {
|
|
1502
|
+
if (acc && typeof acc === "object" && key in acc) {
|
|
1503
|
+
return acc[key];
|
|
1504
|
+
}
|
|
1505
|
+
return void 0;
|
|
1506
|
+
}, value);
|
|
1507
|
+
};
|
|
1508
|
+
var IgniterAgentPromptBuilder = class _IgniterAgentPromptBuilder {
|
|
1509
|
+
constructor(template) {
|
|
1510
|
+
this.template = template;
|
|
1511
|
+
}
|
|
1512
|
+
/**
|
|
1513
|
+
* Creates a new prompt builder.
|
|
1514
|
+
*
|
|
1515
|
+
* @param template - Prompt template string with {{placeholders}}
|
|
1516
|
+
* @returns A new prompt builder instance
|
|
1517
|
+
*/
|
|
1518
|
+
static create(template) {
|
|
1519
|
+
return new _IgniterAgentPromptBuilder(
|
|
1520
|
+
template
|
|
1521
|
+
);
|
|
1522
|
+
}
|
|
1523
|
+
/**
|
|
1524
|
+
* Builds the prompt string with the provided context.
|
|
1525
|
+
*
|
|
1526
|
+
* @param context - Context data used for interpolation
|
|
1527
|
+
* @returns The resolved prompt string
|
|
1528
|
+
*/
|
|
1529
|
+
build(context) {
|
|
1530
|
+
return this.template.replace(TEMPLATE_PATTERN, (_match, path) => {
|
|
1531
|
+
const value = resolvePath(context, String(path));
|
|
1532
|
+
return value === void 0 || value === null ? "" : String(value);
|
|
1533
|
+
});
|
|
1534
|
+
}
|
|
1535
|
+
/**
|
|
1536
|
+
* Returns the raw prompt template string.
|
|
1537
|
+
*/
|
|
1538
|
+
getTemplate() {
|
|
1539
|
+
return this.template;
|
|
1540
|
+
}
|
|
1541
|
+
};
|
|
1542
|
+
var IgniterAgentPrompt = IgniterAgentPromptBuilder;
|
|
1543
|
+
|
|
1544
|
+
// src/builders/agent.builder.ts
|
|
1545
|
+
var IgniterAgentBuilder = class _IgniterAgentBuilder {
|
|
1546
|
+
/**
|
|
1547
|
+
* Creates a new IgniterAgentBuilder instance.
|
|
1548
|
+
*
|
|
1549
|
+
* @param config - Initial configuration
|
|
1550
|
+
* @internal
|
|
1551
|
+
*/
|
|
1552
|
+
constructor(config = {}) {
|
|
1553
|
+
this._config = {
|
|
1554
|
+
name: "agent",
|
|
1555
|
+
toolsets: {},
|
|
1556
|
+
configs: {},
|
|
1557
|
+
instructions: IgniterAgentPromptBuilder.create(""),
|
|
1558
|
+
...config
|
|
1559
|
+
};
|
|
1560
|
+
}
|
|
1561
|
+
/* ---------------------------------------------------------------------------
|
|
1562
|
+
* STATIC FACTORY METHODS
|
|
1563
|
+
* --------------------------------------------------------------------------- */
|
|
1564
|
+
/**
|
|
1565
|
+
* Creates a new agent builder.
|
|
1566
|
+
*
|
|
1567
|
+
* @description
|
|
1568
|
+
* Primary entry point for creating an agent. Returns a new builder
|
|
1569
|
+
* instance that can be configured using fluent methods.
|
|
1570
|
+
*
|
|
1571
|
+
* @returns A new IgniterAgentBuilder instance
|
|
1572
|
+
*
|
|
1573
|
+
* @example
|
|
1574
|
+
* ```typescript
|
|
1575
|
+
* const agent = IgniterAgent.create('my-agent')
|
|
1576
|
+
* .withModel(openai('gpt-4'))
|
|
1577
|
+
* .build();
|
|
1578
|
+
* ```
|
|
1579
|
+
*
|
|
1580
|
+
* @public
|
|
1581
|
+
*/
|
|
1582
|
+
static create(name = "agent") {
|
|
1583
|
+
return new _IgniterAgentBuilder({
|
|
1584
|
+
name
|
|
1585
|
+
});
|
|
1586
|
+
}
|
|
1587
|
+
/* ---------------------------------------------------------------------------
|
|
1588
|
+
* BUILDER METHODS
|
|
1589
|
+
* --------------------------------------------------------------------------- */
|
|
1590
|
+
/**
|
|
1591
|
+
* Sets the language model.
|
|
1592
|
+
*
|
|
1593
|
+
* @description
|
|
1594
|
+
* Configures the AI model that powers the agent. Supports any model
|
|
1595
|
+
* provider compatible with the Vercel AI SDK.
|
|
1596
|
+
*
|
|
1597
|
+
* @typeParam TNewModel - The new model type
|
|
1598
|
+
* @param model - The language model instance
|
|
1599
|
+
* @returns A new builder with the model set
|
|
1600
|
+
*
|
|
1601
|
+
* @example
|
|
1602
|
+
* ```typescript
|
|
1603
|
+
* import { openai } from '@ai-sdk/openai';
|
|
1604
|
+
* import { anthropic } from '@ai-sdk/anthropic';
|
|
1605
|
+
* import { google } from '@ai-sdk/google';
|
|
1606
|
+
*
|
|
1607
|
+
* // Using OpenAI
|
|
1608
|
+
* const agent1 = IgniterAgent.create()
|
|
1609
|
+
* .withModel(openai('gpt-4-turbo'))
|
|
1610
|
+
* .build();
|
|
1611
|
+
*
|
|
1612
|
+
* // Using Anthropic
|
|
1613
|
+
* const agent2 = IgniterAgent.create()
|
|
1614
|
+
* .withModel(anthropic('claude-3-opus'))
|
|
1615
|
+
* .build();
|
|
1616
|
+
*
|
|
1617
|
+
* // Using Google
|
|
1618
|
+
* const agent3 = IgniterAgent.create()
|
|
1619
|
+
* .withModel(google('gemini-pro'))
|
|
1620
|
+
* .build();
|
|
1621
|
+
* ```
|
|
1622
|
+
*
|
|
1623
|
+
* @public
|
|
1624
|
+
*/
|
|
1625
|
+
withModel(model) {
|
|
1626
|
+
return new _IgniterAgentBuilder({
|
|
1627
|
+
...this._config,
|
|
1628
|
+
model
|
|
1629
|
+
});
|
|
1630
|
+
}
|
|
1631
|
+
/**
|
|
1632
|
+
* Sets the prompt instructions.
|
|
1633
|
+
*
|
|
1634
|
+
* @description
|
|
1635
|
+
* Configures the agent's system prompt using an IgniterAgentPrompt template.
|
|
1636
|
+
* The prompt defines the agent's behavior, personality, and capabilities.
|
|
1637
|
+
*
|
|
1638
|
+
* @typeParam TNewInstructions - The new instructions type
|
|
1639
|
+
* @param instructions - The prompt template instance
|
|
1640
|
+
* @returns A new builder with the instructions set
|
|
1641
|
+
*
|
|
1642
|
+
* @example
|
|
1643
|
+
* ```typescript
|
|
1644
|
+
* const agent = IgniterAgent.create()
|
|
1645
|
+
* .withPrompt(
|
|
1646
|
+
* IgniterAgentPrompt.create(`
|
|
1647
|
+
* You are a helpful coding assistant specialized in TypeScript.
|
|
1648
|
+
*
|
|
1649
|
+
* Guidelines:
|
|
1650
|
+
* - Always provide type-safe code
|
|
1651
|
+
* - Include JSDoc comments
|
|
1652
|
+
* - Follow best practices
|
|
1653
|
+
* `)
|
|
1654
|
+
* )
|
|
1655
|
+
* .build();
|
|
1656
|
+
* ```
|
|
1657
|
+
*
|
|
1658
|
+
* @public
|
|
1659
|
+
*/
|
|
1660
|
+
withPrompt(instructions) {
|
|
1661
|
+
return new _IgniterAgentBuilder({
|
|
1662
|
+
...this._config,
|
|
1663
|
+
instructions
|
|
1664
|
+
});
|
|
1665
|
+
}
|
|
1666
|
+
/**
|
|
1667
|
+
* Attaches a logger instance for operational logs.
|
|
1668
|
+
*/
|
|
1669
|
+
withLogger(logger) {
|
|
1670
|
+
return new _IgniterAgentBuilder({
|
|
1671
|
+
...this._config,
|
|
1672
|
+
logger
|
|
1673
|
+
});
|
|
1674
|
+
}
|
|
1675
|
+
/**
|
|
1676
|
+
* Attaches a telemetry manager for observability.
|
|
1677
|
+
*/
|
|
1678
|
+
withTelemetry(telemetry) {
|
|
1679
|
+
return new _IgniterAgentBuilder({
|
|
1680
|
+
...this._config,
|
|
1681
|
+
telemetry
|
|
1682
|
+
});
|
|
1683
|
+
}
|
|
1684
|
+
/**
|
|
1685
|
+
* Configures persistent memory for the agent.
|
|
1686
|
+
*/
|
|
1687
|
+
withMemory(memory) {
|
|
1688
|
+
return new _IgniterAgentBuilder({
|
|
1689
|
+
...this._config,
|
|
1690
|
+
memory
|
|
1691
|
+
});
|
|
1692
|
+
}
|
|
1693
|
+
/**
|
|
1694
|
+
* Callback when the agent starts.
|
|
1695
|
+
*/
|
|
1696
|
+
onAgentStart(callback) {
|
|
1697
|
+
return new _IgniterAgentBuilder({
|
|
1698
|
+
...this._config,
|
|
1699
|
+
hooks: {
|
|
1700
|
+
...this._config.hooks,
|
|
1701
|
+
onAgentStart: callback
|
|
1702
|
+
}
|
|
1703
|
+
});
|
|
1704
|
+
}
|
|
1705
|
+
/**
|
|
1706
|
+
* Callback when the agent errors.
|
|
1707
|
+
*/
|
|
1708
|
+
onAgentError(callback) {
|
|
1709
|
+
return new _IgniterAgentBuilder({
|
|
1710
|
+
...this._config,
|
|
1711
|
+
hooks: {
|
|
1712
|
+
...this._config.hooks,
|
|
1713
|
+
onAgentError: callback
|
|
1714
|
+
}
|
|
1715
|
+
});
|
|
1716
|
+
}
|
|
1717
|
+
/**
|
|
1718
|
+
* Callback when a tool call starts.
|
|
1719
|
+
*/
|
|
1720
|
+
onToolCallStart(callback) {
|
|
1721
|
+
return new _IgniterAgentBuilder({
|
|
1722
|
+
...this._config,
|
|
1723
|
+
hooks: {
|
|
1724
|
+
...this._config.hooks,
|
|
1725
|
+
onToolCallStart: callback
|
|
1726
|
+
}
|
|
1727
|
+
});
|
|
1728
|
+
}
|
|
1729
|
+
/**
|
|
1730
|
+
* Callback when a tool call completes.
|
|
1731
|
+
*/
|
|
1732
|
+
onToolCallEnd(callback) {
|
|
1733
|
+
return new _IgniterAgentBuilder({
|
|
1734
|
+
...this._config,
|
|
1735
|
+
hooks: {
|
|
1736
|
+
...this._config.hooks,
|
|
1737
|
+
onToolCallEnd: callback
|
|
1738
|
+
}
|
|
1739
|
+
});
|
|
1740
|
+
}
|
|
1741
|
+
/**
|
|
1742
|
+
* Callback when a tool call fails.
|
|
1743
|
+
*/
|
|
1744
|
+
onToolCallError(callback) {
|
|
1745
|
+
return new _IgniterAgentBuilder({
|
|
1746
|
+
...this._config,
|
|
1747
|
+
hooks: {
|
|
1748
|
+
...this._config.hooks,
|
|
1749
|
+
onToolCallError: callback
|
|
1750
|
+
}
|
|
1751
|
+
});
|
|
1752
|
+
}
|
|
1753
|
+
/**
|
|
1754
|
+
* Callback when an MCP connection starts.
|
|
1755
|
+
*/
|
|
1756
|
+
onMCPStart(callback) {
|
|
1757
|
+
return new _IgniterAgentBuilder({
|
|
1758
|
+
...this._config,
|
|
1759
|
+
hooks: {
|
|
1760
|
+
...this._config.hooks,
|
|
1761
|
+
onMCPStart: callback
|
|
1762
|
+
}
|
|
1763
|
+
});
|
|
1764
|
+
}
|
|
1765
|
+
/**
|
|
1766
|
+
* Callback when an MCP connection fails.
|
|
1767
|
+
*/
|
|
1768
|
+
onMCPError(callback) {
|
|
1769
|
+
return new _IgniterAgentBuilder({
|
|
1770
|
+
...this._config,
|
|
1771
|
+
hooks: {
|
|
1772
|
+
...this._config.hooks,
|
|
1773
|
+
onMCPError: callback
|
|
1774
|
+
}
|
|
1775
|
+
});
|
|
1776
|
+
}
|
|
1777
|
+
/**
|
|
1778
|
+
* Sets the context schema.
|
|
1779
|
+
*
|
|
1780
|
+
* @description
|
|
1781
|
+
* Configures a Zod schema for validating context passed to the agent.
|
|
1782
|
+
* The context is available in prompt templates and can be used to
|
|
1783
|
+
* customize agent behavior per-request.
|
|
1784
|
+
*
|
|
1785
|
+
* @typeParam TNewSchema - The new schema type
|
|
1786
|
+
* @param schema - The Zod schema for context validation
|
|
1787
|
+
* @returns A new builder with the schema set
|
|
1788
|
+
*
|
|
1789
|
+
* @example
|
|
1790
|
+
* ```typescript
|
|
1791
|
+
* import { z } from 'zod';
|
|
1792
|
+
*
|
|
1793
|
+
* const contextSchema = z.object({
|
|
1794
|
+
* userId: z.string(),
|
|
1795
|
+
* chatId: z.string(),
|
|
1796
|
+
* userRole: z.enum(['admin', 'user', 'guest']),
|
|
1797
|
+
* preferences: z.object({
|
|
1798
|
+
* language: z.string().default('en'),
|
|
1799
|
+
* theme: z.string().optional()
|
|
1800
|
+
* })
|
|
1801
|
+
* });
|
|
1802
|
+
*
|
|
1803
|
+
* const agent = IgniterAgent.create()
|
|
1804
|
+
* .withContextSchema(contextSchema)
|
|
1805
|
+
* .build();
|
|
1806
|
+
*
|
|
1807
|
+
* // Context is type-safe when calling generate
|
|
1808
|
+
* await agent.generate({
|
|
1809
|
+
* messages: [...],
|
|
1810
|
+
* options: {
|
|
1811
|
+
* userId: 'user_123',
|
|
1812
|
+
* chatId: 'chat_456',
|
|
1813
|
+
* userRole: 'admin',
|
|
1814
|
+
* preferences: { language: 'pt' }
|
|
1815
|
+
* }
|
|
1816
|
+
* });
|
|
1817
|
+
* ```
|
|
1818
|
+
*
|
|
1819
|
+
* @public
|
|
1820
|
+
*/
|
|
1821
|
+
withContextSchema(schema) {
|
|
1822
|
+
return new _IgniterAgentBuilder({
|
|
1823
|
+
...this._config,
|
|
1824
|
+
schema
|
|
1825
|
+
});
|
|
1826
|
+
}
|
|
1827
|
+
/**
|
|
1828
|
+
* Adds a toolset to the agent.
|
|
1829
|
+
*
|
|
1830
|
+
* @description
|
|
1831
|
+
* Registers a toolset with the agent. The toolset's tools become available
|
|
1832
|
+
* for the AI to invoke. Multiple toolsets can be added to a single agent.
|
|
1833
|
+
*
|
|
1834
|
+
* Tool names are prefixed with the toolset name to avoid collisions:
|
|
1835
|
+
* `{toolsetName}_{toolName}`
|
|
1836
|
+
*
|
|
1837
|
+
* @typeParam TNewToolset - The toolset type
|
|
1838
|
+
* @param toolset - The toolset to register
|
|
1839
|
+
* @returns A new builder with the toolset added
|
|
1840
|
+
*
|
|
1841
|
+
* @example
|
|
1842
|
+
* ```typescript
|
|
1843
|
+
* const githubToolset = IgniterAgentToolset.create('github')
|
|
1844
|
+
* .addTool(createIssueTool)
|
|
1845
|
+
* .addTool(listReposTool)
|
|
1846
|
+
* .build();
|
|
1847
|
+
*
|
|
1848
|
+
* const dockerToolset = IgniterAgentToolset.create('docker')
|
|
1849
|
+
* .addTool(buildImageTool)
|
|
1850
|
+
* .addTool(runContainerTool)
|
|
1851
|
+
* .build();
|
|
1852
|
+
*
|
|
1853
|
+
* const agent = IgniterAgent.create()
|
|
1854
|
+
* .addToolset(githubToolset) // Adds github_createIssue, github_listRepos
|
|
1855
|
+
* .addToolset(dockerToolset) // Adds docker_build, docker_run
|
|
1856
|
+
* .build();
|
|
1857
|
+
* ```
|
|
1858
|
+
*
|
|
1859
|
+
* @public
|
|
1860
|
+
*/
|
|
1861
|
+
addToolset(toolset) {
|
|
1862
|
+
return new _IgniterAgentBuilder({
|
|
1863
|
+
...this._config,
|
|
1864
|
+
toolsets: {
|
|
1865
|
+
...this._config.toolsets,
|
|
1866
|
+
[toolset.name]: toolset
|
|
1867
|
+
}
|
|
1868
|
+
});
|
|
1869
|
+
}
|
|
1870
|
+
/**
|
|
1871
|
+
* Adds an MCP configuration to the agent.
|
|
1872
|
+
*
|
|
1873
|
+
* @description
|
|
1874
|
+
* Registers an MCP server configuration with the agent. The MCP server
|
|
1875
|
+
* will be connected when `agent.start()` is called, and its tools will
|
|
1876
|
+
* become available for the AI to invoke.
|
|
1877
|
+
*
|
|
1878
|
+
* @typeParam TMCPType - The MCP transport type
|
|
1879
|
+
* @typeParam TMCPName - The MCP configuration name
|
|
1880
|
+
* @param config - The MCP configuration
|
|
1881
|
+
* @returns A new builder with the MCP config added
|
|
1882
|
+
*
|
|
1883
|
+
* @example
|
|
1884
|
+
* ```typescript
|
|
1885
|
+
* const filesystemMCP = IgniterAgentMCPClient.create('filesystem')
|
|
1886
|
+
* .withType('stdio')
|
|
1887
|
+
* .withCommand('npx')
|
|
1888
|
+
* .withArgs(['-y', '@modelcontextprotocol/server-filesystem', '/tmp'])
|
|
1889
|
+
* .build();
|
|
1890
|
+
*
|
|
1891
|
+
* const remoteMCP = IgniterAgentMCPClient.create('remote-api')
|
|
1892
|
+
* .withType('http')
|
|
1893
|
+
* .withURL('https://api.example.com/mcp')
|
|
1894
|
+
* .withHeaders({ 'Authorization': 'Bearer xxx' })
|
|
1895
|
+
* .build();
|
|
1896
|
+
*
|
|
1897
|
+
* const agent = IgniterAgent.create()
|
|
1898
|
+
* .addMCP(filesystemMCP)
|
|
1899
|
+
* .addMCP(remoteMCP)
|
|
1900
|
+
* .build();
|
|
1901
|
+
*
|
|
1902
|
+
* // MCP connections are established here
|
|
1903
|
+
* await agent.start();
|
|
1904
|
+
* ```
|
|
1905
|
+
*
|
|
1906
|
+
* @public
|
|
1907
|
+
*/
|
|
1908
|
+
addMCP(config) {
|
|
1909
|
+
return new _IgniterAgentBuilder({
|
|
1910
|
+
...this._config,
|
|
1911
|
+
configs: {
|
|
1912
|
+
...this._config.configs,
|
|
1913
|
+
[config.name]: config
|
|
1914
|
+
}
|
|
1915
|
+
});
|
|
1916
|
+
}
|
|
1917
|
+
/* ---------------------------------------------------------------------------
|
|
1918
|
+
* BUILD METHOD
|
|
1919
|
+
* --------------------------------------------------------------------------- */
|
|
1920
|
+
/**
|
|
1921
|
+
* Builds and returns the completed agent.
|
|
1922
|
+
*
|
|
1923
|
+
* @description
|
|
1924
|
+
* Finalizes the agent configuration and returns a runtime agent object.
|
|
1925
|
+
* The agent provides methods for:
|
|
1926
|
+
* - `start()`: Initialize MCP connections
|
|
1927
|
+
* - `generate()`: Generate a single response
|
|
1928
|
+
* - `stream()`: Stream a response
|
|
1929
|
+
* - Accessors for configuration
|
|
1930
|
+
*
|
|
1931
|
+
* @returns The built agent runtime object
|
|
1932
|
+
*
|
|
1933
|
+
* @example
|
|
1934
|
+
* ```typescript
|
|
1935
|
+
* const agent = IgniterAgent.create('assistant')
|
|
1936
|
+
* .withModel(openai('gpt-4'))
|
|
1937
|
+
* .addToolset(myToolset)
|
|
1938
|
+
* .build();
|
|
1939
|
+
*
|
|
1940
|
+
* // Start the agent
|
|
1941
|
+
* await agent.start();
|
|
1942
|
+
*
|
|
1943
|
+
* // Generate a response
|
|
1944
|
+
* const result = await agent.generate({
|
|
1945
|
+
* messages: [{ role: 'user', content: 'Hello!' }]
|
|
1946
|
+
* });
|
|
1947
|
+
*
|
|
1948
|
+
* // Access configuration
|
|
1949
|
+
* console.log('Model:', agent.getModel());
|
|
1950
|
+
* console.log('Toolsets:', agent.getToolsets());
|
|
1951
|
+
* ```
|
|
1952
|
+
*
|
|
1953
|
+
* @public
|
|
1954
|
+
*/
|
|
1955
|
+
build() {
|
|
1956
|
+
return new IgniterAgentCore(this._config);
|
|
1957
|
+
}
|
|
1958
|
+
/* ---------------------------------------------------------------------------
|
|
1959
|
+
* ACCESSORS
|
|
1960
|
+
* --------------------------------------------------------------------------- */
|
|
1961
|
+
/**
|
|
1962
|
+
* Gets the current configuration (partial).
|
|
1963
|
+
*
|
|
1964
|
+
* @returns The current configuration state
|
|
1965
|
+
* @public
|
|
1966
|
+
*/
|
|
1967
|
+
getConfig() {
|
|
1968
|
+
return { ...this._config };
|
|
1969
|
+
}
|
|
1970
|
+
/**
|
|
1971
|
+
* Gets the agent name.
|
|
1972
|
+
*
|
|
1973
|
+
* @returns The agent's name
|
|
1974
|
+
* @public
|
|
1975
|
+
*/
|
|
1976
|
+
getName() {
|
|
1977
|
+
return this._config.name;
|
|
1978
|
+
}
|
|
1979
|
+
};
|
|
1980
|
+
var IgniterAgent = {
|
|
1981
|
+
create: IgniterAgentBuilder.create
|
|
1982
|
+
};
|
|
1983
|
+
var IgniterAgentToolsetBuilder = class _IgniterAgentToolsetBuilder {
|
|
1984
|
+
/**
|
|
1985
|
+
* Creates a new IgniterAgentToolsetBuilder instance.
|
|
1986
|
+
*
|
|
1987
|
+
* @param params - The toolset name and initial tools
|
|
1988
|
+
* @internal
|
|
1989
|
+
*/
|
|
1990
|
+
constructor(params) {
|
|
1991
|
+
this._name = params.name;
|
|
1992
|
+
this._tools = params.tools;
|
|
1993
|
+
}
|
|
1994
|
+
/* ---------------------------------------------------------------------------
|
|
1995
|
+
* STATIC FACTORY METHODS
|
|
1996
|
+
* --------------------------------------------------------------------------- */
|
|
1997
|
+
/**
|
|
1998
|
+
* Creates a new toolset builder with the given name.
|
|
1999
|
+
*
|
|
2000
|
+
* @description
|
|
2001
|
+
* This is the primary entry point for creating a new toolset.
|
|
2002
|
+
* The name must be unique within an agent and is used as a prefix
|
|
2003
|
+
* for tool names when the toolset is registered.
|
|
2004
|
+
*
|
|
2005
|
+
* @typeParam TName - The toolset name type
|
|
2006
|
+
* @param name - Unique name for the toolset
|
|
2007
|
+
* @returns A new IgniterAgentToolsetBuilder instance
|
|
2008
|
+
*
|
|
2009
|
+
* @example
|
|
2010
|
+
* ```typescript
|
|
2011
|
+
* const tool = IgniterAgentTool
|
|
2012
|
+
* .create('doSomething')
|
|
2013
|
+
* .withDescription('Does something')
|
|
2014
|
+
* .withInputect({}))
|
|
2015
|
+
* .withExecute(async () => ({ ok: true }))
|
|
2016
|
+
* .build();
|
|
2017
|
+
*
|
|
2018
|
+
* const toolset = IgniterAgentToolset
|
|
2019
|
+
* .create('myTools')
|
|
2020
|
+
* .addTool(tool)
|
|
2021
|
+
* .build();
|
|
2022
|
+
* ```
|
|
2023
|
+
*
|
|
2024
|
+
* @public
|
|
2025
|
+
*/
|
|
2026
|
+
static create(name) {
|
|
2027
|
+
if (!name || typeof name !== "string") {
|
|
2028
|
+
throw new IgniterAgentConfigError({
|
|
2029
|
+
message: "Toolset name is required and must be a non-empty string",
|
|
2030
|
+
field: "name"
|
|
2031
|
+
});
|
|
2032
|
+
}
|
|
2033
|
+
return new _IgniterAgentToolsetBuilder({
|
|
2034
|
+
name,
|
|
2035
|
+
tools: {}
|
|
2036
|
+
});
|
|
2037
|
+
}
|
|
2038
|
+
/* ---------------------------------------------------------------------------
|
|
2039
|
+
* BUILDER METHODS
|
|
2040
|
+
* --------------------------------------------------------------------------- */
|
|
2041
|
+
/**
|
|
2042
|
+
* Changes the toolset name.
|
|
2043
|
+
*
|
|
2044
|
+
* @description
|
|
2045
|
+
* Allows renaming the toolset during the building process.
|
|
2046
|
+
* This creates a new builder instance with the new name.
|
|
2047
|
+
*
|
|
2048
|
+
* @typeParam TNewName - The new name type
|
|
2049
|
+
* @param name - The new name for the toolset
|
|
2050
|
+
* @returns A new builder with the updated name
|
|
2051
|
+
*
|
|
2052
|
+
* @example
|
|
2053
|
+
* ```typescript
|
|
2054
|
+
* const toolset = IgniterAgentToolset
|
|
2055
|
+
* .create('temp')
|
|
2056
|
+
* .withName('production') // Rename to 'production'
|
|
2057
|
+
* .addTool(myTool)
|
|
2058
|
+
* .build();
|
|
2059
|
+
* ```
|
|
2060
|
+
*
|
|
2061
|
+
* @public
|
|
2062
|
+
*/
|
|
2063
|
+
withName(name) {
|
|
2064
|
+
return new _IgniterAgentToolsetBuilder({
|
|
2065
|
+
name,
|
|
2066
|
+
tools: this._tools
|
|
2067
|
+
});
|
|
2068
|
+
}
|
|
2069
|
+
/**
|
|
2070
|
+
* Adds a tool to the toolset.
|
|
2071
|
+
*
|
|
2072
|
+
* @description
|
|
2073
|
+
* Adds a built tool definition to the toolset. The tool is automatically
|
|
2074
|
+
* wrapped with error handling that returns a consistent result format
|
|
2075
|
+
* (`{ success: true, data: ... }` or `{ success: false, error: ... }`).
|
|
2076
|
+
*
|
|
2077
|
+
* @typeParam TToolName - The tool name type
|
|
2078
|
+
* @param dto - The built tool definition
|
|
2079
|
+
* @returns A new builder with the tool added
|
|
2080
|
+
*
|
|
2081
|
+
* @example
|
|
2082
|
+
* ```typescript
|
|
2083
|
+
* const greetTool = IgniterAgentTool.create('greet')
|
|
2084
|
+
* .withDescription('Greets a user')
|
|
2085
|
+
* .withInputect({ name: z.string() }))
|
|
2086
|
+
* .withExecute(async ({ name }) => `Hello, ${name}!`)
|
|
2087
|
+
* .build();
|
|
2088
|
+
*
|
|
2089
|
+
* const toolset = IgniterAgentToolset.create('utils')
|
|
2090
|
+
* .addTool(greetTool)
|
|
2091
|
+
* .build();
|
|
2092
|
+
* ```
|
|
2093
|
+
*
|
|
2094
|
+
* @public
|
|
2095
|
+
*/
|
|
2096
|
+
addTool(dto) {
|
|
2097
|
+
if (!dto.name || typeof dto.name !== "string") {
|
|
2098
|
+
throw new IgniterAgentConfigError({
|
|
2099
|
+
message: "Tool name is required and must be a non-empty string",
|
|
2100
|
+
field: "name",
|
|
2101
|
+
metadata: { toolset: this._name }
|
|
2102
|
+
});
|
|
2103
|
+
}
|
|
2104
|
+
const wrappedTool = ai.tool({
|
|
2105
|
+
description: dto.description,
|
|
2106
|
+
inputSchema: dto.inputSchema,
|
|
2107
|
+
outputSchema: dto.outputSchema,
|
|
2108
|
+
execute: async (params, options) => {
|
|
2109
|
+
if (!dto.execute) {
|
|
2110
|
+
const result = {
|
|
2111
|
+
success: false,
|
|
2112
|
+
error: `Tool '${dto.name}' has no execute method`
|
|
2113
|
+
};
|
|
2114
|
+
return result;
|
|
2115
|
+
}
|
|
2116
|
+
try {
|
|
2117
|
+
const response = await dto.execute(params, options);
|
|
2118
|
+
const result = {
|
|
2119
|
+
success: true,
|
|
2120
|
+
data: response
|
|
2121
|
+
};
|
|
2122
|
+
return result;
|
|
2123
|
+
} catch (error) {
|
|
2124
|
+
console.error(
|
|
2125
|
+
`[IgniterAgent] Error executing tool '${dto.name}':`,
|
|
2126
|
+
error
|
|
2127
|
+
);
|
|
2128
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
2129
|
+
const result = {
|
|
2130
|
+
success: false,
|
|
2131
|
+
error: errorMessage
|
|
2132
|
+
};
|
|
2133
|
+
return result;
|
|
2134
|
+
}
|
|
2135
|
+
}
|
|
2136
|
+
});
|
|
2137
|
+
return new _IgniterAgentToolsetBuilder({
|
|
2138
|
+
name: this._name,
|
|
2139
|
+
tools: {
|
|
2140
|
+
...this._tools,
|
|
2141
|
+
[dto.name]: wrappedTool
|
|
2142
|
+
}
|
|
2143
|
+
});
|
|
2144
|
+
}
|
|
2145
|
+
/* ---------------------------------------------------------------------------
|
|
2146
|
+
* BUILD METHOD
|
|
2147
|
+
* --------------------------------------------------------------------------- */
|
|
2148
|
+
/**
|
|
2149
|
+
* Builds and returns the completed toolset.
|
|
2150
|
+
*
|
|
2151
|
+
* @description
|
|
2152
|
+
* Finalizes the toolset configuration and returns an object that can
|
|
2153
|
+
* be registered with an agent using `addToolset()`.
|
|
2154
|
+
*
|
|
2155
|
+
* The returned object includes:
|
|
2156
|
+
* - `name`: The toolset's unique identifier
|
|
2157
|
+
* - `type`: Always `'custom'` for user-defined toolsets
|
|
2158
|
+
* - `status`: Always `'connected'` for custom toolsets
|
|
2159
|
+
* - `tools`: The tools collection
|
|
2160
|
+
* - `toolset`: Reference to the raw tools object
|
|
2161
|
+
*
|
|
2162
|
+
* @returns The completed toolset configuration
|
|
2163
|
+
*
|
|
2164
|
+
* @example
|
|
2165
|
+
* ```typescript
|
|
2166
|
+
* const toolset = IgniterAgentToolset
|
|
2167
|
+
* .create('utils')
|
|
2168
|
+
* .addTool(formatDateTool)
|
|
2169
|
+
* .addTool(parseJsonTool)
|
|
2170
|
+
* .build();
|
|
2171
|
+
*
|
|
2172
|
+
* console.log(toolset.name); // 'utils'
|
|
2173
|
+
* console.log(toolset.type); // 'custom'
|
|
2174
|
+
* console.log(toolset.status); // 'connected'
|
|
2175
|
+
* console.log(Object.keys(toolset.tools)); // ['formatDate', 'parseJSON']
|
|
2176
|
+
* ```
|
|
2177
|
+
*
|
|
2178
|
+
* @public
|
|
2179
|
+
*/
|
|
2180
|
+
build() {
|
|
2181
|
+
return {
|
|
2182
|
+
type: "custom",
|
|
2183
|
+
name: this._name,
|
|
2184
|
+
toolset: this._tools,
|
|
2185
|
+
status: "connected",
|
|
2186
|
+
tools: this._tools,
|
|
2187
|
+
$Infer: {}
|
|
2188
|
+
};
|
|
2189
|
+
}
|
|
2190
|
+
/* ---------------------------------------------------------------------------
|
|
2191
|
+
* ACCESSORS
|
|
2192
|
+
* --------------------------------------------------------------------------- */
|
|
2193
|
+
/**
|
|
2194
|
+
* Gets the current toolset name.
|
|
2195
|
+
*
|
|
2196
|
+
* @returns The toolset's name
|
|
2197
|
+
* @public
|
|
2198
|
+
*/
|
|
2199
|
+
getName() {
|
|
2200
|
+
return this._name;
|
|
2201
|
+
}
|
|
2202
|
+
/**
|
|
2203
|
+
* Gets the current tools collection.
|
|
2204
|
+
*
|
|
2205
|
+
* @returns The tools object
|
|
2206
|
+
* @public
|
|
2207
|
+
*/
|
|
2208
|
+
getTools() {
|
|
2209
|
+
return this._tools;
|
|
2210
|
+
}
|
|
2211
|
+
/**
|
|
2212
|
+
* Gets the number of tools in the toolset.
|
|
2213
|
+
*
|
|
2214
|
+
* @returns The tool count
|
|
2215
|
+
* @public
|
|
2216
|
+
*/
|
|
2217
|
+
getToolCount() {
|
|
2218
|
+
return Object.keys(this._tools).length;
|
|
2219
|
+
}
|
|
2220
|
+
};
|
|
2221
|
+
var IgniterAgentToolset = {
|
|
2222
|
+
create: IgniterAgentToolsetBuilder.create
|
|
2223
|
+
};
|
|
2224
|
+
|
|
2225
|
+
// src/builders/tool.builder.ts
|
|
2226
|
+
var IgniterAgentToolBuilder = class _IgniterAgentToolBuilder {
|
|
2227
|
+
/**
|
|
2228
|
+
* Creates a new IgniterAgentToolBuilder instance.
|
|
2229
|
+
*
|
|
2230
|
+
* @param name - The tool name
|
|
2231
|
+
* @param definition - Partial tool definition
|
|
2232
|
+
* @internal
|
|
2233
|
+
*/
|
|
2234
|
+
constructor(definition = {}) {
|
|
2235
|
+
this._definition = definition;
|
|
2236
|
+
}
|
|
2237
|
+
/* ---------------------------------------------------------------------------
|
|
2238
|
+
* STATIC FACTORY METHODS
|
|
2239
|
+
* --------------------------------------------------------------------------- */
|
|
2240
|
+
/**
|
|
2241
|
+
* Creates a new tool builder with the given name.
|
|
2242
|
+
*
|
|
2243
|
+
* @description
|
|
2244
|
+
* The name must be unique within a toolset.
|
|
2245
|
+
*
|
|
2246
|
+
* @typeParam TNewName - The tool name type
|
|
2247
|
+
* @param name - Unique name for the tool
|
|
2248
|
+
* @returns A new IgniterAgentToolBuilder instance
|
|
2249
|
+
*
|
|
2250
|
+
* @public
|
|
2251
|
+
*/
|
|
2252
|
+
static create(name) {
|
|
2253
|
+
if (!name || typeof name !== "string") {
|
|
2254
|
+
throw new IgniterAgentConfigError({
|
|
2255
|
+
message: "Tool name is required and must be a non-empty string",
|
|
2256
|
+
field: "name"
|
|
2257
|
+
});
|
|
2258
|
+
}
|
|
2259
|
+
return new _IgniterAgentToolBuilder({
|
|
2260
|
+
name
|
|
2261
|
+
});
|
|
2262
|
+
}
|
|
2263
|
+
/* ---------------------------------------------------------------------------
|
|
2264
|
+
* BUILDER METHODS
|
|
2265
|
+
* --------------------------------------------------------------------------- */
|
|
2266
|
+
/**
|
|
2267
|
+
* Sets the tool description.
|
|
2268
|
+
*
|
|
2269
|
+
* @param description - Human-readable description shown to the AI
|
|
2270
|
+
* @returns A new builder with the description set
|
|
2271
|
+
*
|
|
2272
|
+
* @public
|
|
2273
|
+
*/
|
|
2274
|
+
withDescription(description) {
|
|
2275
|
+
if (!description || typeof description !== "string") {
|
|
2276
|
+
throw new IgniterAgentConfigError({
|
|
2277
|
+
message: "Tool description is required and must be a non-empty string",
|
|
2278
|
+
field: "description",
|
|
2279
|
+
metadata: { tool: this._definition.name }
|
|
2280
|
+
});
|
|
2281
|
+
}
|
|
2282
|
+
return new _IgniterAgentToolBuilder({
|
|
2283
|
+
...this._definition,
|
|
2284
|
+
description
|
|
2285
|
+
});
|
|
2286
|
+
}
|
|
2287
|
+
/**
|
|
2288
|
+
* Sets the tool parameters schema.
|
|
2289
|
+
*
|
|
2290
|
+
* @typeParam TNewParams - The new parameters type
|
|
2291
|
+
* @param parameters - Zod schema defining input parameters
|
|
2292
|
+
* @returns A new builder with the parameters set
|
|
2293
|
+
*
|
|
2294
|
+
* @public
|
|
2295
|
+
*/
|
|
2296
|
+
withInput(inputSchema) {
|
|
2297
|
+
if (!inputSchema) {
|
|
2298
|
+
throw new IgniterAgentConfigError({
|
|
2299
|
+
message: "Tool parameters schema is required",
|
|
2300
|
+
field: "parameters",
|
|
2301
|
+
metadata: { tool: this._definition.name }
|
|
2302
|
+
});
|
|
2303
|
+
}
|
|
2304
|
+
return new _IgniterAgentToolBuilder({
|
|
2305
|
+
...this._definition,
|
|
2306
|
+
inputSchema
|
|
2307
|
+
});
|
|
2308
|
+
}
|
|
2309
|
+
/**
|
|
2310
|
+
* Sets the tool output schema.
|
|
2311
|
+
*
|
|
2312
|
+
* @typeParam TNewResult - The new result type
|
|
2313
|
+
* @param outputSchema - Zod schema defining the tool's output
|
|
2314
|
+
* @returns A new builder with the output schema set
|
|
2315
|
+
*
|
|
2316
|
+
* @public
|
|
2317
|
+
*/
|
|
2318
|
+
withOutput(outputSchema) {
|
|
2319
|
+
if (!outputSchema) {
|
|
2320
|
+
throw new IgniterAgentConfigError({
|
|
2321
|
+
message: "Tool output schema is required",
|
|
2322
|
+
field: "outputSchema",
|
|
2323
|
+
metadata: { tool: this._definition.name }
|
|
2324
|
+
});
|
|
2325
|
+
}
|
|
2326
|
+
return new _IgniterAgentToolBuilder({
|
|
2327
|
+
...this._definition,
|
|
2328
|
+
outputSchema
|
|
2329
|
+
});
|
|
2330
|
+
}
|
|
2331
|
+
/**
|
|
2332
|
+
* Sets the tool execution handler.
|
|
2333
|
+
*
|
|
2334
|
+
* @typeParam TNewResult - The new result type
|
|
2335
|
+
* @param execute - Function that performs the tool's logic
|
|
2336
|
+
* @returns A new builder with the execute handler set
|
|
2337
|
+
*
|
|
2338
|
+
* @public
|
|
2339
|
+
*/
|
|
2340
|
+
withExecute(execute) {
|
|
2341
|
+
if (typeof execute !== "function") {
|
|
2342
|
+
throw new IgniterAgentConfigError({
|
|
2343
|
+
message: "Tool execute handler must be a function",
|
|
2344
|
+
field: "execute",
|
|
2345
|
+
metadata: { tool: this._definition.name }
|
|
2346
|
+
});
|
|
2347
|
+
}
|
|
2348
|
+
return new _IgniterAgentToolBuilder({
|
|
2349
|
+
...this._definition,
|
|
2350
|
+
execute
|
|
2351
|
+
});
|
|
2352
|
+
}
|
|
2353
|
+
/* ---------------------------------------------------------------------------
|
|
2354
|
+
* BUILD METHOD
|
|
2355
|
+
* --------------------------------------------------------------------------- */
|
|
2356
|
+
/**
|
|
2357
|
+
* Builds and returns the completed tool definition.
|
|
2358
|
+
*
|
|
2359
|
+
* @returns The completed tool definition with its name
|
|
2360
|
+
*
|
|
2361
|
+
* @public
|
|
2362
|
+
*/
|
|
2363
|
+
build() {
|
|
2364
|
+
const { name, description, inputSchema, outputSchema, execute } = this._definition;
|
|
2365
|
+
if (!name) {
|
|
2366
|
+
throw new IgniterAgentConfigError({
|
|
2367
|
+
message: "Tool name is required before building",
|
|
2368
|
+
field: "name"
|
|
2369
|
+
});
|
|
2370
|
+
}
|
|
2371
|
+
if (!description) {
|
|
2372
|
+
throw new IgniterAgentConfigError({
|
|
2373
|
+
message: "Tool description is required before building",
|
|
2374
|
+
field: "description",
|
|
2375
|
+
metadata: { tool: this._definition.name }
|
|
2376
|
+
});
|
|
2377
|
+
}
|
|
2378
|
+
if (!inputSchema) {
|
|
2379
|
+
throw new IgniterAgentConfigError({
|
|
2380
|
+
message: "Tool parameters schema is required before building",
|
|
2381
|
+
field: "parameters",
|
|
2382
|
+
metadata: { tool: this._definition.name }
|
|
2383
|
+
});
|
|
2384
|
+
}
|
|
2385
|
+
if (!execute) {
|
|
2386
|
+
throw new IgniterAgentConfigError({
|
|
2387
|
+
message: "Tool execute handler is required before building",
|
|
2388
|
+
field: "execute",
|
|
2389
|
+
metadata: { tool: this._definition.name }
|
|
2390
|
+
});
|
|
2391
|
+
}
|
|
2392
|
+
return {
|
|
2393
|
+
name,
|
|
2394
|
+
description,
|
|
2395
|
+
inputSchema,
|
|
2396
|
+
outputSchema,
|
|
2397
|
+
execute,
|
|
2398
|
+
$Infer: {}
|
|
2399
|
+
};
|
|
2400
|
+
}
|
|
2401
|
+
};
|
|
2402
|
+
var IgniterAgentTool = {
|
|
2403
|
+
create: IgniterAgentToolBuilder.create
|
|
2404
|
+
};
|
|
2405
|
+
|
|
2406
|
+
// src/builders/mcp.builder.ts
|
|
2407
|
+
var IgniterAgentMCPBuilder = class _IgniterAgentMCPBuilder {
|
|
2408
|
+
/**
|
|
2409
|
+
* Creates a new IgniterAgentMCPBuilder instance.
|
|
2410
|
+
*
|
|
2411
|
+
* @param config - Initial configuration
|
|
2412
|
+
* @internal
|
|
2413
|
+
*/
|
|
2414
|
+
constructor(config = {}) {
|
|
2415
|
+
this._config = config;
|
|
2416
|
+
}
|
|
2417
|
+
/* ---------------------------------------------------------------------------
|
|
2418
|
+
* STATIC FACTORY METHODS
|
|
2419
|
+
* --------------------------------------------------------------------------- */
|
|
2420
|
+
/**
|
|
2421
|
+
* Creates a new MCP builder with the given name.
|
|
2422
|
+
*
|
|
2423
|
+
* @description
|
|
2424
|
+
* This is the primary entry point for creating an MCP configuration.
|
|
2425
|
+
* The name must be unique within an agent and is used to identify
|
|
2426
|
+
* the MCP server and its tools.
|
|
2427
|
+
*
|
|
2428
|
+
* After calling `create()`, you must call `withType()` to specify
|
|
2429
|
+
* the transport type before configuring transport-specific options.
|
|
2430
|
+
*
|
|
2431
|
+
* @typeParam TNewName - The configuration name type
|
|
2432
|
+
* @param name - Unique name for the MCP configuration
|
|
2433
|
+
* @returns A new IgniterAgentMCPBuilder instance
|
|
2434
|
+
*
|
|
2435
|
+
* @example
|
|
2436
|
+
* ```typescript
|
|
2437
|
+
* const mcpConfig = IgniterAgentMCPBuilder
|
|
2438
|
+
* .create('my-mcp')
|
|
2439
|
+
* .withType('stdio') // or 'http'
|
|
2440
|
+
* .withCommand('...') // available after withType('stdio')
|
|
2441
|
+
* .build();
|
|
2442
|
+
* ```
|
|
2443
|
+
*
|
|
2444
|
+
* @public
|
|
2445
|
+
*/
|
|
2446
|
+
static create(name) {
|
|
2447
|
+
if (!name || typeof name !== "string") {
|
|
2448
|
+
throw new IgniterAgentConfigError({
|
|
2449
|
+
message: "MCP configuration name is required and must be a non-empty string",
|
|
2450
|
+
field: "name"
|
|
2451
|
+
});
|
|
2452
|
+
}
|
|
2453
|
+
return new _IgniterAgentMCPBuilder({
|
|
2454
|
+
name
|
|
2455
|
+
});
|
|
2456
|
+
}
|
|
2457
|
+
/* ---------------------------------------------------------------------------
|
|
2458
|
+
* COMMON BUILDER METHODS
|
|
2459
|
+
* --------------------------------------------------------------------------- */
|
|
2460
|
+
/**
|
|
2461
|
+
* Sets the configuration name.
|
|
2462
|
+
*
|
|
2463
|
+
* @description
|
|
2464
|
+
* Allows changing the configuration name during the building process.
|
|
2465
|
+
*
|
|
2466
|
+
* @typeParam TNewName - The new name type
|
|
2467
|
+
* @param name - The new name for the configuration
|
|
2468
|
+
* @returns A new builder with the updated name
|
|
2469
|
+
*
|
|
2470
|
+
* @public
|
|
2471
|
+
*/
|
|
2472
|
+
withName(name) {
|
|
2473
|
+
return new _IgniterAgentMCPBuilder({
|
|
2474
|
+
...this._config,
|
|
2475
|
+
name
|
|
2476
|
+
});
|
|
2477
|
+
}
|
|
2478
|
+
/**
|
|
2479
|
+
* Sets the transport type for the MCP connection.
|
|
2480
|
+
*
|
|
2481
|
+
* @description
|
|
2482
|
+
* Specifies how the agent will communicate with the MCP server:
|
|
2483
|
+
* - `stdio`: Spawns a local process and communicates via stdin/stdout
|
|
2484
|
+
* - `http`: Connects to a remote server via HTTP/HTTPS
|
|
2485
|
+
*
|
|
2486
|
+
* After calling this method, transport-specific methods become available:
|
|
2487
|
+
* - For `stdio`: `withCommand()`, `withArgs()`, `withEnv()`
|
|
2488
|
+
* - For `http`: `withURL()`, `withHeaders()`
|
|
2489
|
+
*
|
|
2490
|
+
* @typeParam TNewType - The transport type
|
|
2491
|
+
* @param type - The transport type to use
|
|
2492
|
+
* @returns A new builder configured for the specified transport
|
|
2493
|
+
*
|
|
2494
|
+
* @example
|
|
2495
|
+
* ```typescript
|
|
2496
|
+
* // Stdio transport
|
|
2497
|
+
* const stdioConfig = IgniterAgentMCPBuilder
|
|
2498
|
+
* .create('local')
|
|
2499
|
+
* .withType('stdio')
|
|
2500
|
+
* .withCommand('python')
|
|
2501
|
+
* .withArgs(['mcp_server.py'])
|
|
2502
|
+
* .build();
|
|
2503
|
+
*
|
|
2504
|
+
* // HTTP transport
|
|
2505
|
+
* const httpConfig = IgniterAgentMCPBuilder
|
|
2506
|
+
* .create('remote')
|
|
2507
|
+
* .withType('http')
|
|
2508
|
+
* .withURL('https://api.example.com/mcp')
|
|
2509
|
+
* .build();
|
|
2510
|
+
* ```
|
|
2511
|
+
*
|
|
2512
|
+
* @public
|
|
2513
|
+
*/
|
|
2514
|
+
withType(type) {
|
|
2515
|
+
if (type !== "stdio" && type !== "http") {
|
|
2516
|
+
throw new IgniterAgentConfigError({
|
|
2517
|
+
message: `Invalid MCP transport type: '${type}'. Must be 'stdio' or 'http'`,
|
|
2518
|
+
field: "type",
|
|
2519
|
+
metadata: { validTypes: ["stdio", "http"] }
|
|
2520
|
+
});
|
|
2521
|
+
}
|
|
2522
|
+
return new _IgniterAgentMCPBuilder({
|
|
2523
|
+
...this._config,
|
|
2524
|
+
type
|
|
2525
|
+
});
|
|
2526
|
+
}
|
|
2527
|
+
/* ---------------------------------------------------------------------------
|
|
2528
|
+
* STDIO-SPECIFIC BUILDER METHODS
|
|
2529
|
+
* --------------------------------------------------------------------------- */
|
|
2530
|
+
/**
|
|
2531
|
+
* Sets the command to execute for stdio transport.
|
|
2532
|
+
*
|
|
2533
|
+
* @description
|
|
2534
|
+
* Specifies the executable to run when starting the MCP server process.
|
|
2535
|
+
* Common values include `npx`, `node`, `python`, `deno`, etc.
|
|
2536
|
+
*
|
|
2537
|
+
* @param command - The command to execute
|
|
2538
|
+
* @returns A new builder with the command set
|
|
2539
|
+
* @throws {IgniterAgentConfigError} If transport type is not 'stdio'
|
|
2540
|
+
*
|
|
2541
|
+
* @example
|
|
2542
|
+
* ```typescript
|
|
2543
|
+
* const config = IgniterAgentMCPBuilder
|
|
2544
|
+
* .create('filesystem')
|
|
2545
|
+
* .withType('stdio')
|
|
2546
|
+
* .withCommand('npx')
|
|
2547
|
+
* .withArgs(['-y', '@modelcontextprotocol/server-filesystem'])
|
|
2548
|
+
* .build();
|
|
2549
|
+
* ```
|
|
2550
|
+
*
|
|
2551
|
+
* @public
|
|
2552
|
+
*/
|
|
2553
|
+
withCommand(command) {
|
|
2554
|
+
this._assertStdioType("withCommand");
|
|
2555
|
+
if (!command || typeof command !== "string") {
|
|
2556
|
+
throw new IgniterAgentConfigError({
|
|
2557
|
+
message: "Command is required and must be a non-empty string",
|
|
2558
|
+
field: "command",
|
|
2559
|
+
metadata: { mcpName: this._config.name }
|
|
2560
|
+
});
|
|
2561
|
+
}
|
|
2562
|
+
return new _IgniterAgentMCPBuilder({
|
|
2563
|
+
...this._config,
|
|
2564
|
+
command
|
|
2565
|
+
});
|
|
2566
|
+
}
|
|
2567
|
+
/**
|
|
2568
|
+
* Sets the arguments to pass to the command.
|
|
2569
|
+
*
|
|
2570
|
+
* @description
|
|
2571
|
+
* Specifies command-line arguments for the MCP server process.
|
|
2572
|
+
*
|
|
2573
|
+
* @param args - Array of command arguments
|
|
2574
|
+
* @returns A new builder with the arguments set
|
|
2575
|
+
* @throws {IgniterAgentConfigError} If transport type is not 'stdio'
|
|
2576
|
+
*
|
|
2577
|
+
* @example
|
|
2578
|
+
* ```typescript
|
|
2579
|
+
* const config = IgniterAgentMCPBuilder
|
|
2580
|
+
* .create('filesystem')
|
|
2581
|
+
* .withType('stdio')
|
|
2582
|
+
* .withCommand('npx')
|
|
2583
|
+
* .withArgs([
|
|
2584
|
+
* '-y',
|
|
2585
|
+
* '@modelcontextprotocol/server-filesystem',
|
|
2586
|
+
* '/home/user/documents',
|
|
2587
|
+
* '--read-only'
|
|
2588
|
+
* ])
|
|
2589
|
+
* .build();
|
|
2590
|
+
* ```
|
|
2591
|
+
*
|
|
2592
|
+
* @public
|
|
2593
|
+
*/
|
|
2594
|
+
withArgs(args) {
|
|
2595
|
+
this._assertStdioType("withArgs");
|
|
2596
|
+
if (!Array.isArray(args)) {
|
|
2597
|
+
throw new IgniterAgentConfigError({
|
|
2598
|
+
message: "Args must be an array of strings",
|
|
2599
|
+
field: "args",
|
|
2600
|
+
metadata: { mcpName: this._config.name }
|
|
2601
|
+
});
|
|
2602
|
+
}
|
|
2603
|
+
return new _IgniterAgentMCPBuilder({
|
|
2604
|
+
...this._config,
|
|
2605
|
+
args
|
|
2606
|
+
});
|
|
2607
|
+
}
|
|
2608
|
+
/**
|
|
2609
|
+
* Sets environment variables for the command process.
|
|
2610
|
+
*
|
|
2611
|
+
* @description
|
|
2612
|
+
* Specifies environment variables to pass to the MCP server process.
|
|
2613
|
+
* Useful for passing API keys, configuration, or feature flags.
|
|
2614
|
+
*
|
|
2615
|
+
* @param env - Record of environment variable names to values
|
|
2616
|
+
* @returns A new builder with the environment variables set
|
|
2617
|
+
* @throws {IgniterAgentConfigError} If transport type is not 'stdio'
|
|
2618
|
+
*
|
|
2619
|
+
* @example
|
|
2620
|
+
* ```typescript
|
|
2621
|
+
* const config = IgniterAgentMCPBuilder
|
|
2622
|
+
* .create('github')
|
|
2623
|
+
* .withType('stdio')
|
|
2624
|
+
* .withCommand('npx')
|
|
2625
|
+
* .withArgs(['-y', '@modelcontextprotocol/server-github'])
|
|
2626
|
+
* .withEnv({
|
|
2627
|
+
* GITHUB_TOKEN: process.env.GITHUB_TOKEN!,
|
|
2628
|
+
* GITHUB_ORG: 'my-organization',
|
|
2629
|
+
* DEBUG: 'mcp:*'
|
|
2630
|
+
* })
|
|
2631
|
+
* .build();
|
|
2632
|
+
* ```
|
|
2633
|
+
*
|
|
2634
|
+
* @public
|
|
2635
|
+
*/
|
|
2636
|
+
withEnv(env) {
|
|
2637
|
+
this._assertStdioType("withEnv");
|
|
2638
|
+
if (!env || typeof env !== "object") {
|
|
2639
|
+
throw new IgniterAgentConfigError({
|
|
2640
|
+
message: "Env must be an object mapping variable names to values",
|
|
2641
|
+
field: "env",
|
|
2642
|
+
metadata: { mcpName: this._config.name }
|
|
2643
|
+
});
|
|
2644
|
+
}
|
|
2645
|
+
return new _IgniterAgentMCPBuilder({
|
|
2646
|
+
...this._config,
|
|
2647
|
+
env
|
|
2648
|
+
});
|
|
2649
|
+
}
|
|
2650
|
+
/* ---------------------------------------------------------------------------
|
|
2651
|
+
* HTTP-SPECIFIC BUILDER METHODS
|
|
2652
|
+
* --------------------------------------------------------------------------- */
|
|
2653
|
+
/**
|
|
2654
|
+
* Sets the URL for HTTP transport.
|
|
2655
|
+
*
|
|
2656
|
+
* @description
|
|
2657
|
+
* Specifies the endpoint URL for the remote MCP server.
|
|
2658
|
+
* Must be a valid HTTP or HTTPS URL.
|
|
2659
|
+
*
|
|
2660
|
+
* @param url - The MCP server URL
|
|
2661
|
+
* @returns A new builder with the URL set
|
|
2662
|
+
* @throws {IgniterAgentConfigError} If transport type is not 'http' or URL is invalid
|
|
2663
|
+
*
|
|
2664
|
+
* @example
|
|
2665
|
+
* ```typescript
|
|
2666
|
+
* const config = IgniterAgentMCPBuilder
|
|
2667
|
+
* .create('opencode')
|
|
2668
|
+
* .withType('http')
|
|
2669
|
+
* .withURL('https://sandbox.example.com/mcp/v1')
|
|
2670
|
+
* .build();
|
|
2671
|
+
* ```
|
|
2672
|
+
*
|
|
2673
|
+
* @public
|
|
2674
|
+
*/
|
|
2675
|
+
withURL(url) {
|
|
2676
|
+
this._assertHttpType("withURL");
|
|
2677
|
+
if (!url || typeof url !== "string") {
|
|
2678
|
+
throw new IgniterAgentConfigError({
|
|
2679
|
+
message: "URL is required and must be a non-empty string",
|
|
2680
|
+
field: "url",
|
|
2681
|
+
metadata: { mcpName: this._config.name }
|
|
2682
|
+
});
|
|
2683
|
+
}
|
|
2684
|
+
try {
|
|
2685
|
+
new URL(url);
|
|
2686
|
+
} catch {
|
|
2687
|
+
throw new IgniterAgentConfigError({
|
|
2688
|
+
message: `Invalid URL format: '${url}'`,
|
|
2689
|
+
field: "url",
|
|
2690
|
+
metadata: { mcpName: this._config.name }
|
|
2691
|
+
});
|
|
2692
|
+
}
|
|
2693
|
+
return new _IgniterAgentMCPBuilder({
|
|
2694
|
+
...this._config,
|
|
2695
|
+
url
|
|
2696
|
+
});
|
|
2697
|
+
}
|
|
2698
|
+
/**
|
|
2699
|
+
* Sets HTTP headers for requests to the MCP server.
|
|
2700
|
+
*
|
|
2701
|
+
* @description
|
|
2702
|
+
* Specifies headers to include in all HTTP requests to the MCP server.
|
|
2703
|
+
* Commonly used for authentication, API keys, or content type.
|
|
2704
|
+
*
|
|
2705
|
+
* @param headers - Record of header names to values
|
|
2706
|
+
* @returns A new builder with the headers set
|
|
2707
|
+
* @throws {IgniterAgentConfigError} If transport type is not 'http'
|
|
2708
|
+
*
|
|
2709
|
+
* @example
|
|
2710
|
+
* ```typescript
|
|
2711
|
+
* const config = IgniterAgentMCPBuilder
|
|
2712
|
+
* .create('authenticated-mcp')
|
|
2713
|
+
* .withType('http')
|
|
2714
|
+
* .withURL('https://api.example.com/mcp')
|
|
2715
|
+
* .withHeaders({
|
|
2716
|
+
* 'Authorization': `Bearer ${process.env.API_TOKEN}`,
|
|
2717
|
+
* 'X-Client-ID': 'my-app',
|
|
2718
|
+
* 'Content-Type': 'application/json'
|
|
2719
|
+
* })
|
|
2720
|
+
* .build();
|
|
2721
|
+
* ```
|
|
2722
|
+
*
|
|
2723
|
+
* @public
|
|
2724
|
+
*/
|
|
2725
|
+
withHeaders(headers) {
|
|
2726
|
+
this._assertHttpType("withHeaders");
|
|
2727
|
+
if (!headers || typeof headers !== "object") {
|
|
2728
|
+
throw new IgniterAgentConfigError({
|
|
2729
|
+
message: "Headers must be an object mapping header names to values",
|
|
2730
|
+
field: "headers",
|
|
2731
|
+
metadata: { mcpName: this._config.name }
|
|
2732
|
+
});
|
|
2733
|
+
}
|
|
2734
|
+
return new _IgniterAgentMCPBuilder({
|
|
2735
|
+
...this._config,
|
|
2736
|
+
headers
|
|
2737
|
+
});
|
|
2738
|
+
}
|
|
2739
|
+
/* ---------------------------------------------------------------------------
|
|
2740
|
+
* BUILD METHOD
|
|
2741
|
+
* --------------------------------------------------------------------------- */
|
|
2742
|
+
/**
|
|
2743
|
+
* Builds and returns the completed MCP configuration.
|
|
2744
|
+
*
|
|
2745
|
+
* @description
|
|
2746
|
+
* Validates the configuration and returns a typed configuration object
|
|
2747
|
+
* that can be registered with an agent using `addMCP()`.
|
|
2748
|
+
*
|
|
2749
|
+
* @returns The completed MCP configuration
|
|
2750
|
+
* @throws {IgniterAgentConfigError} If required fields are missing
|
|
2751
|
+
*
|
|
2752
|
+
* @example
|
|
2753
|
+
* ```typescript
|
|
2754
|
+
* const mcpConfig = IgniterAgentMCPBuilder
|
|
2755
|
+
* .create('filesystem')
|
|
2756
|
+
* .withType('stdio')
|
|
2757
|
+
* .withCommand('npx')
|
|
2758
|
+
* .withArgs(['-y', '@modelcontextprotocol/server-filesystem', '/tmp'])
|
|
2759
|
+
* .build();
|
|
2760
|
+
*
|
|
2761
|
+
* // Use with an agent
|
|
2762
|
+
* const agent = IgniterAgent.create()
|
|
2763
|
+
* .addMCP(mcpConfig)
|
|
2764
|
+
* .build();
|
|
2765
|
+
* ```
|
|
2766
|
+
*
|
|
2767
|
+
* @public
|
|
2768
|
+
*/
|
|
2769
|
+
build() {
|
|
2770
|
+
if (!this._config.name) {
|
|
2771
|
+
throw new IgniterAgentConfigError({
|
|
2772
|
+
message: "MCP configuration name is required",
|
|
2773
|
+
field: "name"
|
|
2774
|
+
});
|
|
2775
|
+
}
|
|
2776
|
+
if (!this._config.type) {
|
|
2777
|
+
throw new IgniterAgentConfigError({
|
|
2778
|
+
message: 'MCP transport type is required. Call withType("stdio") or withType("http")',
|
|
2779
|
+
field: "type",
|
|
2780
|
+
metadata: { mcpName: this._config.name }
|
|
2781
|
+
});
|
|
2782
|
+
}
|
|
2783
|
+
if (this._config.type === "stdio") {
|
|
2784
|
+
const config = this._config;
|
|
2785
|
+
if (!config.command) {
|
|
2786
|
+
throw new IgniterAgentConfigError({
|
|
2787
|
+
message: "Command is required for stdio transport",
|
|
2788
|
+
field: "command",
|
|
2789
|
+
metadata: { mcpName: this._config.name }
|
|
2790
|
+
});
|
|
2791
|
+
}
|
|
2792
|
+
if (!config.args) {
|
|
2793
|
+
throw new IgniterAgentConfigError({
|
|
2794
|
+
message: "Args are required for stdio transport",
|
|
2795
|
+
field: "args",
|
|
2796
|
+
metadata: { mcpName: this._config.name }
|
|
2797
|
+
});
|
|
2798
|
+
}
|
|
2799
|
+
return {
|
|
2800
|
+
type: "stdio",
|
|
2801
|
+
name: config.name,
|
|
2802
|
+
command: config.command,
|
|
2803
|
+
args: config.args,
|
|
2804
|
+
env: config.env
|
|
2805
|
+
};
|
|
2806
|
+
}
|
|
2807
|
+
if (this._config.type === "http") {
|
|
2808
|
+
const config = this._config;
|
|
2809
|
+
if (!config.url) {
|
|
2810
|
+
throw new IgniterAgentConfigError({
|
|
2811
|
+
message: "URL is required for http transport",
|
|
2812
|
+
field: "url",
|
|
2813
|
+
metadata: { mcpName: this._config.name }
|
|
2814
|
+
});
|
|
2815
|
+
}
|
|
2816
|
+
return {
|
|
2817
|
+
type: "http",
|
|
2818
|
+
name: config.name,
|
|
2819
|
+
url: config.url,
|
|
2820
|
+
headers: config.headers
|
|
2821
|
+
};
|
|
2822
|
+
}
|
|
2823
|
+
throw new IgniterAgentConfigError({
|
|
2824
|
+
message: `Invalid transport type: '${this._config.type}'`,
|
|
2825
|
+
field: "type",
|
|
2826
|
+
metadata: { mcpName: this._config.name }
|
|
2827
|
+
});
|
|
2828
|
+
}
|
|
2829
|
+
/* ---------------------------------------------------------------------------
|
|
2830
|
+
* PRIVATE HELPER METHODS
|
|
2831
|
+
* --------------------------------------------------------------------------- */
|
|
2832
|
+
/**
|
|
2833
|
+
* Asserts that the current transport type is 'stdio'.
|
|
2834
|
+
* @internal
|
|
2835
|
+
*/
|
|
2836
|
+
_assertStdioType(methodName) {
|
|
2837
|
+
if (this._config.type !== "stdio") {
|
|
2838
|
+
throw new IgniterAgentConfigError({
|
|
2839
|
+
message: `${methodName}() is only available for stdio transport. Current type: '${this._config.type || "not set"}'. Call withType('stdio') first.`,
|
|
2840
|
+
field: "type",
|
|
2841
|
+
metadata: { mcpName: this._config.name, operation: methodName }
|
|
2842
|
+
});
|
|
2843
|
+
}
|
|
2844
|
+
}
|
|
2845
|
+
/**
|
|
2846
|
+
* Asserts that the current transport type is 'http'.
|
|
2847
|
+
* @internal
|
|
2848
|
+
*/
|
|
2849
|
+
_assertHttpType(methodName) {
|
|
2850
|
+
if (this._config.type !== "http") {
|
|
2851
|
+
throw new IgniterAgentConfigError({
|
|
2852
|
+
message: `${methodName}() is only available for http transport. Current type: '${this._config.type || "not set"}'. Call withType('http') first.`,
|
|
2853
|
+
field: "type",
|
|
2854
|
+
metadata: { mcpName: this._config.name, operation: methodName }
|
|
2855
|
+
});
|
|
2856
|
+
}
|
|
2857
|
+
}
|
|
2858
|
+
};
|
|
2859
|
+
var IgniterAgentMCPClient = {
|
|
2860
|
+
create: IgniterAgentMCPBuilder.create
|
|
2861
|
+
};
|
|
2862
|
+
|
|
2863
|
+
// src/adapters/memory.adapter.ts
|
|
2864
|
+
var IgniterAgentInMemoryAdapter = class _IgniterAgentInMemoryAdapter {
|
|
2865
|
+
/**
|
|
2866
|
+
* Creates a new IgniterAgentInMemoryAdapter.
|
|
2867
|
+
*
|
|
2868
|
+
* @param options - Adapter configuration
|
|
2869
|
+
*
|
|
2870
|
+
* @example
|
|
2871
|
+
* ```typescript
|
|
2872
|
+
* const adapter = new IgniterAgentInMemoryAdapter({
|
|
2873
|
+
* namespace: 'myapp',
|
|
2874
|
+
* maxMessages: 1000,
|
|
2875
|
+
* maxChats: 100
|
|
2876
|
+
* });
|
|
2877
|
+
* ```
|
|
2878
|
+
*/
|
|
2879
|
+
constructor(options = {}) {
|
|
2880
|
+
/**
|
|
2881
|
+
* Storage for working memory.
|
|
2882
|
+
* Key format: `{namespace}:{scope}:{identifier}`
|
|
2883
|
+
* @internal
|
|
2884
|
+
*/
|
|
2885
|
+
this._workingMemory = /* @__PURE__ */ new Map();
|
|
2886
|
+
/**
|
|
2887
|
+
* Storage for conversation messages.
|
|
2888
|
+
* Key format: `{namespace}:{chatId}`
|
|
2889
|
+
* @internal
|
|
2890
|
+
*/
|
|
2891
|
+
this._messages = /* @__PURE__ */ new Map();
|
|
2892
|
+
/**
|
|
2893
|
+
* Storage for chat sessions.
|
|
2894
|
+
* Key format: `{namespace}:{chatId}`
|
|
2895
|
+
* @internal
|
|
2896
|
+
*/
|
|
2897
|
+
this._chats = /* @__PURE__ */ new Map();
|
|
2898
|
+
this.options = {
|
|
2899
|
+
namespace: options.namespace ?? "igniter",
|
|
2900
|
+
maxMessages: options.maxMessages ?? 1e3,
|
|
2901
|
+
maxChats: options.maxChats ?? 100
|
|
2902
|
+
};
|
|
2903
|
+
}
|
|
2904
|
+
/**
|
|
2905
|
+
* Factory method to create a new adapter instance.
|
|
2906
|
+
* @param options - Adapter configuration
|
|
2907
|
+
* @returns A new IgniterAgentInMemoryAdapter instance
|
|
2908
|
+
*
|
|
2909
|
+
* @example
|
|
2910
|
+
* ```typescript
|
|
2911
|
+
* const adapter = IgniterAgentInMemoryAdapter.create({
|
|
2912
|
+
* namespace: 'test',
|
|
2913
|
+
* });
|
|
2914
|
+
* ```
|
|
2915
|
+
*/
|
|
2916
|
+
static create(options) {
|
|
2917
|
+
return new _IgniterAgentInMemoryAdapter(options);
|
|
2918
|
+
}
|
|
2919
|
+
/* ---------------------------------------------------------------------------
|
|
2920
|
+
* HELPER METHODS
|
|
2921
|
+
* --------------------------------------------------------------------------- */
|
|
2922
|
+
/**
|
|
2923
|
+
* Generates a storage key with namespace prefix.
|
|
2924
|
+
* @internal
|
|
2925
|
+
*/
|
|
2926
|
+
_key(...parts) {
|
|
2927
|
+
return [this.options.namespace, ...parts].join(":");
|
|
2928
|
+
}
|
|
2929
|
+
/* ---------------------------------------------------------------------------
|
|
2930
|
+
* LIFECYCLE METHODS
|
|
2931
|
+
* --------------------------------------------------------------------------- */
|
|
2932
|
+
/**
|
|
2933
|
+
* Connects to the storage backend (no-op for in-memory).
|
|
2934
|
+
*
|
|
2935
|
+
* @description
|
|
2936
|
+
* In-memory adapter doesn't require connection setup.
|
|
2937
|
+
* This method exists for interface compatibility.
|
|
2938
|
+
*/
|
|
2939
|
+
async connect() {
|
|
2940
|
+
}
|
|
2941
|
+
/**
|
|
2942
|
+
* Disconnects from the storage backend (no-op for in-memory).
|
|
2943
|
+
*
|
|
2944
|
+
* @description
|
|
2945
|
+
* In-memory adapter doesn't require disconnection.
|
|
2946
|
+
* This method exists for interface compatibility.
|
|
2947
|
+
*/
|
|
2948
|
+
async disconnect() {
|
|
2949
|
+
}
|
|
2950
|
+
/**
|
|
2951
|
+
* Checks if the adapter is connected.
|
|
2952
|
+
*
|
|
2953
|
+
* @returns Always true for in-memory adapter
|
|
2954
|
+
*/
|
|
2955
|
+
isConnected() {
|
|
2956
|
+
return true;
|
|
2957
|
+
}
|
|
2958
|
+
/**
|
|
2959
|
+
* Clears all stored data.
|
|
2960
|
+
*
|
|
2961
|
+
* @description
|
|
2962
|
+
* Removes all working memory, messages, and chat sessions.
|
|
2963
|
+
* Use with caution.
|
|
2964
|
+
*
|
|
2965
|
+
* @example
|
|
2966
|
+
* ```typescript
|
|
2967
|
+
* // Clear all data (useful for testing)
|
|
2968
|
+
* await adapter.clear();
|
|
2969
|
+
* ```
|
|
2970
|
+
*/
|
|
2971
|
+
async clear() {
|
|
2972
|
+
this._workingMemory.clear();
|
|
2973
|
+
this._messages.clear();
|
|
2974
|
+
this._chats.clear();
|
|
2975
|
+
}
|
|
2976
|
+
/* ---------------------------------------------------------------------------
|
|
2977
|
+
* WORKING MEMORY METHODS
|
|
2978
|
+
* --------------------------------------------------------------------------- */
|
|
2979
|
+
/**
|
|
2980
|
+
* Gets working memory for a scope and identifier.
|
|
2981
|
+
*
|
|
2982
|
+
* @param params - The scope and identifier
|
|
2983
|
+
* @returns The working memory or null if not found
|
|
2984
|
+
*
|
|
2985
|
+
* @example
|
|
2986
|
+
* ```typescript
|
|
2987
|
+
* const memory = await adapter.getWorkingMemory({
|
|
2988
|
+
* scope: 'chat',
|
|
2989
|
+
* identifier: 'chat_123'
|
|
2990
|
+
* });
|
|
2991
|
+
*
|
|
2992
|
+
* if (memory) {
|
|
2993
|
+
* console.log('Content:', memory.content);
|
|
2994
|
+
* console.log('Updated:', memory.updatedAt);
|
|
2995
|
+
* }
|
|
2996
|
+
* ```
|
|
2997
|
+
*/
|
|
2998
|
+
async getWorkingMemory(params) {
|
|
2999
|
+
const key = this._key("memory", params.scope, params.identifier);
|
|
3000
|
+
return this._workingMemory.get(key) ?? null;
|
|
3001
|
+
}
|
|
3002
|
+
/**
|
|
3003
|
+
* Updates working memory for a scope and identifier.
|
|
3004
|
+
*
|
|
3005
|
+
* @param params - The scope, identifier, and new content
|
|
3006
|
+
*
|
|
3007
|
+
* @example
|
|
3008
|
+
* ```typescript
|
|
3009
|
+
* await adapter.updateWorkingMemory({
|
|
3010
|
+
* scope: 'chat',
|
|
3011
|
+
* identifier: 'chat_123',
|
|
3012
|
+
* content: `
|
|
3013
|
+
* ## User Preferences
|
|
3014
|
+
* - Prefers TypeScript
|
|
3015
|
+
* - Uses VS Code
|
|
3016
|
+
* `
|
|
3017
|
+
* });
|
|
3018
|
+
* ```
|
|
3019
|
+
*/
|
|
3020
|
+
async updateWorkingMemory(params) {
|
|
3021
|
+
const key = this._key("memory", params.scope, params.identifier);
|
|
3022
|
+
this._workingMemory.set(key, {
|
|
3023
|
+
content: params.content,
|
|
3024
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
3025
|
+
});
|
|
3026
|
+
}
|
|
3027
|
+
/* ---------------------------------------------------------------------------
|
|
3028
|
+
* MESSAGE METHODS
|
|
3029
|
+
* --------------------------------------------------------------------------- */
|
|
3030
|
+
/**
|
|
3031
|
+
* Saves a message to the conversation history.
|
|
3032
|
+
*
|
|
3033
|
+
* @param message - The message to save
|
|
3034
|
+
*
|
|
3035
|
+
* @example
|
|
3036
|
+
* ```typescript
|
|
3037
|
+
* await adapter.saveMessage({
|
|
3038
|
+
* chatId: 'chat_123',
|
|
3039
|
+
* userId: 'user_456',
|
|
3040
|
+
* role: 'user',
|
|
3041
|
+
* content: 'How do I use TypeScript?',
|
|
3042
|
+
* timestamp: new Date()
|
|
3043
|
+
* });
|
|
3044
|
+
* ```
|
|
3045
|
+
*/
|
|
3046
|
+
async saveMessage(message) {
|
|
3047
|
+
const key = this._key("messages", message.chatId);
|
|
3048
|
+
let messages = this._messages.get(key);
|
|
3049
|
+
if (!messages) {
|
|
3050
|
+
messages = [];
|
|
3051
|
+
this._messages.set(key, messages);
|
|
3052
|
+
}
|
|
3053
|
+
messages.push(message);
|
|
3054
|
+
if (messages.length > this.options.maxMessages) {
|
|
3055
|
+
messages.splice(0, messages.length - this.options.maxMessages);
|
|
3056
|
+
}
|
|
3057
|
+
}
|
|
3058
|
+
/**
|
|
3059
|
+
* Gets messages from the conversation history.
|
|
3060
|
+
*
|
|
3061
|
+
* @typeParam T - The message type to return
|
|
3062
|
+
* @param params - Query parameters
|
|
3063
|
+
* @returns Array of messages
|
|
3064
|
+
*
|
|
3065
|
+
* @example
|
|
3066
|
+
* ```typescript
|
|
3067
|
+
* const messages = await adapter.getMessages({
|
|
3068
|
+
* chatId: 'chat_123',
|
|
3069
|
+
* limit: 50
|
|
3070
|
+
* });
|
|
3071
|
+
*
|
|
3072
|
+
* for (const msg of messages) {
|
|
3073
|
+
* console.log(`[${msg.role}]: ${msg.content}`);
|
|
3074
|
+
* }
|
|
3075
|
+
* ```
|
|
3076
|
+
*/
|
|
3077
|
+
async getMessages(params) {
|
|
3078
|
+
const key = this._key("messages", params.chatId);
|
|
3079
|
+
let messages = this._messages.get(key) ?? [];
|
|
3080
|
+
if (params.userId) {
|
|
3081
|
+
messages = messages.filter((m) => m.userId === params.userId);
|
|
3082
|
+
}
|
|
3083
|
+
if (params.limit && params.limit > 0) {
|
|
3084
|
+
messages = messages.slice(-params.limit);
|
|
3085
|
+
}
|
|
3086
|
+
return messages.map((m) => ({
|
|
3087
|
+
id: `${m.chatId}-${m.timestamp.getTime()}`,
|
|
3088
|
+
role: m.role,
|
|
3089
|
+
content: m.content,
|
|
3090
|
+
createdAt: m.timestamp
|
|
3091
|
+
}));
|
|
3092
|
+
}
|
|
3093
|
+
/* ---------------------------------------------------------------------------
|
|
3094
|
+
* CHAT SESSION METHODS
|
|
3095
|
+
* --------------------------------------------------------------------------- */
|
|
3096
|
+
/**
|
|
3097
|
+
* Saves or updates a chat session.
|
|
3098
|
+
*
|
|
3099
|
+
* @param chat - The chat session to save
|
|
3100
|
+
*
|
|
3101
|
+
* @example
|
|
3102
|
+
* ```typescript
|
|
3103
|
+
* await adapter.saveChat({
|
|
3104
|
+
* chatId: 'chat_123',
|
|
3105
|
+
* userId: 'user_456',
|
|
3106
|
+
* title: 'TypeScript Help',
|
|
3107
|
+
* createdAt: new Date(),
|
|
3108
|
+
* updatedAt: new Date(),
|
|
3109
|
+
* messageCount: 0
|
|
3110
|
+
* });
|
|
3111
|
+
* ```
|
|
3112
|
+
*/
|
|
3113
|
+
async saveChat(chat) {
|
|
3114
|
+
const key = this._key("chats", chat.chatId);
|
|
3115
|
+
this._chats.set(key, chat);
|
|
3116
|
+
if (this._chats.size > this.options.maxChats) {
|
|
3117
|
+
const entries = Array.from(this._chats.entries()).sort((a, b) => a[1].updatedAt.getTime() - b[1].updatedAt.getTime());
|
|
3118
|
+
const toRemove = entries.slice(0, this._chats.size - this.options.maxChats);
|
|
3119
|
+
for (const [k] of toRemove) {
|
|
3120
|
+
this._chats.delete(k);
|
|
3121
|
+
}
|
|
3122
|
+
}
|
|
3123
|
+
}
|
|
3124
|
+
/**
|
|
3125
|
+
* Gets chat sessions matching the query parameters.
|
|
3126
|
+
*
|
|
3127
|
+
* @param params - Query parameters
|
|
3128
|
+
* @returns Array of chat sessions
|
|
3129
|
+
*
|
|
3130
|
+
* @example
|
|
3131
|
+
* ```typescript
|
|
3132
|
+
* const chats = await adapter.getChats({
|
|
3133
|
+
* userId: 'user_456',
|
|
3134
|
+
* search: 'typescript',
|
|
3135
|
+
* limit: 10
|
|
3136
|
+
* });
|
|
3137
|
+
*
|
|
3138
|
+
* for (const chat of chats) {
|
|
3139
|
+
* console.log(`${chat.title} (${chat.messageCount} messages)`);
|
|
3140
|
+
* }
|
|
3141
|
+
* ```
|
|
3142
|
+
*/
|
|
3143
|
+
async getChats(params) {
|
|
3144
|
+
let chats = Array.from(this._chats.values());
|
|
3145
|
+
if (params.userId) {
|
|
3146
|
+
chats = chats.filter((c) => c.userId === params.userId);
|
|
3147
|
+
}
|
|
3148
|
+
if (params.search) {
|
|
3149
|
+
const searchLower = params.search.toLowerCase();
|
|
3150
|
+
chats = chats.filter(
|
|
3151
|
+
(c) => c.title?.toLowerCase().includes(searchLower)
|
|
3152
|
+
);
|
|
3153
|
+
}
|
|
3154
|
+
chats.sort((a, b) => b.updatedAt.getTime() - a.updatedAt.getTime());
|
|
3155
|
+
if (params.limit && params.limit > 0) {
|
|
3156
|
+
chats = chats.slice(0, params.limit);
|
|
3157
|
+
}
|
|
3158
|
+
return chats;
|
|
3159
|
+
}
|
|
3160
|
+
/**
|
|
3161
|
+
* Gets a specific chat session by ID.
|
|
3162
|
+
*
|
|
3163
|
+
* @param chatId - The chat ID
|
|
3164
|
+
* @returns The chat session or null if not found
|
|
3165
|
+
*
|
|
3166
|
+
* @example
|
|
3167
|
+
* ```typescript
|
|
3168
|
+
* const chat = await adapter.getChat('chat_123');
|
|
3169
|
+
*
|
|
3170
|
+
* if (chat) {
|
|
3171
|
+
* console.log('Title:', chat.title);
|
|
3172
|
+
* console.log('Messages:', chat.messageCount);
|
|
3173
|
+
* }
|
|
3174
|
+
* ```
|
|
3175
|
+
*/
|
|
3176
|
+
async getChat(chatId) {
|
|
3177
|
+
const key = this._key("chats", chatId);
|
|
3178
|
+
return this._chats.get(key) ?? null;
|
|
3179
|
+
}
|
|
3180
|
+
/**
|
|
3181
|
+
* Updates the title of a chat session.
|
|
3182
|
+
*
|
|
3183
|
+
* @param chatId - The chat ID
|
|
3184
|
+
* @param title - The new title
|
|
3185
|
+
*
|
|
3186
|
+
* @example
|
|
3187
|
+
* ```typescript
|
|
3188
|
+
* await adapter.updateChatTitle('chat_123', 'New Title');
|
|
3189
|
+
* ```
|
|
3190
|
+
*/
|
|
3191
|
+
async updateChatTitle(chatId, title) {
|
|
3192
|
+
const key = this._key("chats", chatId);
|
|
3193
|
+
const chat = this._chats.get(key);
|
|
3194
|
+
if (chat) {
|
|
3195
|
+
chat.title = title;
|
|
3196
|
+
chat.updatedAt = /* @__PURE__ */ new Date();
|
|
3197
|
+
}
|
|
3198
|
+
}
|
|
3199
|
+
/**
|
|
3200
|
+
* Deletes a chat session and its messages.
|
|
3201
|
+
*
|
|
3202
|
+
* @param chatId - The chat ID to delete
|
|
3203
|
+
*
|
|
3204
|
+
* @example
|
|
3205
|
+
* ```typescript
|
|
3206
|
+
* await adapter.deleteChat('chat_123');
|
|
3207
|
+
* ```
|
|
3208
|
+
*/
|
|
3209
|
+
async deleteChat(chatId) {
|
|
3210
|
+
const chatKey = this._key("chats", chatId);
|
|
3211
|
+
const messagesKey = this._key("messages", chatId);
|
|
3212
|
+
this._chats.delete(chatKey);
|
|
3213
|
+
this._messages.delete(messagesKey);
|
|
3214
|
+
}
|
|
3215
|
+
/* ---------------------------------------------------------------------------
|
|
3216
|
+
* STATS METHODS
|
|
3217
|
+
* --------------------------------------------------------------------------- */
|
|
3218
|
+
/**
|
|
3219
|
+
* Gets storage statistics.
|
|
3220
|
+
*
|
|
3221
|
+
* @returns Current storage stats
|
|
3222
|
+
*
|
|
3223
|
+
* @example
|
|
3224
|
+
* ```typescript
|
|
3225
|
+
* const stats = await adapter.getStats();
|
|
3226
|
+
* console.log(`Storing ${stats.messageCount} messages`);
|
|
3227
|
+
* console.log(`Across ${stats.chatCount} chats`);
|
|
3228
|
+
* ```
|
|
3229
|
+
*/
|
|
3230
|
+
async getStats() {
|
|
3231
|
+
let messageCount = 0;
|
|
3232
|
+
for (const messages of this._messages.values()) {
|
|
3233
|
+
messageCount += messages.length;
|
|
3234
|
+
}
|
|
3235
|
+
return {
|
|
3236
|
+
workingMemoryCount: this._workingMemory.size,
|
|
3237
|
+
messageCount,
|
|
3238
|
+
chatCount: this._chats.size,
|
|
3239
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
3240
|
+
};
|
|
3241
|
+
}
|
|
3242
|
+
};
|
|
3243
|
+
var IgniterAgentJSONFileAdapter = class _IgniterAgentJSONFileAdapter {
|
|
3244
|
+
/**
|
|
3245
|
+
* Creates a new IgniterAgentJSONFileAdapter.
|
|
3246
|
+
*
|
|
3247
|
+
* @param options - Adapter configuration
|
|
3248
|
+
*
|
|
3249
|
+
* @example
|
|
3250
|
+
* ```typescript
|
|
3251
|
+
* const adapter = new IgniterAgentJSONFileAdapter({
|
|
3252
|
+
* dataDir: './memory',
|
|
3253
|
+
* namespace: 'myapp'
|
|
3254
|
+
* });
|
|
3255
|
+
* ```
|
|
3256
|
+
*/
|
|
3257
|
+
constructor(options = {}) {
|
|
3258
|
+
/**
|
|
3259
|
+
* Whether the adapter is currently connected and ready to use.
|
|
3260
|
+
* @internal
|
|
3261
|
+
*/
|
|
3262
|
+
this._connected = false;
|
|
3263
|
+
/**
|
|
3264
|
+
* In-memory cache of working memory entries.
|
|
3265
|
+
* @internal
|
|
3266
|
+
*/
|
|
3267
|
+
this._workingMemoryCache = /* @__PURE__ */ new Map();
|
|
3268
|
+
/**
|
|
3269
|
+
* In-memory cache of messages.
|
|
3270
|
+
* @internal
|
|
3271
|
+
*/
|
|
3272
|
+
this._messagesCache = /* @__PURE__ */ new Map();
|
|
3273
|
+
/**
|
|
3274
|
+
* In-memory cache of chat sessions.
|
|
3275
|
+
* @internal
|
|
3276
|
+
*/
|
|
3277
|
+
this._chatsCache = /* @__PURE__ */ new Map();
|
|
3278
|
+
this.options = {
|
|
3279
|
+
dataDir: options.dataDir ?? "./igniter-agent-memory",
|
|
3280
|
+
namespace: options.namespace ?? "igniter",
|
|
3281
|
+
autoSync: options.autoSync ?? true,
|
|
3282
|
+
debug: options.debug ?? false,
|
|
3283
|
+
maxMessages: options.maxMessages ?? 1e3,
|
|
3284
|
+
maxChats: options.maxChats ?? 100
|
|
3285
|
+
};
|
|
3286
|
+
}
|
|
3287
|
+
/**
|
|
3288
|
+
* Factory method to create a new adapter instance.
|
|
3289
|
+
* @param options - Adapter configuration
|
|
3290
|
+
* @returns A new IgniterAgentJSONFileAdapter instance
|
|
3291
|
+
*
|
|
3292
|
+
* @example
|
|
3293
|
+
* ```typescript
|
|
3294
|
+
* const adapter = IgniterAgentJSONFileAdapter.create({
|
|
3295
|
+
* dataDir: './data',
|
|
3296
|
+
* });
|
|
3297
|
+
* ```
|
|
3298
|
+
*/
|
|
3299
|
+
static create(options) {
|
|
3300
|
+
return new _IgniterAgentJSONFileAdapter(options);
|
|
3301
|
+
}
|
|
3302
|
+
/* ---------------------------------------------------------------------------
|
|
3303
|
+
* PRIVATE HELPER METHODS
|
|
3304
|
+
* --------------------------------------------------------------------------- */
|
|
3305
|
+
/**
|
|
3306
|
+
* Logs debug messages if debug mode is enabled.
|
|
3307
|
+
* @internal
|
|
3308
|
+
*/
|
|
3309
|
+
_log(message, data) {
|
|
3310
|
+
if (this.options.debug) {
|
|
3311
|
+
console.log(`[IgniterAgentJSONFileAdapter] ${message}`, data ?? "");
|
|
3312
|
+
}
|
|
3313
|
+
}
|
|
3314
|
+
/**
|
|
3315
|
+
* Gets the path for the working memory JSON file.
|
|
3316
|
+
* @internal
|
|
3317
|
+
*/
|
|
3318
|
+
_getWorkingMemoryPath() {
|
|
3319
|
+
return path.join(this.options.dataDir, "working-memory.json");
|
|
3320
|
+
}
|
|
3321
|
+
/**
|
|
3322
|
+
* Gets the path for the chats JSON file.
|
|
3323
|
+
* @internal
|
|
3324
|
+
*/
|
|
3325
|
+
_getChatsPath() {
|
|
3326
|
+
return path.join(this.options.dataDir, "chats.json");
|
|
3327
|
+
}
|
|
3328
|
+
/**
|
|
3329
|
+
* Gets the directory for message files.
|
|
3330
|
+
* @internal
|
|
3331
|
+
*/
|
|
3332
|
+
_getMessagesDir() {
|
|
3333
|
+
return path.join(this.options.dataDir, "messages");
|
|
3334
|
+
}
|
|
3335
|
+
/**
|
|
3336
|
+
* Gets the path for a specific chat's messages JSON file.
|
|
3337
|
+
* @internal
|
|
3338
|
+
*/
|
|
3339
|
+
_getMessagesPath(chatId) {
|
|
3340
|
+
return path.join(this._getMessagesDir(), `${chatId}.json`);
|
|
3341
|
+
}
|
|
3342
|
+
/**
|
|
3343
|
+
* Generates a cache key with namespace prefix.
|
|
3344
|
+
* @internal
|
|
3345
|
+
*/
|
|
3346
|
+
_key(...parts) {
|
|
3347
|
+
return [this.options.namespace, ...parts].join(":");
|
|
3348
|
+
}
|
|
3349
|
+
/**
|
|
3350
|
+
* Loads working memory from disk.
|
|
3351
|
+
* @internal
|
|
3352
|
+
*/
|
|
3353
|
+
async _loadWorkingMemory() {
|
|
3354
|
+
try {
|
|
3355
|
+
const path = this._getWorkingMemoryPath();
|
|
3356
|
+
const content = await promises.readFile(path, "utf-8");
|
|
3357
|
+
const data = JSON.parse(content);
|
|
3358
|
+
this._workingMemoryCache.clear();
|
|
3359
|
+
for (const [key, value] of Object.entries(data)) {
|
|
3360
|
+
this._workingMemoryCache.set(key, {
|
|
3361
|
+
content: value.content,
|
|
3362
|
+
updatedAt: new Date(value.updatedAt)
|
|
3363
|
+
});
|
|
3364
|
+
}
|
|
3365
|
+
this._log("Loaded working memory", {
|
|
3366
|
+
entries: this._workingMemoryCache.size
|
|
3367
|
+
});
|
|
3368
|
+
} catch (err) {
|
|
3369
|
+
this._log("Working memory file not found, starting fresh");
|
|
3370
|
+
}
|
|
3371
|
+
}
|
|
3372
|
+
/**
|
|
3373
|
+
* Loads chats from disk.
|
|
3374
|
+
* @internal
|
|
3375
|
+
*/
|
|
3376
|
+
async _loadChats() {
|
|
3377
|
+
try {
|
|
3378
|
+
const path = this._getChatsPath();
|
|
3379
|
+
const content = await promises.readFile(path, "utf-8");
|
|
3380
|
+
const data = JSON.parse(content);
|
|
3381
|
+
this._chatsCache.clear();
|
|
3382
|
+
for (const [key, value] of Object.entries(data)) {
|
|
3383
|
+
const chat = value;
|
|
3384
|
+
this._chatsCache.set(key, {
|
|
3385
|
+
chatId: chat.chatId,
|
|
3386
|
+
userId: chat.userId,
|
|
3387
|
+
title: chat.title,
|
|
3388
|
+
createdAt: new Date(chat.createdAt),
|
|
3389
|
+
updatedAt: new Date(chat.updatedAt),
|
|
3390
|
+
messageCount: chat.messageCount
|
|
3391
|
+
});
|
|
3392
|
+
}
|
|
3393
|
+
this._log("Loaded chats", { entries: this._chatsCache.size });
|
|
3394
|
+
} catch (err) {
|
|
3395
|
+
this._log("Chats file not found, starting fresh");
|
|
3396
|
+
}
|
|
3397
|
+
}
|
|
3398
|
+
/**
|
|
3399
|
+
* Loads a specific chat's messages from disk.
|
|
3400
|
+
* @internal
|
|
3401
|
+
*/
|
|
3402
|
+
async _loadMessages(chatId) {
|
|
3403
|
+
try {
|
|
3404
|
+
const path = this._getMessagesPath(chatId);
|
|
3405
|
+
const content = await promises.readFile(path, "utf-8");
|
|
3406
|
+
const data = JSON.parse(content);
|
|
3407
|
+
const key = this._key("messages", chatId);
|
|
3408
|
+
const messages = data.map(
|
|
3409
|
+
(msg) => ({
|
|
3410
|
+
chatId: msg.chatId,
|
|
3411
|
+
userId: msg.userId,
|
|
3412
|
+
role: msg.role,
|
|
3413
|
+
content: msg.content,
|
|
3414
|
+
timestamp: new Date(msg.timestamp)
|
|
3415
|
+
})
|
|
3416
|
+
);
|
|
3417
|
+
this._messagesCache.set(key, messages);
|
|
3418
|
+
this._log("Loaded messages", { chatId, count: messages.length });
|
|
3419
|
+
} catch (err) {
|
|
3420
|
+
this._log("Messages file not found for chat", { chatId });
|
|
3421
|
+
}
|
|
3422
|
+
}
|
|
3423
|
+
/**
|
|
3424
|
+
* Saves working memory to disk.
|
|
3425
|
+
* @internal
|
|
3426
|
+
*/
|
|
3427
|
+
async _saveWorkingMemory() {
|
|
3428
|
+
const path = this._getWorkingMemoryPath();
|
|
3429
|
+
const data = {};
|
|
3430
|
+
for (const [key, value] of this._workingMemoryCache.entries()) {
|
|
3431
|
+
data[key] = {
|
|
3432
|
+
content: value.content,
|
|
3433
|
+
updatedAt: value.updatedAt.toISOString()
|
|
3434
|
+
};
|
|
3435
|
+
}
|
|
3436
|
+
await promises.writeFile(path, JSON.stringify(data, null, 2));
|
|
3437
|
+
this._log("Saved working memory");
|
|
3438
|
+
}
|
|
3439
|
+
/**
|
|
3440
|
+
* Saves chats to disk.
|
|
3441
|
+
* @internal
|
|
3442
|
+
*/
|
|
3443
|
+
async _saveChats() {
|
|
3444
|
+
const path = this._getChatsPath();
|
|
3445
|
+
const data = {};
|
|
3446
|
+
for (const [key, value] of this._chatsCache.entries()) {
|
|
3447
|
+
data[key] = {
|
|
3448
|
+
chatId: value.chatId,
|
|
3449
|
+
userId: value.userId,
|
|
3450
|
+
title: value.title,
|
|
3451
|
+
createdAt: value.createdAt.toISOString(),
|
|
3452
|
+
updatedAt: value.updatedAt.toISOString(),
|
|
3453
|
+
messageCount: value.messageCount
|
|
3454
|
+
};
|
|
3455
|
+
}
|
|
3456
|
+
await promises.writeFile(path, JSON.stringify(data, null, 2));
|
|
3457
|
+
this._log("Saved chats");
|
|
3458
|
+
}
|
|
3459
|
+
/**
|
|
3460
|
+
* Saves messages for a specific chat to disk.
|
|
3461
|
+
* @internal
|
|
3462
|
+
*/
|
|
3463
|
+
async _saveMessages(chatId) {
|
|
3464
|
+
const key = this._key("messages", chatId);
|
|
3465
|
+
const messages = this._messagesCache.get(key) ?? [];
|
|
3466
|
+
const path = this._getMessagesPath(chatId);
|
|
3467
|
+
const data = messages.map((msg) => ({
|
|
3468
|
+
chatId: msg.chatId,
|
|
3469
|
+
userId: msg.userId,
|
|
3470
|
+
role: msg.role,
|
|
3471
|
+
content: msg.content,
|
|
3472
|
+
timestamp: msg.timestamp.toISOString()
|
|
3473
|
+
}));
|
|
3474
|
+
await promises.writeFile(path, JSON.stringify(data, null, 2));
|
|
3475
|
+
this._log("Saved messages", { chatId, count: messages.length });
|
|
3476
|
+
}
|
|
3477
|
+
/* ---------------------------------------------------------------------------
|
|
3478
|
+
* LIFECYCLE METHODS
|
|
3479
|
+
* --------------------------------------------------------------------------- */
|
|
3480
|
+
/**
|
|
3481
|
+
* Connects to the storage backend and loads data from disk.
|
|
3482
|
+
*
|
|
3483
|
+
* @description
|
|
3484
|
+
* Creates necessary directories and loads all data into memory cache.
|
|
3485
|
+
* Must be called before using the adapter.
|
|
3486
|
+
*
|
|
3487
|
+
* @throws {Error} If directory creation fails
|
|
3488
|
+
*
|
|
3489
|
+
* @example
|
|
3490
|
+
* ```typescript
|
|
3491
|
+
* const adapter = new IgniterAgentJSONFileAdapter();
|
|
3492
|
+
* await adapter.connect();
|
|
3493
|
+
* ```
|
|
3494
|
+
*/
|
|
3495
|
+
async connect() {
|
|
3496
|
+
try {
|
|
3497
|
+
await promises.mkdir(this.options.dataDir, { recursive: true });
|
|
3498
|
+
await promises.mkdir(this._getMessagesDir(), { recursive: true });
|
|
3499
|
+
await this._loadWorkingMemory();
|
|
3500
|
+
await this._loadChats();
|
|
3501
|
+
this._connected = true;
|
|
3502
|
+
this._log("Connected successfully", {
|
|
3503
|
+
dataDir: this.options.dataDir
|
|
3504
|
+
});
|
|
3505
|
+
} catch (err) {
|
|
3506
|
+
this._log("Connection failed", err);
|
|
3507
|
+
throw err;
|
|
3508
|
+
}
|
|
3509
|
+
}
|
|
3510
|
+
/**
|
|
3511
|
+
* Disconnects from the storage backend.
|
|
3512
|
+
*
|
|
3513
|
+
* @description
|
|
3514
|
+
* Syncs any pending changes to disk before disconnecting.
|
|
3515
|
+
*
|
|
3516
|
+
* @example
|
|
3517
|
+
* ```typescript
|
|
3518
|
+
* await adapter.disconnect();
|
|
3519
|
+
* ```
|
|
3520
|
+
*/
|
|
3521
|
+
async disconnect() {
|
|
3522
|
+
if (this._connected) {
|
|
3523
|
+
await this.sync();
|
|
3524
|
+
this._connected = false;
|
|
3525
|
+
this._log("Disconnected");
|
|
3526
|
+
}
|
|
3527
|
+
}
|
|
3528
|
+
/**
|
|
3529
|
+
* Checks if the adapter is connected and ready to use.
|
|
3530
|
+
*
|
|
3531
|
+
* @returns True if connected, false otherwise
|
|
3532
|
+
*/
|
|
3533
|
+
isConnected() {
|
|
3534
|
+
return this._connected;
|
|
3535
|
+
}
|
|
3536
|
+
/**
|
|
3537
|
+
* Manually syncs all in-memory data to disk.
|
|
3538
|
+
*
|
|
3539
|
+
* @description
|
|
3540
|
+
* Called automatically if autoSync is enabled. Can be called manually
|
|
3541
|
+
* to ensure data is persisted to disk.
|
|
3542
|
+
*
|
|
3543
|
+
* @example
|
|
3544
|
+
* ```typescript
|
|
3545
|
+
* await adapter.updateWorkingMemory({ ... });
|
|
3546
|
+
* await adapter.sync(); // Ensure written to disk
|
|
3547
|
+
* ```
|
|
3548
|
+
*/
|
|
3549
|
+
async sync() {
|
|
3550
|
+
try {
|
|
3551
|
+
await this._saveWorkingMemory();
|
|
3552
|
+
await this._saveChats();
|
|
3553
|
+
for (const chatId of this._messagesCache.keys()) {
|
|
3554
|
+
const idParts = chatId.split(":");
|
|
3555
|
+
if (idParts.length > 0) {
|
|
3556
|
+
const actualChatId = idParts[idParts.length - 1];
|
|
3557
|
+
await this._saveMessages(actualChatId);
|
|
3558
|
+
}
|
|
3559
|
+
}
|
|
3560
|
+
this._log("Synced all data to disk");
|
|
3561
|
+
} catch (err) {
|
|
3562
|
+
this._log("Sync failed", err);
|
|
3563
|
+
throw err;
|
|
3564
|
+
}
|
|
3565
|
+
}
|
|
3566
|
+
/**
|
|
3567
|
+
* Clears all stored data from disk and memory.
|
|
3568
|
+
*
|
|
3569
|
+
* @description
|
|
3570
|
+
* Removes all working memory, messages, and chat sessions.
|
|
3571
|
+
* Use with caution.
|
|
3572
|
+
*
|
|
3573
|
+
* @example
|
|
3574
|
+
* ```typescript
|
|
3575
|
+
* await adapter.clear();
|
|
3576
|
+
* ```
|
|
3577
|
+
*/
|
|
3578
|
+
async clear() {
|
|
3579
|
+
this._workingMemoryCache.clear();
|
|
3580
|
+
this._messagesCache.clear();
|
|
3581
|
+
this._chatsCache.clear();
|
|
3582
|
+
if (this.options.autoSync) {
|
|
3583
|
+
await this.sync();
|
|
3584
|
+
}
|
|
3585
|
+
this._log("Cleared all data");
|
|
3586
|
+
}
|
|
3587
|
+
/* ---------------------------------------------------------------------------
|
|
3588
|
+
* WORKING MEMORY METHODS
|
|
3589
|
+
* --------------------------------------------------------------------------- */
|
|
3590
|
+
/**
|
|
3591
|
+
* Gets working memory for a scope and identifier.
|
|
3592
|
+
*
|
|
3593
|
+
* @param params - The scope and identifier
|
|
3594
|
+
* @returns The working memory or null if not found
|
|
3595
|
+
*
|
|
3596
|
+
* @example
|
|
3597
|
+
* ```typescript
|
|
3598
|
+
* const memory = await adapter.getWorkingMemory({
|
|
3599
|
+
* scope: 'chat',
|
|
3600
|
+
* identifier: 'chat_123'
|
|
3601
|
+
* });
|
|
3602
|
+
*
|
|
3603
|
+
* if (memory) {
|
|
3604
|
+
* console.log('Content:', memory.content);
|
|
3605
|
+
* }
|
|
3606
|
+
* ```
|
|
3607
|
+
*/
|
|
3608
|
+
async getWorkingMemory(params) {
|
|
3609
|
+
const key = this._key("memory", params.scope, params.identifier);
|
|
3610
|
+
return this._workingMemoryCache.get(key) ?? null;
|
|
3611
|
+
}
|
|
3612
|
+
/**
|
|
3613
|
+
* Updates working memory for a scope and identifier.
|
|
3614
|
+
*
|
|
3615
|
+
* @param params - The scope, identifier, and new content
|
|
3616
|
+
*
|
|
3617
|
+
* @example
|
|
3618
|
+
* ```typescript
|
|
3619
|
+
* await adapter.updateWorkingMemory({
|
|
3620
|
+
* scope: 'chat',
|
|
3621
|
+
* identifier: 'chat_123',
|
|
3622
|
+
* content: 'User prefers TypeScript'
|
|
3623
|
+
* });
|
|
3624
|
+
* ```
|
|
3625
|
+
*/
|
|
3626
|
+
async updateWorkingMemory(params) {
|
|
3627
|
+
const key = this._key("memory", params.scope, params.identifier);
|
|
3628
|
+
this._workingMemoryCache.set(key, {
|
|
3629
|
+
content: params.content,
|
|
3630
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
3631
|
+
});
|
|
3632
|
+
if (this.options.autoSync) {
|
|
3633
|
+
await this._saveWorkingMemory();
|
|
3634
|
+
}
|
|
3635
|
+
}
|
|
3636
|
+
/* ---------------------------------------------------------------------------
|
|
3637
|
+
* MESSAGE METHODS
|
|
3638
|
+
* --------------------------------------------------------------------------- */
|
|
3639
|
+
/**
|
|
3640
|
+
* Saves a message to the conversation history.
|
|
3641
|
+
*
|
|
3642
|
+
* @param message - The message to save
|
|
3643
|
+
*
|
|
3644
|
+
* @example
|
|
3645
|
+
* ```typescript
|
|
3646
|
+
* await adapter.saveMessage({
|
|
3647
|
+
* chatId: 'chat_123',
|
|
3648
|
+
* userId: 'user_456',
|
|
3649
|
+
* role: 'user',
|
|
3650
|
+
* content: 'How do I use TypeScript?',
|
|
3651
|
+
* timestamp: new Date()
|
|
3652
|
+
* });
|
|
3653
|
+
* ```
|
|
3654
|
+
*/
|
|
3655
|
+
async saveMessage(message) {
|
|
3656
|
+
const key = this._key("messages", message.chatId);
|
|
3657
|
+
let messages = this._messagesCache.get(key);
|
|
3658
|
+
if (!messages) {
|
|
3659
|
+
messages = [];
|
|
3660
|
+
this._messagesCache.set(key, messages);
|
|
3661
|
+
}
|
|
3662
|
+
messages.push(message);
|
|
3663
|
+
if (messages.length > this.options.maxMessages) {
|
|
3664
|
+
messages.splice(0, messages.length - this.options.maxMessages);
|
|
3665
|
+
}
|
|
3666
|
+
if (this.options.autoSync) {
|
|
3667
|
+
await this._saveMessages(message.chatId);
|
|
3668
|
+
}
|
|
3669
|
+
}
|
|
3670
|
+
/**
|
|
3671
|
+
* Gets messages from the conversation history.
|
|
3672
|
+
*
|
|
3673
|
+
* @typeParam T - The message type to return
|
|
3674
|
+
* @param params - Query parameters
|
|
3675
|
+
* @returns Array of messages
|
|
3676
|
+
*
|
|
3677
|
+
* @example
|
|
3678
|
+
* ```typescript
|
|
3679
|
+
* const messages = await adapter.getMessages({
|
|
3680
|
+
* chatId: 'chat_123',
|
|
3681
|
+
* limit: 50
|
|
3682
|
+
* });
|
|
3683
|
+
*
|
|
3684
|
+
* for (const msg of messages) {
|
|
3685
|
+
* console.log(`[${msg.role}]: ${msg.content}`);
|
|
3686
|
+
* }
|
|
3687
|
+
* ```
|
|
3688
|
+
*/
|
|
3689
|
+
async getMessages(params) {
|
|
3690
|
+
const key = this._key("messages", params.chatId);
|
|
3691
|
+
if (!this._messagesCache.has(key)) {
|
|
3692
|
+
await this._loadMessages(params.chatId);
|
|
3693
|
+
}
|
|
3694
|
+
let messages = this._messagesCache.get(key) ?? [];
|
|
3695
|
+
if (params.userId) {
|
|
3696
|
+
messages = messages.filter((m) => m.userId === params.userId);
|
|
3697
|
+
}
|
|
3698
|
+
if (params.limit && params.limit > 0) {
|
|
3699
|
+
messages = messages.slice(-params.limit);
|
|
3700
|
+
}
|
|
3701
|
+
return messages.map((m) => ({
|
|
3702
|
+
id: `${m.chatId}-${m.timestamp.getTime()}`,
|
|
3703
|
+
role: m.role,
|
|
3704
|
+
content: m.content,
|
|
3705
|
+
createdAt: m.timestamp
|
|
3706
|
+
}));
|
|
3707
|
+
}
|
|
3708
|
+
/* ---------------------------------------------------------------------------
|
|
3709
|
+
* CHAT SESSION METHODS
|
|
3710
|
+
* --------------------------------------------------------------------------- */
|
|
3711
|
+
/**
|
|
3712
|
+
* Saves or updates a chat session.
|
|
3713
|
+
*
|
|
3714
|
+
* @param chat - The chat session to save
|
|
3715
|
+
*
|
|
3716
|
+
* @example
|
|
3717
|
+
* ```typescript
|
|
3718
|
+
* await adapter.saveChat({
|
|
3719
|
+
* chatId: 'chat_123',
|
|
3720
|
+
* userId: 'user_456',
|
|
3721
|
+
* title: 'TypeScript Help',
|
|
3722
|
+
* createdAt: new Date(),
|
|
3723
|
+
* updatedAt: new Date(),
|
|
3724
|
+
* messageCount: 0
|
|
3725
|
+
* });
|
|
3726
|
+
* ```
|
|
3727
|
+
*/
|
|
3728
|
+
async saveChat(chat) {
|
|
3729
|
+
const key = this._key("chats", chat.chatId);
|
|
3730
|
+
this._chatsCache.set(key, chat);
|
|
3731
|
+
if (this._chatsCache.size > this.options.maxChats) {
|
|
3732
|
+
const entries = Array.from(this._chatsCache.entries()).sort(
|
|
3733
|
+
(a, b) => a[1].updatedAt.getTime() - b[1].updatedAt.getTime()
|
|
3734
|
+
);
|
|
3735
|
+
const toRemove = entries.slice(
|
|
3736
|
+
0,
|
|
3737
|
+
this._chatsCache.size - this.options.maxChats
|
|
3738
|
+
);
|
|
3739
|
+
for (const [k] of toRemove) {
|
|
3740
|
+
this._chatsCache.delete(k);
|
|
3741
|
+
}
|
|
3742
|
+
}
|
|
3743
|
+
if (this.options.autoSync) {
|
|
3744
|
+
await this._saveChats();
|
|
3745
|
+
}
|
|
3746
|
+
}
|
|
3747
|
+
/**
|
|
3748
|
+
* Gets chat sessions matching the query parameters.
|
|
3749
|
+
*
|
|
3750
|
+
* @param params - Query parameters
|
|
3751
|
+
* @returns Array of chat sessions
|
|
3752
|
+
*
|
|
3753
|
+
* @example
|
|
3754
|
+
* ```typescript
|
|
3755
|
+
* const chats = await adapter.getChats({
|
|
3756
|
+
* userId: 'user_456',
|
|
3757
|
+
* search: 'typescript',
|
|
3758
|
+
* limit: 10
|
|
3759
|
+
* });
|
|
3760
|
+
*
|
|
3761
|
+
* for (const chat of chats) {
|
|
3762
|
+
* console.log(`${chat.title} (${chat.messageCount} messages)`);
|
|
3763
|
+
* }
|
|
3764
|
+
* ```
|
|
3765
|
+
*/
|
|
3766
|
+
async getChats(params) {
|
|
3767
|
+
let chats = Array.from(this._chatsCache.values());
|
|
3768
|
+
if (params.userId) {
|
|
3769
|
+
chats = chats.filter((c) => c.userId === params.userId);
|
|
3770
|
+
}
|
|
3771
|
+
if (params.search) {
|
|
3772
|
+
const searchLower = params.search.toLowerCase();
|
|
3773
|
+
chats = chats.filter(
|
|
3774
|
+
(c) => c.title?.toLowerCase().includes(searchLower)
|
|
3775
|
+
);
|
|
3776
|
+
}
|
|
3777
|
+
chats.sort((a, b) => b.updatedAt.getTime() - a.updatedAt.getTime());
|
|
3778
|
+
if (params.limit && params.limit > 0) {
|
|
3779
|
+
chats = chats.slice(0, params.limit);
|
|
3780
|
+
}
|
|
3781
|
+
return chats;
|
|
3782
|
+
}
|
|
3783
|
+
/**
|
|
3784
|
+
* Gets a specific chat session by ID.
|
|
3785
|
+
*
|
|
3786
|
+
* @param chatId - The chat ID
|
|
3787
|
+
* @returns The chat session or null if not found
|
|
3788
|
+
*
|
|
3789
|
+
* @example
|
|
3790
|
+
* ```typescript
|
|
3791
|
+
* const chat = await adapter.getChat('chat_123');
|
|
3792
|
+
*
|
|
3793
|
+
* if (chat) {
|
|
3794
|
+
* console.log('Title:', chat.title);
|
|
3795
|
+
* }
|
|
3796
|
+
* ```
|
|
3797
|
+
*/
|
|
3798
|
+
async getChat(chatId) {
|
|
3799
|
+
const key = this._key("chats", chatId);
|
|
3800
|
+
return this._chatsCache.get(key) ?? null;
|
|
3801
|
+
}
|
|
3802
|
+
/**
|
|
3803
|
+
* Updates the title of a chat session.
|
|
3804
|
+
*
|
|
3805
|
+
* @param chatId - The chat ID
|
|
3806
|
+
* @param title - The new title
|
|
3807
|
+
*
|
|
3808
|
+
* @example
|
|
3809
|
+
* ```typescript
|
|
3810
|
+
* await adapter.updateChatTitle('chat_123', 'New Title');
|
|
3811
|
+
* ```
|
|
3812
|
+
*/
|
|
3813
|
+
async updateChatTitle(chatId, title) {
|
|
3814
|
+
const key = this._key("chats", chatId);
|
|
3815
|
+
const chat = this._chatsCache.get(key);
|
|
3816
|
+
if (chat) {
|
|
3817
|
+
chat.title = title;
|
|
3818
|
+
chat.updatedAt = /* @__PURE__ */ new Date();
|
|
3819
|
+
if (this.options.autoSync) {
|
|
3820
|
+
await this._saveChats();
|
|
3821
|
+
}
|
|
3822
|
+
}
|
|
3823
|
+
}
|
|
3824
|
+
/**
|
|
3825
|
+
* Deletes a chat session and its messages.
|
|
3826
|
+
*
|
|
3827
|
+
* @param chatId - The chat ID to delete
|
|
3828
|
+
*
|
|
3829
|
+
* @example
|
|
3830
|
+
* ```typescript
|
|
3831
|
+
* await adapter.deleteChat('chat_123');
|
|
3832
|
+
* ```
|
|
3833
|
+
*/
|
|
3834
|
+
async deleteChat(chatId) {
|
|
3835
|
+
const chatKey = this._key("chats", chatId);
|
|
3836
|
+
const messagesKey = this._key("messages", chatId);
|
|
3837
|
+
this._chatsCache.delete(chatKey);
|
|
3838
|
+
this._messagesCache.delete(messagesKey);
|
|
3839
|
+
if (this.options.autoSync) {
|
|
3840
|
+
await this._saveChats();
|
|
3841
|
+
try {
|
|
3842
|
+
const path = this._getMessagesPath(chatId);
|
|
3843
|
+
await promises.readFile(path);
|
|
3844
|
+
this._log("Messages file for deleted chat still exists");
|
|
3845
|
+
} catch {
|
|
3846
|
+
}
|
|
3847
|
+
}
|
|
3848
|
+
}
|
|
3849
|
+
/* ---------------------------------------------------------------------------
|
|
3850
|
+
* STATS METHODS
|
|
3851
|
+
* --------------------------------------------------------------------------- */
|
|
3852
|
+
/**
|
|
3853
|
+
* Gets storage statistics.
|
|
3854
|
+
*
|
|
3855
|
+
* @returns Current storage stats
|
|
3856
|
+
*
|
|
3857
|
+
* @example
|
|
3858
|
+
* ```typescript
|
|
3859
|
+
* const stats = await adapter.getStats();
|
|
3860
|
+
* console.log(`Storing ${stats.messageCount} messages`);
|
|
3861
|
+
* console.log(`Across ${stats.chatCount} chats`);
|
|
3862
|
+
* ```
|
|
3863
|
+
*/
|
|
3864
|
+
async getStats() {
|
|
3865
|
+
let messageCount = 0;
|
|
3866
|
+
for (const messages of this._messagesCache.values()) {
|
|
3867
|
+
messageCount += messages.length;
|
|
3868
|
+
}
|
|
3869
|
+
return {
|
|
3870
|
+
workingMemoryCount: this._workingMemoryCache.size,
|
|
3871
|
+
messageCount,
|
|
3872
|
+
chatCount: this._chatsCache.size,
|
|
3873
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
3874
|
+
};
|
|
3875
|
+
}
|
|
3876
|
+
};
|
|
3877
|
+
|
|
3878
|
+
// src/utils/strings.ts
|
|
3879
|
+
var IgniterAgentStringUtils = class {
|
|
3880
|
+
/**
|
|
3881
|
+
* Generates a unique identifier.
|
|
3882
|
+
*/
|
|
3883
|
+
static generateId(prefix) {
|
|
3884
|
+
const random = Math.random().toString(36).substring(2, 12);
|
|
3885
|
+
return prefix ? `${prefix}_${random}` : random;
|
|
3886
|
+
}
|
|
3887
|
+
/**
|
|
3888
|
+
* Truncates a string to a maximum length.
|
|
3889
|
+
*/
|
|
3890
|
+
static truncate(str, maxLength, suffix = "...") {
|
|
3891
|
+
if (str.length <= maxLength) return str;
|
|
3892
|
+
return str.slice(0, maxLength - suffix.length) + suffix;
|
|
3893
|
+
}
|
|
3894
|
+
/**
|
|
3895
|
+
* Converts a string to snake_case.
|
|
3896
|
+
*/
|
|
3897
|
+
static toSnakeCase(str) {
|
|
3898
|
+
return str.replace(/([A-Z])/g, "_$1").toLowerCase().replace(/^_/, "");
|
|
3899
|
+
}
|
|
3900
|
+
/**
|
|
3901
|
+
* Converts a string to camelCase.
|
|
3902
|
+
*/
|
|
3903
|
+
static toCamelCase(str) {
|
|
3904
|
+
return str.replace(/[-_](.)/g, (_, char) => char.toUpperCase()).replace(/^(.)/, (char) => char.toLowerCase());
|
|
3905
|
+
}
|
|
3906
|
+
};
|
|
3907
|
+
|
|
3908
|
+
// src/utils/objects.ts
|
|
3909
|
+
var IgniterAgentObjectUtils = class _IgniterAgentObjectUtils {
|
|
3910
|
+
/**
|
|
3911
|
+
* Deep merges two objects.
|
|
3912
|
+
*/
|
|
3913
|
+
static deepMerge(target, source) {
|
|
3914
|
+
const result = { ...target };
|
|
3915
|
+
for (const key in source) {
|
|
3916
|
+
const sourceValue = source[key];
|
|
3917
|
+
const targetValue = target[key];
|
|
3918
|
+
if (sourceValue !== null && typeof sourceValue === "object" && !Array.isArray(sourceValue) && targetValue !== null && typeof targetValue === "object" && !Array.isArray(targetValue)) {
|
|
3919
|
+
result[key] = _IgniterAgentObjectUtils.deepMerge(
|
|
3920
|
+
targetValue,
|
|
3921
|
+
sourceValue
|
|
3922
|
+
);
|
|
3923
|
+
} else {
|
|
3924
|
+
result[key] = sourceValue;
|
|
3925
|
+
}
|
|
3926
|
+
}
|
|
3927
|
+
return result;
|
|
3928
|
+
}
|
|
3929
|
+
/**
|
|
3930
|
+
* Picks specified keys from an object.
|
|
3931
|
+
*/
|
|
3932
|
+
static pick(obj, keys) {
|
|
3933
|
+
const result = {};
|
|
3934
|
+
for (const key of keys) {
|
|
3935
|
+
if (key in obj) {
|
|
3936
|
+
result[key] = obj[key];
|
|
3937
|
+
}
|
|
3938
|
+
}
|
|
3939
|
+
return result;
|
|
3940
|
+
}
|
|
3941
|
+
/**
|
|
3942
|
+
* Omits specified keys from an object.
|
|
3943
|
+
*/
|
|
3944
|
+
static omit(obj, keys) {
|
|
3945
|
+
const result = { ...obj };
|
|
3946
|
+
for (const key of keys) {
|
|
3947
|
+
delete result[key];
|
|
3948
|
+
}
|
|
3949
|
+
return result;
|
|
3950
|
+
}
|
|
3951
|
+
};
|
|
3952
|
+
|
|
3953
|
+
// src/utils/async.ts
|
|
3954
|
+
var IgniterAgentAsyncUtils = class _IgniterAgentAsyncUtils {
|
|
3955
|
+
/**
|
|
3956
|
+
* Delays execution for a specified duration.
|
|
3957
|
+
*/
|
|
3958
|
+
static delay(ms) {
|
|
3959
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
3960
|
+
}
|
|
3961
|
+
/**
|
|
3962
|
+
* Retries an async function with exponential backoff.
|
|
3963
|
+
*/
|
|
3964
|
+
static async retry(fn, options = {}) {
|
|
3965
|
+
const {
|
|
3966
|
+
maxAttempts = 3,
|
|
3967
|
+
baseDelay = 1e3,
|
|
3968
|
+
maxDelay = 3e4,
|
|
3969
|
+
onRetry
|
|
3970
|
+
} = options;
|
|
3971
|
+
let lastError;
|
|
3972
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
3973
|
+
try {
|
|
3974
|
+
return await fn();
|
|
3975
|
+
} catch (error) {
|
|
3976
|
+
lastError = error instanceof Error ? error : new Error(String(error));
|
|
3977
|
+
if (attempt === maxAttempts) {
|
|
3978
|
+
throw lastError;
|
|
3979
|
+
}
|
|
3980
|
+
onRetry?.(attempt, lastError);
|
|
3981
|
+
const delayMs = Math.min(baseDelay * Math.pow(2, attempt - 1), maxDelay);
|
|
3982
|
+
await _IgniterAgentAsyncUtils.delay(delayMs);
|
|
3983
|
+
}
|
|
3984
|
+
}
|
|
3985
|
+
throw lastError;
|
|
3986
|
+
}
|
|
3987
|
+
};
|
|
3988
|
+
|
|
3989
|
+
// src/utils/validation.ts
|
|
3990
|
+
var IgniterAgentValidationUtils = class {
|
|
3991
|
+
/**
|
|
3992
|
+
* Checks if a value is defined (not null or undefined).
|
|
3993
|
+
*/
|
|
3994
|
+
static isDefined(value) {
|
|
3995
|
+
return value !== null && value !== void 0;
|
|
3996
|
+
}
|
|
3997
|
+
/**
|
|
3998
|
+
* Checks if a value is a non-empty string.
|
|
3999
|
+
*/
|
|
4000
|
+
static isNonEmptyString(value) {
|
|
4001
|
+
return typeof value === "string" && value.length > 0;
|
|
4002
|
+
}
|
|
4003
|
+
/**
|
|
4004
|
+
* Checks if a value is a plain object.
|
|
4005
|
+
*/
|
|
4006
|
+
static isPlainObject(value) {
|
|
4007
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) && Object.getPrototypeOf(value) === Object.prototype;
|
|
4008
|
+
}
|
|
4009
|
+
};
|
|
4010
|
+
|
|
4011
|
+
exports.IgniterAgent = IgniterAgent;
|
|
4012
|
+
exports.IgniterAgentAdapterError = IgniterAgentAdapterError;
|
|
4013
|
+
exports.IgniterAgentAsyncUtils = IgniterAgentAsyncUtils;
|
|
4014
|
+
exports.IgniterAgentBuilder = IgniterAgentBuilder;
|
|
4015
|
+
exports.IgniterAgentConfigError = IgniterAgentConfigError;
|
|
4016
|
+
exports.IgniterAgentCore = IgniterAgentCore;
|
|
4017
|
+
exports.IgniterAgentError = IgniterAgentError;
|
|
4018
|
+
exports.IgniterAgentErrorCode = IgniterAgentErrorCode;
|
|
4019
|
+
exports.IgniterAgentInMemoryAdapter = IgniterAgentInMemoryAdapter;
|
|
4020
|
+
exports.IgniterAgentJSONFileAdapter = IgniterAgentJSONFileAdapter;
|
|
4021
|
+
exports.IgniterAgentMCPBuilder = IgniterAgentMCPBuilder;
|
|
4022
|
+
exports.IgniterAgentMCPClient = IgniterAgentMCPClient;
|
|
4023
|
+
exports.IgniterAgentMCPError = IgniterAgentMCPError;
|
|
4024
|
+
exports.IgniterAgentManager = IgniterAgentManager;
|
|
4025
|
+
exports.IgniterAgentManagerBuilder = IgniterAgentManagerBuilder;
|
|
4026
|
+
exports.IgniterAgentManagerCore = IgniterAgentManagerCore;
|
|
4027
|
+
exports.IgniterAgentMemoryCore = IgniterAgentMemoryCore;
|
|
4028
|
+
exports.IgniterAgentMemoryError = IgniterAgentMemoryError;
|
|
4029
|
+
exports.IgniterAgentObjectUtils = IgniterAgentObjectUtils;
|
|
4030
|
+
exports.IgniterAgentPrompt = IgniterAgentPrompt;
|
|
4031
|
+
exports.IgniterAgentPromptBuilder = IgniterAgentPromptBuilder;
|
|
4032
|
+
exports.IgniterAgentStringUtils = IgniterAgentStringUtils;
|
|
4033
|
+
exports.IgniterAgentTelemetryEvents = IgniterAgentTelemetryEvents;
|
|
4034
|
+
exports.IgniterAgentTool = IgniterAgentTool;
|
|
4035
|
+
exports.IgniterAgentToolBuilder = IgniterAgentToolBuilder;
|
|
4036
|
+
exports.IgniterAgentToolError = IgniterAgentToolError;
|
|
4037
|
+
exports.IgniterAgentToolset = IgniterAgentToolset;
|
|
4038
|
+
exports.IgniterAgentToolsetBuilder = IgniterAgentToolsetBuilder;
|
|
4039
|
+
exports.IgniterAgentValidationUtils = IgniterAgentValidationUtils;
|
|
4040
|
+
exports.isIgniterAgentError = isIgniterAgentError;
|
|
4041
|
+
exports.isIgniterAgentMCPError = isIgniterAgentMCPError;
|
|
4042
|
+
exports.isIgniterAgentToolError = isIgniterAgentToolError;
|
|
4043
|
+
exports.wrapError = wrapError;
|
|
4044
|
+
//# sourceMappingURL=index.js.map
|
|
4045
|
+
//# sourceMappingURL=index.js.map
|