@lov3kaizen/agentsea-crews 0.5.1
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/LICENSE +21 -0
- package/README.md +325 -0
- package/dist/chunk-4PF73ECN.mjs +587 -0
- package/dist/chunk-G3PAPYOI.mjs +1070 -0
- package/dist/chunk-QMK3HWFX.mjs +4584 -0
- package/dist/index.js +8761 -0
- package/dist/index.mjs +2528 -0
- package/dist/nestjs/index.js +5589 -0
- package/dist/nestjs/index.mjs +453 -0
- package/dist/templates/index.js +5625 -0
- package/dist/templates/index.mjs +29 -0
- package/package.json +89 -0
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,2528 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CrewDashboard,
|
|
3
|
+
DebugMode,
|
|
4
|
+
createDashboard,
|
|
5
|
+
createDebugMode
|
|
6
|
+
} from "./chunk-4PF73ECN.mjs";
|
|
7
|
+
import {
|
|
8
|
+
CodeReviewTasks,
|
|
9
|
+
CustomerSupportTasks,
|
|
10
|
+
ResearchTasks,
|
|
11
|
+
WritingTasks,
|
|
12
|
+
createCodeReviewCrew,
|
|
13
|
+
createCodeReviewCrewConfig,
|
|
14
|
+
createCustomerSupportCrew,
|
|
15
|
+
createCustomerSupportCrewConfig,
|
|
16
|
+
createResearchCrew,
|
|
17
|
+
createResearchCrewConfig,
|
|
18
|
+
createWritingCrew,
|
|
19
|
+
createWritingCrewConfig
|
|
20
|
+
} from "./chunk-G3PAPYOI.mjs";
|
|
21
|
+
import {
|
|
22
|
+
AgentCapabilities,
|
|
23
|
+
AgentRegistry,
|
|
24
|
+
AuctionStrategy,
|
|
25
|
+
BaseDelegationStrategy,
|
|
26
|
+
BestMatchStrategy,
|
|
27
|
+
CollaborationManager,
|
|
28
|
+
ConflictResolver,
|
|
29
|
+
ConsensusStrategy,
|
|
30
|
+
Crew,
|
|
31
|
+
CrewAgent,
|
|
32
|
+
DelegationCoordinator,
|
|
33
|
+
DelegationError,
|
|
34
|
+
ExecutionContext,
|
|
35
|
+
HierarchicalStrategy,
|
|
36
|
+
Role,
|
|
37
|
+
RoundRobinStrategy,
|
|
38
|
+
STRATEGY_TYPES,
|
|
39
|
+
Task,
|
|
40
|
+
TaskQueue,
|
|
41
|
+
createAgentRegistry,
|
|
42
|
+
createAuctionStrategy,
|
|
43
|
+
createBestMatchStrategy,
|
|
44
|
+
createCollaborationManager,
|
|
45
|
+
createConflictResolver,
|
|
46
|
+
createConsensusStrategy,
|
|
47
|
+
createCrew,
|
|
48
|
+
createCrewAgent,
|
|
49
|
+
createDelegationCoordinator,
|
|
50
|
+
createExecutionContext,
|
|
51
|
+
createHierarchicalStrategy,
|
|
52
|
+
createRole,
|
|
53
|
+
createRoundRobinStrategy,
|
|
54
|
+
createStrategy,
|
|
55
|
+
createTask,
|
|
56
|
+
createTaskQueue
|
|
57
|
+
} from "./chunk-QMK3HWFX.mjs";
|
|
58
|
+
|
|
59
|
+
// src/types/role.types.ts
|
|
60
|
+
var PROFICIENCY_WEIGHTS = {
|
|
61
|
+
novice: 0.25,
|
|
62
|
+
intermediate: 0.5,
|
|
63
|
+
expert: 0.75,
|
|
64
|
+
master: 1
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
// src/types/task.types.ts
|
|
68
|
+
var PRIORITY_WEIGHTS = {
|
|
69
|
+
critical: 100,
|
|
70
|
+
high: 75,
|
|
71
|
+
medium: 50,
|
|
72
|
+
low: 25
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
// src/workflows/WorkflowBuilder.ts
|
|
76
|
+
import { nanoid } from "nanoid";
|
|
77
|
+
var BranchBuilder = class {
|
|
78
|
+
parent;
|
|
79
|
+
condition;
|
|
80
|
+
thenSteps = [];
|
|
81
|
+
elseSteps = [];
|
|
82
|
+
constructor(parent, condition) {
|
|
83
|
+
this.parent = parent;
|
|
84
|
+
this.condition = condition;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Add steps for true branch
|
|
88
|
+
*/
|
|
89
|
+
then(builder) {
|
|
90
|
+
const subBuilder = new WorkflowBuilder(`then-${nanoid(6)}`);
|
|
91
|
+
builder(subBuilder);
|
|
92
|
+
this.thenSteps = subBuilder.getSteps();
|
|
93
|
+
return this;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Add steps for false branch
|
|
97
|
+
*/
|
|
98
|
+
otherwise(builder) {
|
|
99
|
+
const subBuilder = new WorkflowBuilder(`else-${nanoid(6)}`);
|
|
100
|
+
builder(subBuilder);
|
|
101
|
+
this.elseSteps = subBuilder.getSteps();
|
|
102
|
+
return this;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Complete the branch and return to parent builder
|
|
106
|
+
*/
|
|
107
|
+
endBranch() {
|
|
108
|
+
const conditionalConfig = {
|
|
109
|
+
name: `conditional-${nanoid(6)}`,
|
|
110
|
+
condition: this.condition,
|
|
111
|
+
thenSteps: this.thenSteps.map((s) => s.config),
|
|
112
|
+
elseSteps: this.elseSteps.length > 0 ? this.elseSteps.map((s) => s.config) : void 0
|
|
113
|
+
};
|
|
114
|
+
this.parent.addConditional(
|
|
115
|
+
conditionalConfig,
|
|
116
|
+
this.thenSteps,
|
|
117
|
+
this.elseSteps
|
|
118
|
+
);
|
|
119
|
+
return this.parent;
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
var LoopBuilder = class {
|
|
123
|
+
parent;
|
|
124
|
+
condition;
|
|
125
|
+
bodySteps = [];
|
|
126
|
+
maxIter = 100;
|
|
127
|
+
constructor(parent, condition) {
|
|
128
|
+
this.parent = parent;
|
|
129
|
+
this.condition = condition;
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Set loop body
|
|
133
|
+
*/
|
|
134
|
+
do(builder) {
|
|
135
|
+
const subBuilder = new WorkflowBuilder(`loop-body-${nanoid(6)}`);
|
|
136
|
+
builder(subBuilder);
|
|
137
|
+
this.bodySteps = subBuilder.getSteps();
|
|
138
|
+
return this;
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Set maximum iterations
|
|
142
|
+
*/
|
|
143
|
+
maxIterations(max) {
|
|
144
|
+
this.maxIter = max;
|
|
145
|
+
return this;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Complete the loop and return to parent builder
|
|
149
|
+
*/
|
|
150
|
+
endLoop() {
|
|
151
|
+
const loopConfig = {
|
|
152
|
+
name: `loop-${nanoid(6)}`,
|
|
153
|
+
condition: this.condition,
|
|
154
|
+
maxIterations: this.maxIter,
|
|
155
|
+
bodySteps: this.bodySteps.map((s) => s.config)
|
|
156
|
+
};
|
|
157
|
+
this.parent.addLoop(loopConfig, this.bodySteps);
|
|
158
|
+
return this.parent;
|
|
159
|
+
}
|
|
160
|
+
};
|
|
161
|
+
var WorkflowBuilder = class {
|
|
162
|
+
name;
|
|
163
|
+
description;
|
|
164
|
+
steps = [];
|
|
165
|
+
stepHandlers = /* @__PURE__ */ new Map();
|
|
166
|
+
checkpointEnabled = false;
|
|
167
|
+
checkpointInterval = "after-step";
|
|
168
|
+
constructor(name) {
|
|
169
|
+
this.name = name;
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Set workflow description
|
|
173
|
+
*/
|
|
174
|
+
describe(description) {
|
|
175
|
+
this.description = description;
|
|
176
|
+
return this;
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Add a sequential step
|
|
180
|
+
*/
|
|
181
|
+
addStep(name, handler, options = {}) {
|
|
182
|
+
const stepConfig = {
|
|
183
|
+
name,
|
|
184
|
+
type: "task",
|
|
185
|
+
...options
|
|
186
|
+
};
|
|
187
|
+
this.steps.push({
|
|
188
|
+
type: "step",
|
|
189
|
+
config: stepConfig,
|
|
190
|
+
handler
|
|
191
|
+
});
|
|
192
|
+
this.stepHandlers.set(name, handler);
|
|
193
|
+
return this;
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Add parallel steps
|
|
197
|
+
*/
|
|
198
|
+
parallel(...steps) {
|
|
199
|
+
const parallelConfig = {
|
|
200
|
+
name: `parallel-${nanoid(6)}`,
|
|
201
|
+
steps: steps.map((s) => ({
|
|
202
|
+
name: s.name,
|
|
203
|
+
type: "task"
|
|
204
|
+
})),
|
|
205
|
+
waitFor: "all"
|
|
206
|
+
};
|
|
207
|
+
const children = steps.map((s) => ({
|
|
208
|
+
type: "step",
|
|
209
|
+
config: { name: s.name, type: "task" },
|
|
210
|
+
handler: s.handler
|
|
211
|
+
}));
|
|
212
|
+
for (const step of steps) {
|
|
213
|
+
this.stepHandlers.set(step.name, step.handler);
|
|
214
|
+
}
|
|
215
|
+
this.steps.push({
|
|
216
|
+
type: "parallel",
|
|
217
|
+
config: parallelConfig,
|
|
218
|
+
children
|
|
219
|
+
});
|
|
220
|
+
return this;
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Add sequential steps
|
|
224
|
+
*/
|
|
225
|
+
sequential(...steps) {
|
|
226
|
+
for (const step of steps) {
|
|
227
|
+
this.addStep(step.name, step.handler);
|
|
228
|
+
}
|
|
229
|
+
return this;
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* Add conditional branch
|
|
233
|
+
*/
|
|
234
|
+
when(condition) {
|
|
235
|
+
return new BranchBuilder(this, condition);
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* Add a loop
|
|
239
|
+
*/
|
|
240
|
+
while(condition) {
|
|
241
|
+
return new LoopBuilder(this, condition);
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Add a checkpoint
|
|
245
|
+
*/
|
|
246
|
+
checkpoint(name) {
|
|
247
|
+
this.steps.push({
|
|
248
|
+
type: "checkpoint",
|
|
249
|
+
config: {
|
|
250
|
+
name,
|
|
251
|
+
type: "checkpoint"
|
|
252
|
+
}
|
|
253
|
+
});
|
|
254
|
+
return this;
|
|
255
|
+
}
|
|
256
|
+
/**
|
|
257
|
+
* Enable automatic checkpointing
|
|
258
|
+
*/
|
|
259
|
+
enableCheckpoints(interval = "after-step") {
|
|
260
|
+
this.checkpointEnabled = true;
|
|
261
|
+
this.checkpointInterval = interval;
|
|
262
|
+
return this;
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* Add a task step with agent assignment
|
|
266
|
+
*/
|
|
267
|
+
task(name, description, options = {}) {
|
|
268
|
+
const stepConfig = {
|
|
269
|
+
name,
|
|
270
|
+
type: "task",
|
|
271
|
+
agentName: options.agent,
|
|
272
|
+
taskConfig: {
|
|
273
|
+
description,
|
|
274
|
+
expectedOutput: `Result of ${name}`,
|
|
275
|
+
requiredCapabilities: options.requiredCapabilities
|
|
276
|
+
},
|
|
277
|
+
timeoutMs: options.timeout
|
|
278
|
+
};
|
|
279
|
+
this.steps.push({
|
|
280
|
+
type: "step",
|
|
281
|
+
config: stepConfig
|
|
282
|
+
});
|
|
283
|
+
return this;
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* Add a crew step (delegate to entire crew)
|
|
287
|
+
*/
|
|
288
|
+
crew(name, description, options = {}) {
|
|
289
|
+
const stepConfig = {
|
|
290
|
+
name,
|
|
291
|
+
type: "crew",
|
|
292
|
+
taskConfig: {
|
|
293
|
+
description,
|
|
294
|
+
expectedOutput: `Result of crew: ${name}`
|
|
295
|
+
},
|
|
296
|
+
timeoutMs: options.timeout
|
|
297
|
+
};
|
|
298
|
+
this.steps.push({
|
|
299
|
+
type: "step",
|
|
300
|
+
config: stepConfig
|
|
301
|
+
});
|
|
302
|
+
return this;
|
|
303
|
+
}
|
|
304
|
+
/**
|
|
305
|
+
* Internal: add conditional step
|
|
306
|
+
*/
|
|
307
|
+
addConditional(config, thenSteps, elseSteps) {
|
|
308
|
+
this.steps.push({
|
|
309
|
+
type: "conditional",
|
|
310
|
+
config,
|
|
311
|
+
children: [...thenSteps, ...elseSteps]
|
|
312
|
+
});
|
|
313
|
+
for (const step of [...thenSteps, ...elseSteps]) {
|
|
314
|
+
if (step.handler) {
|
|
315
|
+
this.stepHandlers.set(
|
|
316
|
+
step.config.name,
|
|
317
|
+
step.handler
|
|
318
|
+
);
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
/**
|
|
323
|
+
* Internal: add loop step
|
|
324
|
+
*/
|
|
325
|
+
addLoop(config, bodySteps) {
|
|
326
|
+
this.steps.push({
|
|
327
|
+
type: "loop",
|
|
328
|
+
config,
|
|
329
|
+
children: bodySteps
|
|
330
|
+
});
|
|
331
|
+
for (const step of bodySteps) {
|
|
332
|
+
if (step.handler) {
|
|
333
|
+
this.stepHandlers.set(
|
|
334
|
+
step.config.name,
|
|
335
|
+
step.handler
|
|
336
|
+
);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
/**
|
|
341
|
+
* Internal: get steps for sub-builders
|
|
342
|
+
*/
|
|
343
|
+
getSteps() {
|
|
344
|
+
return [...this.steps];
|
|
345
|
+
}
|
|
346
|
+
/**
|
|
347
|
+
* Build the workflow definition
|
|
348
|
+
*/
|
|
349
|
+
build() {
|
|
350
|
+
return {
|
|
351
|
+
id: nanoid(),
|
|
352
|
+
name: this.name,
|
|
353
|
+
description: this.description,
|
|
354
|
+
steps: this.steps.map((s) => s.config),
|
|
355
|
+
handlers: this.stepHandlers,
|
|
356
|
+
checkpointing: this.checkpointEnabled ? { enabled: true, interval: this.checkpointInterval } : void 0
|
|
357
|
+
};
|
|
358
|
+
}
|
|
359
|
+
/**
|
|
360
|
+
* Get a step handler by name
|
|
361
|
+
*/
|
|
362
|
+
getHandler(stepName) {
|
|
363
|
+
return this.stepHandlers.get(stepName);
|
|
364
|
+
}
|
|
365
|
+
/**
|
|
366
|
+
* Validate the workflow
|
|
367
|
+
*/
|
|
368
|
+
validate() {
|
|
369
|
+
const errors = [];
|
|
370
|
+
if (this.steps.length === 0) {
|
|
371
|
+
errors.push("Workflow has no steps");
|
|
372
|
+
}
|
|
373
|
+
const names = /* @__PURE__ */ new Set();
|
|
374
|
+
for (const step of this.steps) {
|
|
375
|
+
const name = step.config.name;
|
|
376
|
+
if (names.has(name)) {
|
|
377
|
+
errors.push(`Duplicate step name: ${name}`);
|
|
378
|
+
}
|
|
379
|
+
names.add(name);
|
|
380
|
+
}
|
|
381
|
+
for (const step of this.steps) {
|
|
382
|
+
if (step.type === "step") {
|
|
383
|
+
const name = step.config.name;
|
|
384
|
+
if (!this.stepHandlers.has(name) && !step.config.agentName) {
|
|
385
|
+
errors.push(`Step "${name}" has no handler and no assigned agent`);
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
return {
|
|
390
|
+
valid: errors.length === 0,
|
|
391
|
+
errors
|
|
392
|
+
};
|
|
393
|
+
}
|
|
394
|
+
};
|
|
395
|
+
function workflow(name) {
|
|
396
|
+
return new WorkflowBuilder(name);
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
// src/workflows/DAGExecutor.ts
|
|
400
|
+
import { nanoid as nanoid2 } from "nanoid";
|
|
401
|
+
var DAGExecutor = class {
|
|
402
|
+
dag;
|
|
403
|
+
_handlers;
|
|
404
|
+
config;
|
|
405
|
+
nodeStates = /* @__PURE__ */ new Map();
|
|
406
|
+
cache = /* @__PURE__ */ new Map();
|
|
407
|
+
aborted = false;
|
|
408
|
+
constructor(dag, handlers, config = {}) {
|
|
409
|
+
this.dag = dag;
|
|
410
|
+
this._handlers = handlers;
|
|
411
|
+
this.config = {
|
|
412
|
+
maxParallel: config.maxParallel ?? 5,
|
|
413
|
+
defaultTimeout: config.defaultTimeout ?? 6e4,
|
|
414
|
+
retryOnFailure: config.retryOnFailure ?? true,
|
|
415
|
+
maxRetries: config.maxRetries ?? 2,
|
|
416
|
+
enableCaching: config.enableCaching ?? false
|
|
417
|
+
};
|
|
418
|
+
for (const node of dag.nodes) {
|
|
419
|
+
this.nodeStates.set(node.id, {
|
|
420
|
+
status: "pending",
|
|
421
|
+
retries: 0
|
|
422
|
+
});
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
/**
|
|
426
|
+
* Execute the DAG
|
|
427
|
+
*/
|
|
428
|
+
async execute(context) {
|
|
429
|
+
const startTime = Date.now();
|
|
430
|
+
const events = [];
|
|
431
|
+
for await (const event of this.executeStream(context)) {
|
|
432
|
+
events.push(event);
|
|
433
|
+
}
|
|
434
|
+
const results = /* @__PURE__ */ new Map();
|
|
435
|
+
for (const [nodeId, state] of this.nodeStates) {
|
|
436
|
+
if (state.result) {
|
|
437
|
+
results.set(nodeId, state.result);
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
const failedNodes = Array.from(this.nodeStates.entries()).filter(([, state]) => state.status === "failed").map(([id]) => id);
|
|
441
|
+
return {
|
|
442
|
+
success: failedNodes.length === 0 && !this.aborted,
|
|
443
|
+
results,
|
|
444
|
+
events,
|
|
445
|
+
executionTimeMs: Date.now() - startTime,
|
|
446
|
+
failedNodes: failedNodes.length > 0 ? failedNodes : void 0
|
|
447
|
+
};
|
|
448
|
+
}
|
|
449
|
+
/**
|
|
450
|
+
* Execute the DAG with streaming events
|
|
451
|
+
*/
|
|
452
|
+
async *executeStream(context) {
|
|
453
|
+
this.aborted = false;
|
|
454
|
+
yield {
|
|
455
|
+
type: "dag:start",
|
|
456
|
+
dagId: this.dag.id,
|
|
457
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
458
|
+
};
|
|
459
|
+
while (!this.isComplete()) {
|
|
460
|
+
if (this.aborted) {
|
|
461
|
+
yield {
|
|
462
|
+
type: "dag:aborted",
|
|
463
|
+
dagId: this.dag.id,
|
|
464
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
465
|
+
};
|
|
466
|
+
break;
|
|
467
|
+
}
|
|
468
|
+
const readyNodes = this.getReadyNodes();
|
|
469
|
+
if (readyNodes.length === 0) {
|
|
470
|
+
const pendingCount = this.getPendingCount();
|
|
471
|
+
if (pendingCount > 0) {
|
|
472
|
+
yield {
|
|
473
|
+
type: "dag:error",
|
|
474
|
+
dagId: this.dag.id,
|
|
475
|
+
error: "Deadlock detected: no nodes ready but pending nodes exist",
|
|
476
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
477
|
+
};
|
|
478
|
+
break;
|
|
479
|
+
}
|
|
480
|
+
continue;
|
|
481
|
+
}
|
|
482
|
+
const batch = readyNodes.slice(0, this.config.maxParallel);
|
|
483
|
+
const promises = [];
|
|
484
|
+
for (const node of batch) {
|
|
485
|
+
this.updateNodeState(node.id, {
|
|
486
|
+
status: "running",
|
|
487
|
+
startTime: /* @__PURE__ */ new Date()
|
|
488
|
+
});
|
|
489
|
+
yield {
|
|
490
|
+
type: "node:start",
|
|
491
|
+
dagId: this.dag.id,
|
|
492
|
+
nodeId: node.id,
|
|
493
|
+
nodeName: node.name,
|
|
494
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
495
|
+
};
|
|
496
|
+
promises.push(this.executeNode(node, context));
|
|
497
|
+
}
|
|
498
|
+
const results = await Promise.all(promises);
|
|
499
|
+
for (const { nodeId, result, error } of results) {
|
|
500
|
+
const node = this.dag.nodes.find((n) => n.id === nodeId);
|
|
501
|
+
if (error) {
|
|
502
|
+
const state = this.nodeStates.get(nodeId);
|
|
503
|
+
if (this.config.retryOnFailure && state.retries < this.config.maxRetries) {
|
|
504
|
+
this.updateNodeState(nodeId, {
|
|
505
|
+
status: "pending",
|
|
506
|
+
retries: state.retries + 1
|
|
507
|
+
});
|
|
508
|
+
yield {
|
|
509
|
+
type: "node:retry",
|
|
510
|
+
dagId: this.dag.id,
|
|
511
|
+
nodeId,
|
|
512
|
+
nodeName: node.name,
|
|
513
|
+
attempt: state.retries + 1,
|
|
514
|
+
error,
|
|
515
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
516
|
+
};
|
|
517
|
+
} else {
|
|
518
|
+
this.updateNodeState(nodeId, {
|
|
519
|
+
status: "failed",
|
|
520
|
+
error,
|
|
521
|
+
endTime: /* @__PURE__ */ new Date()
|
|
522
|
+
});
|
|
523
|
+
yield {
|
|
524
|
+
type: "node:error",
|
|
525
|
+
dagId: this.dag.id,
|
|
526
|
+
nodeId,
|
|
527
|
+
nodeName: node.name,
|
|
528
|
+
error,
|
|
529
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
530
|
+
};
|
|
531
|
+
for (const depId of this.getDependentNodes(nodeId)) {
|
|
532
|
+
this.updateNodeState(depId, { status: "skipped" });
|
|
533
|
+
yield {
|
|
534
|
+
type: "node:skipped",
|
|
535
|
+
dagId: this.dag.id,
|
|
536
|
+
nodeId: depId,
|
|
537
|
+
nodeName: this.dag.nodes.find((n) => n.id === depId)?.name ?? "",
|
|
538
|
+
reason: `Dependency ${node.name} failed`,
|
|
539
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
540
|
+
};
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
} else {
|
|
544
|
+
this.updateNodeState(nodeId, {
|
|
545
|
+
status: "completed",
|
|
546
|
+
result,
|
|
547
|
+
endTime: /* @__PURE__ */ new Date()
|
|
548
|
+
});
|
|
549
|
+
if (this.config.enableCaching) {
|
|
550
|
+
this.cache.set(nodeId, result);
|
|
551
|
+
}
|
|
552
|
+
yield {
|
|
553
|
+
type: "node:complete",
|
|
554
|
+
dagId: this.dag.id,
|
|
555
|
+
nodeId,
|
|
556
|
+
nodeName: node.name,
|
|
557
|
+
result,
|
|
558
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
559
|
+
};
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
yield {
|
|
564
|
+
type: "dag:complete",
|
|
565
|
+
dagId: this.dag.id,
|
|
566
|
+
success: this.isSuccessful(),
|
|
567
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
568
|
+
};
|
|
569
|
+
}
|
|
570
|
+
/**
|
|
571
|
+
* Execute a single node
|
|
572
|
+
*/
|
|
573
|
+
async executeNode(node, context) {
|
|
574
|
+
if (this.config.enableCaching && this.cache.has(node.id)) {
|
|
575
|
+
return { nodeId: node.id, result: this.cache.get(node.id) };
|
|
576
|
+
}
|
|
577
|
+
const nodeName = node.name ?? node.id;
|
|
578
|
+
const handler = this._handlers.get(nodeName);
|
|
579
|
+
if (!handler) {
|
|
580
|
+
return {
|
|
581
|
+
nodeId: node.id,
|
|
582
|
+
result: null,
|
|
583
|
+
error: `No handler found for node: ${nodeName}`
|
|
584
|
+
};
|
|
585
|
+
}
|
|
586
|
+
const workflowContext = this.buildWorkflowContext(node, context);
|
|
587
|
+
const timeout = node.stepConfig?.timeoutMs ?? this.config.defaultTimeout;
|
|
588
|
+
try {
|
|
589
|
+
const result = await Promise.race([
|
|
590
|
+
handler(workflowContext),
|
|
591
|
+
this.createTimeout(timeout, nodeName)
|
|
592
|
+
]);
|
|
593
|
+
return { nodeId: node.id, result };
|
|
594
|
+
} catch (error) {
|
|
595
|
+
return {
|
|
596
|
+
nodeId: node.id,
|
|
597
|
+
result: null,
|
|
598
|
+
error: error instanceof Error ? error.message : String(error)
|
|
599
|
+
};
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
/**
|
|
603
|
+
* Build workflow context for a node
|
|
604
|
+
*/
|
|
605
|
+
buildWorkflowContext(node, context) {
|
|
606
|
+
const stepResults = /* @__PURE__ */ new Map();
|
|
607
|
+
for (const depId of node.dependencies ?? []) {
|
|
608
|
+
const state = this.nodeStates.get(depId);
|
|
609
|
+
if (state?.result) {
|
|
610
|
+
const depNode = this.dag.nodes.find((n) => n.id === depId);
|
|
611
|
+
if (depNode) {
|
|
612
|
+
const depNodeName = depNode.name ?? depNode.id;
|
|
613
|
+
stepResults.set(depNodeName, state.result);
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
return {
|
|
618
|
+
stepName: node.name ?? node.id,
|
|
619
|
+
stepResults,
|
|
620
|
+
variables: new Map(context.entries()),
|
|
621
|
+
setVariable: (key, value) => context.set(key, value),
|
|
622
|
+
getVariable: (key) => context.get(key),
|
|
623
|
+
emit: (event) => context.emit(event),
|
|
624
|
+
isAborted: () => context.isAborted || this.aborted
|
|
625
|
+
};
|
|
626
|
+
}
|
|
627
|
+
/**
|
|
628
|
+
* Create a timeout promise
|
|
629
|
+
*/
|
|
630
|
+
createTimeout(ms, stepName) {
|
|
631
|
+
return new Promise((_, reject) => {
|
|
632
|
+
setTimeout(() => {
|
|
633
|
+
reject(new Error(`Step "${stepName}" timed out after ${ms}ms`));
|
|
634
|
+
}, ms);
|
|
635
|
+
});
|
|
636
|
+
}
|
|
637
|
+
/**
|
|
638
|
+
* Get nodes ready for execution
|
|
639
|
+
*/
|
|
640
|
+
getReadyNodes() {
|
|
641
|
+
const ready = [];
|
|
642
|
+
for (const node of this.dag.nodes) {
|
|
643
|
+
const state = this.nodeStates.get(node.id);
|
|
644
|
+
if (state?.status !== "pending") continue;
|
|
645
|
+
const depsCompleted = (node.dependencies ?? []).every((depId) => {
|
|
646
|
+
const depState = this.nodeStates.get(depId);
|
|
647
|
+
return depState?.status === "completed";
|
|
648
|
+
});
|
|
649
|
+
if (depsCompleted) {
|
|
650
|
+
ready.push(node);
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
return ready;
|
|
654
|
+
}
|
|
655
|
+
/**
|
|
656
|
+
* Get nodes that depend on a given node
|
|
657
|
+
*/
|
|
658
|
+
getDependentNodes(nodeId) {
|
|
659
|
+
const dependents = [];
|
|
660
|
+
for (const node of this.dag.nodes) {
|
|
661
|
+
if (node.dependencies?.includes(nodeId)) {
|
|
662
|
+
dependents.push(node.id);
|
|
663
|
+
dependents.push(...this.getDependentNodes(node.id));
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
return [...new Set(dependents)];
|
|
667
|
+
}
|
|
668
|
+
/**
|
|
669
|
+
* Update node state
|
|
670
|
+
*/
|
|
671
|
+
updateNodeState(nodeId, update) {
|
|
672
|
+
const current = this.nodeStates.get(nodeId) ?? {
|
|
673
|
+
status: "pending",
|
|
674
|
+
retries: 0
|
|
675
|
+
};
|
|
676
|
+
this.nodeStates.set(nodeId, { ...current, ...update });
|
|
677
|
+
}
|
|
678
|
+
/**
|
|
679
|
+
* Check if all nodes are done
|
|
680
|
+
*/
|
|
681
|
+
isComplete() {
|
|
682
|
+
for (const state of this.nodeStates.values()) {
|
|
683
|
+
if (state.status === "pending" || state.status === "running") {
|
|
684
|
+
return false;
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
return true;
|
|
688
|
+
}
|
|
689
|
+
/**
|
|
690
|
+
* Check if execution was successful
|
|
691
|
+
*/
|
|
692
|
+
isSuccessful() {
|
|
693
|
+
for (const state of this.nodeStates.values()) {
|
|
694
|
+
if (state.status === "failed") {
|
|
695
|
+
return false;
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
return !this.aborted;
|
|
699
|
+
}
|
|
700
|
+
/**
|
|
701
|
+
* Get count of pending nodes
|
|
702
|
+
*/
|
|
703
|
+
getPendingCount() {
|
|
704
|
+
let count = 0;
|
|
705
|
+
for (const state of this.nodeStates.values()) {
|
|
706
|
+
if (state.status === "pending") count++;
|
|
707
|
+
}
|
|
708
|
+
return count;
|
|
709
|
+
}
|
|
710
|
+
/**
|
|
711
|
+
* Abort execution
|
|
712
|
+
*/
|
|
713
|
+
abort() {
|
|
714
|
+
this.aborted = true;
|
|
715
|
+
}
|
|
716
|
+
/**
|
|
717
|
+
* Validate the DAG
|
|
718
|
+
*/
|
|
719
|
+
validate() {
|
|
720
|
+
const errors = [];
|
|
721
|
+
const warnings = [];
|
|
722
|
+
if (this.dag.nodes.length === 0) {
|
|
723
|
+
errors.push("DAG has no nodes");
|
|
724
|
+
}
|
|
725
|
+
const nodeIds = new Set(this.dag.nodes.map((n) => n.id));
|
|
726
|
+
for (const node of this.dag.nodes) {
|
|
727
|
+
const nodeName = node.name ?? node.id;
|
|
728
|
+
for (const depId of node.dependencies ?? []) {
|
|
729
|
+
if (!nodeIds.has(depId)) {
|
|
730
|
+
errors.push(`Node "${nodeName}" has missing dependency: ${depId}`);
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
const cycle = this.detectCycle();
|
|
735
|
+
if (cycle) {
|
|
736
|
+
errors.push(`Cycle detected: ${cycle.join(" -> ")}`);
|
|
737
|
+
}
|
|
738
|
+
for (const node of this.dag.nodes) {
|
|
739
|
+
const nodeName = node.name ?? node.id;
|
|
740
|
+
if (!this._handlers.has(nodeName)) {
|
|
741
|
+
warnings.push(`Node "${nodeName}" has no handler`);
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
return {
|
|
745
|
+
valid: errors.length === 0,
|
|
746
|
+
errors,
|
|
747
|
+
warnings
|
|
748
|
+
};
|
|
749
|
+
}
|
|
750
|
+
/**
|
|
751
|
+
* Detect cycles in the DAG
|
|
752
|
+
*/
|
|
753
|
+
detectCycle() {
|
|
754
|
+
const visited = /* @__PURE__ */ new Set();
|
|
755
|
+
const recursionStack = /* @__PURE__ */ new Set();
|
|
756
|
+
const path = [];
|
|
757
|
+
const dfs = (nodeId) => {
|
|
758
|
+
visited.add(nodeId);
|
|
759
|
+
recursionStack.add(nodeId);
|
|
760
|
+
path.push(nodeId);
|
|
761
|
+
const node = this.dag.nodes.find((n) => n.id === nodeId);
|
|
762
|
+
if (node) {
|
|
763
|
+
for (const depId of node.dependencies ?? []) {
|
|
764
|
+
if (!visited.has(depId)) {
|
|
765
|
+
if (dfs(depId)) return true;
|
|
766
|
+
} else if (recursionStack.has(depId)) {
|
|
767
|
+
path.push(depId);
|
|
768
|
+
return true;
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
recursionStack.delete(nodeId);
|
|
773
|
+
path.pop();
|
|
774
|
+
return false;
|
|
775
|
+
};
|
|
776
|
+
for (const node of this.dag.nodes) {
|
|
777
|
+
if (!visited.has(node.id)) {
|
|
778
|
+
if (dfs(node.id)) {
|
|
779
|
+
const cycleStart = path.indexOf(path[path.length - 1]);
|
|
780
|
+
return path.slice(cycleStart).map((id) => this.dag.nodes.find((n) => n.id === id)?.name ?? id);
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
return null;
|
|
785
|
+
}
|
|
786
|
+
/**
|
|
787
|
+
* Get execution state for all nodes
|
|
788
|
+
*/
|
|
789
|
+
getState() {
|
|
790
|
+
return new Map(this.nodeStates);
|
|
791
|
+
}
|
|
792
|
+
/**
|
|
793
|
+
* Get execution statistics
|
|
794
|
+
*/
|
|
795
|
+
getStatistics() {
|
|
796
|
+
let completed = 0;
|
|
797
|
+
let failed = 0;
|
|
798
|
+
let skipped = 0;
|
|
799
|
+
let pending = 0;
|
|
800
|
+
let running = 0;
|
|
801
|
+
for (const state of this.nodeStates.values()) {
|
|
802
|
+
switch (state.status) {
|
|
803
|
+
case "completed":
|
|
804
|
+
completed++;
|
|
805
|
+
break;
|
|
806
|
+
case "failed":
|
|
807
|
+
failed++;
|
|
808
|
+
break;
|
|
809
|
+
case "skipped":
|
|
810
|
+
skipped++;
|
|
811
|
+
break;
|
|
812
|
+
case "pending":
|
|
813
|
+
pending++;
|
|
814
|
+
break;
|
|
815
|
+
case "running":
|
|
816
|
+
running++;
|
|
817
|
+
break;
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
return {
|
|
821
|
+
totalNodes: this.dag.nodes.length,
|
|
822
|
+
completed,
|
|
823
|
+
failed,
|
|
824
|
+
skipped,
|
|
825
|
+
pending,
|
|
826
|
+
running
|
|
827
|
+
};
|
|
828
|
+
}
|
|
829
|
+
};
|
|
830
|
+
function createDAGFromSteps(steps, _handlers) {
|
|
831
|
+
const nodes = [];
|
|
832
|
+
const previousNodeIds = [];
|
|
833
|
+
for (let i = 0; i < steps.length; i++) {
|
|
834
|
+
const step = steps[i];
|
|
835
|
+
const nodeId = nanoid2();
|
|
836
|
+
const node = {
|
|
837
|
+
id: nodeId,
|
|
838
|
+
name: step.name,
|
|
839
|
+
stepConfig: step,
|
|
840
|
+
dependencies: [...previousNodeIds]
|
|
841
|
+
// Sequential by default
|
|
842
|
+
};
|
|
843
|
+
nodes.push(node);
|
|
844
|
+
previousNodeIds.length = 0;
|
|
845
|
+
previousNodeIds.push(nodeId);
|
|
846
|
+
}
|
|
847
|
+
return {
|
|
848
|
+
id: nanoid2(),
|
|
849
|
+
nodes,
|
|
850
|
+
edges: []
|
|
851
|
+
};
|
|
852
|
+
}
|
|
853
|
+
function createDAGExecutor(dag, handlers, config) {
|
|
854
|
+
return new DAGExecutor(dag, handlers, config);
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
// src/workflows/ParallelExecution.ts
|
|
858
|
+
var ParallelExecutor = class {
|
|
859
|
+
options;
|
|
860
|
+
constructor(options = {}) {
|
|
861
|
+
this.options = {
|
|
862
|
+
maxConcurrency: options.maxConcurrency ?? 5,
|
|
863
|
+
taskTimeout: options.taskTimeout ?? 6e4,
|
|
864
|
+
failFast: options.failFast ?? false,
|
|
865
|
+
continueOnError: options.continueOnError ?? true
|
|
866
|
+
};
|
|
867
|
+
}
|
|
868
|
+
/**
|
|
869
|
+
* Execute tasks in parallel
|
|
870
|
+
*/
|
|
871
|
+
async executeBatch(tasks, executor, _context) {
|
|
872
|
+
const startTime = Date.now();
|
|
873
|
+
const results = [];
|
|
874
|
+
let aborted = false;
|
|
875
|
+
const semaphore = new Semaphore(this.options.maxConcurrency);
|
|
876
|
+
const taskPromises = tasks.map(async (task) => {
|
|
877
|
+
if (aborted) {
|
|
878
|
+
return {
|
|
879
|
+
taskId: task.id,
|
|
880
|
+
status: "failed",
|
|
881
|
+
error: "Batch aborted",
|
|
882
|
+
executionTimeMs: 0
|
|
883
|
+
};
|
|
884
|
+
}
|
|
885
|
+
await semaphore.acquire();
|
|
886
|
+
const taskStartTime = Date.now();
|
|
887
|
+
try {
|
|
888
|
+
const result = await Promise.race([
|
|
889
|
+
executor(task),
|
|
890
|
+
this.createTimeout(this.options.taskTimeout)
|
|
891
|
+
]);
|
|
892
|
+
return {
|
|
893
|
+
taskId: task.id,
|
|
894
|
+
status: "success",
|
|
895
|
+
result,
|
|
896
|
+
executionTimeMs: Date.now() - taskStartTime
|
|
897
|
+
};
|
|
898
|
+
} catch (error) {
|
|
899
|
+
const isTimeout = error instanceof TimeoutError;
|
|
900
|
+
if (this.options.failFast && !isTimeout) {
|
|
901
|
+
aborted = true;
|
|
902
|
+
}
|
|
903
|
+
return {
|
|
904
|
+
taskId: task.id,
|
|
905
|
+
status: isTimeout ? "timeout" : "failed",
|
|
906
|
+
error: error instanceof Error ? error.message : String(error),
|
|
907
|
+
executionTimeMs: Date.now() - taskStartTime
|
|
908
|
+
};
|
|
909
|
+
} finally {
|
|
910
|
+
semaphore.release();
|
|
911
|
+
}
|
|
912
|
+
});
|
|
913
|
+
const parallelResults = await Promise.all(taskPromises);
|
|
914
|
+
results.push(...parallelResults);
|
|
915
|
+
const successCount = results.filter((r) => r.status === "success").length;
|
|
916
|
+
const failureCount = results.filter((r) => r.status !== "success").length;
|
|
917
|
+
return {
|
|
918
|
+
results,
|
|
919
|
+
totalTimeMs: Date.now() - startTime,
|
|
920
|
+
successCount,
|
|
921
|
+
failureCount,
|
|
922
|
+
allSuccessful: failureCount === 0
|
|
923
|
+
};
|
|
924
|
+
}
|
|
925
|
+
/**
|
|
926
|
+
* Execute tasks with agent assignment
|
|
927
|
+
*/
|
|
928
|
+
async executeWithAgents(tasks, agents, context) {
|
|
929
|
+
const agentQueue = new AgentPool(agents);
|
|
930
|
+
const executor = async (task) => {
|
|
931
|
+
const agent = await agentQueue.acquire();
|
|
932
|
+
try {
|
|
933
|
+
return await agent.executeTask(task);
|
|
934
|
+
} finally {
|
|
935
|
+
agentQueue.release(agent);
|
|
936
|
+
}
|
|
937
|
+
};
|
|
938
|
+
return this.executeBatch(tasks, executor, context);
|
|
939
|
+
}
|
|
940
|
+
/**
|
|
941
|
+
* Map over items with parallel execution
|
|
942
|
+
*/
|
|
943
|
+
async map(items, mapper) {
|
|
944
|
+
const semaphore = new Semaphore(this.options.maxConcurrency);
|
|
945
|
+
let aborted = false;
|
|
946
|
+
const results = await Promise.all(
|
|
947
|
+
items.map(async (item, index) => {
|
|
948
|
+
if (aborted) {
|
|
949
|
+
return { item, error: "Aborted" };
|
|
950
|
+
}
|
|
951
|
+
await semaphore.acquire();
|
|
952
|
+
try {
|
|
953
|
+
const result = await mapper(item, index);
|
|
954
|
+
return { item, result };
|
|
955
|
+
} catch (error) {
|
|
956
|
+
if (this.options.failFast) {
|
|
957
|
+
aborted = true;
|
|
958
|
+
}
|
|
959
|
+
return {
|
|
960
|
+
item,
|
|
961
|
+
error: error instanceof Error ? error.message : String(error)
|
|
962
|
+
};
|
|
963
|
+
} finally {
|
|
964
|
+
semaphore.release();
|
|
965
|
+
}
|
|
966
|
+
})
|
|
967
|
+
);
|
|
968
|
+
return results;
|
|
969
|
+
}
|
|
970
|
+
/**
|
|
971
|
+
* Execute tasks in batches
|
|
972
|
+
*/
|
|
973
|
+
async executeBatches(items, batchSize, processor) {
|
|
974
|
+
const results = [];
|
|
975
|
+
const batches = [];
|
|
976
|
+
for (let i = 0; i < items.length; i += batchSize) {
|
|
977
|
+
batches.push(items.slice(i, i + batchSize));
|
|
978
|
+
}
|
|
979
|
+
const semaphore = new Semaphore(this.options.maxConcurrency);
|
|
980
|
+
await Promise.all(
|
|
981
|
+
batches.map(async (batch) => {
|
|
982
|
+
await semaphore.acquire();
|
|
983
|
+
try {
|
|
984
|
+
const batchResults = await processor(batch);
|
|
985
|
+
results.push(...batchResults);
|
|
986
|
+
} finally {
|
|
987
|
+
semaphore.release();
|
|
988
|
+
}
|
|
989
|
+
})
|
|
990
|
+
);
|
|
991
|
+
return results;
|
|
992
|
+
}
|
|
993
|
+
/**
|
|
994
|
+
* Create a timeout promise
|
|
995
|
+
*/
|
|
996
|
+
createTimeout(ms) {
|
|
997
|
+
return new Promise((_, reject) => {
|
|
998
|
+
setTimeout(() => {
|
|
999
|
+
reject(new TimeoutError(`Task timed out after ${ms}ms`));
|
|
1000
|
+
}, ms);
|
|
1001
|
+
});
|
|
1002
|
+
}
|
|
1003
|
+
};
|
|
1004
|
+
var Semaphore = class {
|
|
1005
|
+
permits;
|
|
1006
|
+
waiting = [];
|
|
1007
|
+
constructor(permits) {
|
|
1008
|
+
this.permits = permits;
|
|
1009
|
+
}
|
|
1010
|
+
async acquire() {
|
|
1011
|
+
if (this.permits > 0) {
|
|
1012
|
+
this.permits--;
|
|
1013
|
+
return;
|
|
1014
|
+
}
|
|
1015
|
+
return new Promise((resolve) => {
|
|
1016
|
+
this.waiting.push(resolve);
|
|
1017
|
+
});
|
|
1018
|
+
}
|
|
1019
|
+
release() {
|
|
1020
|
+
if (this.waiting.length > 0) {
|
|
1021
|
+
const next = this.waiting.shift();
|
|
1022
|
+
next();
|
|
1023
|
+
} else {
|
|
1024
|
+
this.permits++;
|
|
1025
|
+
}
|
|
1026
|
+
}
|
|
1027
|
+
};
|
|
1028
|
+
var AgentPool = class {
|
|
1029
|
+
available;
|
|
1030
|
+
waiting = [];
|
|
1031
|
+
constructor(agents) {
|
|
1032
|
+
this.available = [...agents];
|
|
1033
|
+
}
|
|
1034
|
+
async acquire() {
|
|
1035
|
+
if (this.available.length > 0) {
|
|
1036
|
+
return this.available.pop();
|
|
1037
|
+
}
|
|
1038
|
+
return new Promise((resolve) => {
|
|
1039
|
+
this.waiting.push(resolve);
|
|
1040
|
+
});
|
|
1041
|
+
}
|
|
1042
|
+
release(agent) {
|
|
1043
|
+
if (this.waiting.length > 0) {
|
|
1044
|
+
const next = this.waiting.shift();
|
|
1045
|
+
next(agent);
|
|
1046
|
+
} else {
|
|
1047
|
+
this.available.push(agent);
|
|
1048
|
+
}
|
|
1049
|
+
}
|
|
1050
|
+
};
|
|
1051
|
+
var TimeoutError = class extends Error {
|
|
1052
|
+
constructor(message) {
|
|
1053
|
+
super(message);
|
|
1054
|
+
this.name = "TimeoutError";
|
|
1055
|
+
}
|
|
1056
|
+
};
|
|
1057
|
+
function createParallelExecutor(options) {
|
|
1058
|
+
return new ParallelExecutor(options);
|
|
1059
|
+
}
|
|
1060
|
+
|
|
1061
|
+
// src/workflows/Checkpointing.ts
|
|
1062
|
+
import { nanoid as nanoid3 } from "nanoid";
|
|
1063
|
+
var InMemoryCheckpointStorage = class {
|
|
1064
|
+
checkpoints = /* @__PURE__ */ new Map();
|
|
1065
|
+
save(checkpoint) {
|
|
1066
|
+
this.checkpoints.set(checkpoint.id, checkpoint);
|
|
1067
|
+
return Promise.resolve(checkpoint.id);
|
|
1068
|
+
}
|
|
1069
|
+
load(checkpointId) {
|
|
1070
|
+
return Promise.resolve(this.checkpoints.get(checkpointId) ?? null);
|
|
1071
|
+
}
|
|
1072
|
+
list(workflowId) {
|
|
1073
|
+
const ids = [];
|
|
1074
|
+
for (const [id, checkpoint] of this.checkpoints) {
|
|
1075
|
+
if (checkpoint.workflowId === workflowId) {
|
|
1076
|
+
ids.push(id);
|
|
1077
|
+
}
|
|
1078
|
+
}
|
|
1079
|
+
return Promise.resolve(ids);
|
|
1080
|
+
}
|
|
1081
|
+
delete(checkpointId) {
|
|
1082
|
+
this.checkpoints.delete(checkpointId);
|
|
1083
|
+
return Promise.resolve();
|
|
1084
|
+
}
|
|
1085
|
+
deleteAll(workflowId) {
|
|
1086
|
+
for (const [id, checkpoint] of this.checkpoints) {
|
|
1087
|
+
if (checkpoint.workflowId === workflowId) {
|
|
1088
|
+
this.checkpoints.delete(id);
|
|
1089
|
+
}
|
|
1090
|
+
}
|
|
1091
|
+
return Promise.resolve();
|
|
1092
|
+
}
|
|
1093
|
+
clear() {
|
|
1094
|
+
this.checkpoints.clear();
|
|
1095
|
+
}
|
|
1096
|
+
};
|
|
1097
|
+
var CheckpointManager = class {
|
|
1098
|
+
storage;
|
|
1099
|
+
config;
|
|
1100
|
+
autoCheckpointTimer;
|
|
1101
|
+
stepCounter = 0;
|
|
1102
|
+
currentWorkflowId;
|
|
1103
|
+
constructor(config = {}) {
|
|
1104
|
+
this.storage = config.storage ?? new InMemoryCheckpointStorage();
|
|
1105
|
+
this.config = {
|
|
1106
|
+
autoCheckpointInterval: config.autoCheckpointInterval ?? "after-step",
|
|
1107
|
+
maxCheckpoints: config.maxCheckpoints ?? 10,
|
|
1108
|
+
compress: config.compress ?? false
|
|
1109
|
+
};
|
|
1110
|
+
}
|
|
1111
|
+
/**
|
|
1112
|
+
* Save a checkpoint
|
|
1113
|
+
*/
|
|
1114
|
+
async save(workflowId, state) {
|
|
1115
|
+
const checkpoint = {
|
|
1116
|
+
id: nanoid3(),
|
|
1117
|
+
workflowId,
|
|
1118
|
+
timestamp: /* @__PURE__ */ new Date(),
|
|
1119
|
+
stepIndex: state.currentStepIndex,
|
|
1120
|
+
stepResults: state.stepResults,
|
|
1121
|
+
variables: Object.fromEntries(state.variables),
|
|
1122
|
+
metadata: state.metadata
|
|
1123
|
+
};
|
|
1124
|
+
await this.storage.save(checkpoint);
|
|
1125
|
+
await this.cleanupOldCheckpoints(workflowId);
|
|
1126
|
+
return checkpoint;
|
|
1127
|
+
}
|
|
1128
|
+
/**
|
|
1129
|
+
* Load a checkpoint
|
|
1130
|
+
*/
|
|
1131
|
+
async load(checkpointId) {
|
|
1132
|
+
return this.storage.load(checkpointId);
|
|
1133
|
+
}
|
|
1134
|
+
/**
|
|
1135
|
+
* Load the latest checkpoint for a workflow
|
|
1136
|
+
*/
|
|
1137
|
+
async loadLatest(workflowId) {
|
|
1138
|
+
const checkpointIds = await this.storage.list(workflowId);
|
|
1139
|
+
if (checkpointIds.length === 0) {
|
|
1140
|
+
return null;
|
|
1141
|
+
}
|
|
1142
|
+
let latest = null;
|
|
1143
|
+
for (const id of checkpointIds) {
|
|
1144
|
+
const checkpoint = await this.storage.load(id);
|
|
1145
|
+
if (checkpoint) {
|
|
1146
|
+
if (!latest || checkpoint.timestamp > latest.timestamp) {
|
|
1147
|
+
latest = checkpoint;
|
|
1148
|
+
}
|
|
1149
|
+
}
|
|
1150
|
+
}
|
|
1151
|
+
return latest;
|
|
1152
|
+
}
|
|
1153
|
+
/**
|
|
1154
|
+
* Delete a checkpoint
|
|
1155
|
+
*/
|
|
1156
|
+
async delete(checkpointId) {
|
|
1157
|
+
await this.storage.delete(checkpointId);
|
|
1158
|
+
}
|
|
1159
|
+
/**
|
|
1160
|
+
* Delete all checkpoints for a workflow
|
|
1161
|
+
*/
|
|
1162
|
+
async deleteAll(workflowId) {
|
|
1163
|
+
await this.storage.deleteAll(workflowId);
|
|
1164
|
+
}
|
|
1165
|
+
/**
|
|
1166
|
+
* List checkpoints for a workflow
|
|
1167
|
+
*/
|
|
1168
|
+
async list(workflowId) {
|
|
1169
|
+
const ids = await this.storage.list(workflowId);
|
|
1170
|
+
const checkpoints = [];
|
|
1171
|
+
for (const id of ids) {
|
|
1172
|
+
const checkpoint = await this.storage.load(id);
|
|
1173
|
+
if (checkpoint) {
|
|
1174
|
+
checkpoints.push(checkpoint);
|
|
1175
|
+
}
|
|
1176
|
+
}
|
|
1177
|
+
checkpoints.sort((a, b) => b.timestamp.getTime() - a.timestamp.getTime());
|
|
1178
|
+
return checkpoints;
|
|
1179
|
+
}
|
|
1180
|
+
/**
|
|
1181
|
+
* Enable automatic checkpointing
|
|
1182
|
+
*/
|
|
1183
|
+
enableAutoCheckpoint(workflowId, getState) {
|
|
1184
|
+
this.currentWorkflowId = workflowId;
|
|
1185
|
+
this.stepCounter = 0;
|
|
1186
|
+
if (typeof this.config.autoCheckpointInterval === "number") {
|
|
1187
|
+
this.autoCheckpointTimer = setInterval(() => {
|
|
1188
|
+
void (async () => {
|
|
1189
|
+
try {
|
|
1190
|
+
await this.save(workflowId, getState());
|
|
1191
|
+
} catch (error) {
|
|
1192
|
+
console.error("Auto-checkpoint failed:", error);
|
|
1193
|
+
}
|
|
1194
|
+
})();
|
|
1195
|
+
}, this.config.autoCheckpointInterval);
|
|
1196
|
+
}
|
|
1197
|
+
}
|
|
1198
|
+
/**
|
|
1199
|
+
* Disable automatic checkpointing
|
|
1200
|
+
*/
|
|
1201
|
+
disableAutoCheckpoint() {
|
|
1202
|
+
if (this.autoCheckpointTimer) {
|
|
1203
|
+
clearInterval(this.autoCheckpointTimer);
|
|
1204
|
+
this.autoCheckpointTimer = void 0;
|
|
1205
|
+
}
|
|
1206
|
+
this.currentWorkflowId = void 0;
|
|
1207
|
+
}
|
|
1208
|
+
/**
|
|
1209
|
+
* Notify that a step completed (for step-based checkpointing)
|
|
1210
|
+
*/
|
|
1211
|
+
async onStepComplete(workflowId, state) {
|
|
1212
|
+
if (this.config.autoCheckpointInterval === "after-step") {
|
|
1213
|
+
await this.save(workflowId, state);
|
|
1214
|
+
}
|
|
1215
|
+
this.stepCounter++;
|
|
1216
|
+
}
|
|
1217
|
+
/**
|
|
1218
|
+
* Restore workflow state from checkpoint
|
|
1219
|
+
*/
|
|
1220
|
+
async restore(checkpointId) {
|
|
1221
|
+
const checkpoint = await this.load(checkpointId);
|
|
1222
|
+
if (!checkpoint) {
|
|
1223
|
+
return null;
|
|
1224
|
+
}
|
|
1225
|
+
return {
|
|
1226
|
+
currentStepIndex: checkpoint.stepIndex ?? 0,
|
|
1227
|
+
stepResults: checkpoint.stepResults instanceof Map ? checkpoint.stepResults : new Map(Object.entries(checkpoint.stepResults)),
|
|
1228
|
+
variables: new Map(Object.entries(checkpoint.variables)),
|
|
1229
|
+
metadata: checkpoint.metadata ?? {}
|
|
1230
|
+
};
|
|
1231
|
+
}
|
|
1232
|
+
/**
|
|
1233
|
+
* Cleanup old checkpoints
|
|
1234
|
+
*/
|
|
1235
|
+
async cleanupOldCheckpoints(workflowId) {
|
|
1236
|
+
const checkpoints = await this.list(workflowId);
|
|
1237
|
+
if (checkpoints.length > this.config.maxCheckpoints) {
|
|
1238
|
+
const toDelete = checkpoints.slice(this.config.maxCheckpoints);
|
|
1239
|
+
for (const checkpoint of toDelete) {
|
|
1240
|
+
await this.storage.delete(checkpoint.id);
|
|
1241
|
+
}
|
|
1242
|
+
}
|
|
1243
|
+
}
|
|
1244
|
+
/**
|
|
1245
|
+
* Export checkpoints for a workflow
|
|
1246
|
+
*/
|
|
1247
|
+
async export(workflowId) {
|
|
1248
|
+
const checkpoints = await this.list(workflowId);
|
|
1249
|
+
return JSON.stringify(checkpoints, null, 2);
|
|
1250
|
+
}
|
|
1251
|
+
/**
|
|
1252
|
+
* Import checkpoints
|
|
1253
|
+
*/
|
|
1254
|
+
async import(data) {
|
|
1255
|
+
const checkpoints = JSON.parse(data);
|
|
1256
|
+
let count = 0;
|
|
1257
|
+
for (const checkpoint of checkpoints) {
|
|
1258
|
+
checkpoint.timestamp = new Date(checkpoint.timestamp);
|
|
1259
|
+
await this.storage.save(checkpoint);
|
|
1260
|
+
count++;
|
|
1261
|
+
}
|
|
1262
|
+
return count;
|
|
1263
|
+
}
|
|
1264
|
+
/**
|
|
1265
|
+
* Get statistics
|
|
1266
|
+
*/
|
|
1267
|
+
async getStatistics(workflowId) {
|
|
1268
|
+
const checkpoints = await this.list(workflowId);
|
|
1269
|
+
let totalSize = 0;
|
|
1270
|
+
let oldestTimestamp = null;
|
|
1271
|
+
let newestTimestamp = null;
|
|
1272
|
+
for (const checkpoint of checkpoints) {
|
|
1273
|
+
totalSize += JSON.stringify(checkpoint).length;
|
|
1274
|
+
if (!oldestTimestamp || checkpoint.timestamp < oldestTimestamp) {
|
|
1275
|
+
oldestTimestamp = checkpoint.timestamp;
|
|
1276
|
+
}
|
|
1277
|
+
if (!newestTimestamp || checkpoint.timestamp > newestTimestamp) {
|
|
1278
|
+
newestTimestamp = checkpoint.timestamp;
|
|
1279
|
+
}
|
|
1280
|
+
}
|
|
1281
|
+
return {
|
|
1282
|
+
checkpointCount: checkpoints.length,
|
|
1283
|
+
estimatedSizeBytes: totalSize,
|
|
1284
|
+
oldestCheckpoint: oldestTimestamp,
|
|
1285
|
+
newestCheckpoint: newestTimestamp
|
|
1286
|
+
};
|
|
1287
|
+
}
|
|
1288
|
+
};
|
|
1289
|
+
function createCheckpointManager(config) {
|
|
1290
|
+
return new CheckpointManager(config);
|
|
1291
|
+
}
|
|
1292
|
+
|
|
1293
|
+
// src/memory/SharedMemory.ts
|
|
1294
|
+
import { EventEmitter } from "eventemitter3";
|
|
1295
|
+
var SharedMemory = class extends EventEmitter {
|
|
1296
|
+
namespaces = /* @__PURE__ */ new Map();
|
|
1297
|
+
agentNamespaces = /* @__PURE__ */ new Map();
|
|
1298
|
+
history = [];
|
|
1299
|
+
config;
|
|
1300
|
+
constructor(config = {}) {
|
|
1301
|
+
super();
|
|
1302
|
+
this.config = {
|
|
1303
|
+
maxEntriesPerNamespace: config.maxEntriesPerNamespace ?? 1e3,
|
|
1304
|
+
trackHistory: config.trackHistory ?? true,
|
|
1305
|
+
maxHistoryEntries: config.maxHistoryEntries ?? 100,
|
|
1306
|
+
persist: config.persist ?? false
|
|
1307
|
+
};
|
|
1308
|
+
this.createNamespace("shared");
|
|
1309
|
+
}
|
|
1310
|
+
// ============ Shared Operations ============
|
|
1311
|
+
/**
|
|
1312
|
+
* Set a shared value
|
|
1313
|
+
*/
|
|
1314
|
+
setShared(key, value, agent) {
|
|
1315
|
+
this.set("shared", key, value, agent);
|
|
1316
|
+
}
|
|
1317
|
+
/**
|
|
1318
|
+
* Get a shared value
|
|
1319
|
+
*/
|
|
1320
|
+
getShared(key) {
|
|
1321
|
+
return this.get("shared", key);
|
|
1322
|
+
}
|
|
1323
|
+
/**
|
|
1324
|
+
* Delete a shared value
|
|
1325
|
+
*/
|
|
1326
|
+
deleteShared(key, agent) {
|
|
1327
|
+
return this.delete("shared", key, agent);
|
|
1328
|
+
}
|
|
1329
|
+
/**
|
|
1330
|
+
* Check if shared key exists
|
|
1331
|
+
*/
|
|
1332
|
+
hasShared(key) {
|
|
1333
|
+
return this.has("shared", key);
|
|
1334
|
+
}
|
|
1335
|
+
/**
|
|
1336
|
+
* Get all shared keys
|
|
1337
|
+
*/
|
|
1338
|
+
getSharedKeys() {
|
|
1339
|
+
return this.getKeys("shared");
|
|
1340
|
+
}
|
|
1341
|
+
// ============ Agent-Specific Operations ============
|
|
1342
|
+
/**
|
|
1343
|
+
* Set a value for a specific agent
|
|
1344
|
+
*/
|
|
1345
|
+
setForAgent(agentName, key, value) {
|
|
1346
|
+
if (!this.agentNamespaces.has(agentName)) {
|
|
1347
|
+
const namespace2 = `agent:${agentName}`;
|
|
1348
|
+
this.createNamespace(namespace2);
|
|
1349
|
+
this.agentNamespaces.set(agentName, namespace2);
|
|
1350
|
+
}
|
|
1351
|
+
const namespace = this.agentNamespaces.get(agentName);
|
|
1352
|
+
this.set(namespace, key, value, agentName);
|
|
1353
|
+
}
|
|
1354
|
+
/**
|
|
1355
|
+
* Get a value for a specific agent
|
|
1356
|
+
*/
|
|
1357
|
+
getForAgent(agentName, key) {
|
|
1358
|
+
const namespace = this.agentNamespaces.get(agentName);
|
|
1359
|
+
if (!namespace) return void 0;
|
|
1360
|
+
return this.get(namespace, key);
|
|
1361
|
+
}
|
|
1362
|
+
/**
|
|
1363
|
+
* Delete a value for a specific agent
|
|
1364
|
+
*/
|
|
1365
|
+
deleteForAgent(agentName, key) {
|
|
1366
|
+
const namespace = this.agentNamespaces.get(agentName);
|
|
1367
|
+
if (!namespace) return false;
|
|
1368
|
+
return this.delete(namespace, key, agentName);
|
|
1369
|
+
}
|
|
1370
|
+
/**
|
|
1371
|
+
* Get all keys for an agent
|
|
1372
|
+
*/
|
|
1373
|
+
getAgentKeys(agentName) {
|
|
1374
|
+
const namespace = this.agentNamespaces.get(agentName);
|
|
1375
|
+
if (!namespace) return [];
|
|
1376
|
+
return this.getKeys(namespace);
|
|
1377
|
+
}
|
|
1378
|
+
/**
|
|
1379
|
+
* Clear all agent data
|
|
1380
|
+
*/
|
|
1381
|
+
clearAgent(agentName) {
|
|
1382
|
+
const namespace = this.agentNamespaces.get(agentName);
|
|
1383
|
+
if (namespace) {
|
|
1384
|
+
this.clearNamespace(namespace, agentName);
|
|
1385
|
+
}
|
|
1386
|
+
}
|
|
1387
|
+
// ============ Broadcast Operations ============
|
|
1388
|
+
/**
|
|
1389
|
+
* Broadcast a value to all agents
|
|
1390
|
+
*/
|
|
1391
|
+
broadcast(key, value, fromAgent) {
|
|
1392
|
+
this.setShared(key, value, fromAgent);
|
|
1393
|
+
this.emit("change", {
|
|
1394
|
+
type: "set",
|
|
1395
|
+
key: `broadcast:${key}`,
|
|
1396
|
+
value,
|
|
1397
|
+
agent: fromAgent,
|
|
1398
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
1399
|
+
});
|
|
1400
|
+
}
|
|
1401
|
+
// ============ Namespace Operations ============
|
|
1402
|
+
/**
|
|
1403
|
+
* Create a namespace
|
|
1404
|
+
*/
|
|
1405
|
+
createNamespace(name) {
|
|
1406
|
+
if (this.namespaces.has(name)) {
|
|
1407
|
+
return this.namespaces.get(name);
|
|
1408
|
+
}
|
|
1409
|
+
const namespace = {
|
|
1410
|
+
name,
|
|
1411
|
+
data: /* @__PURE__ */ new Map(),
|
|
1412
|
+
created: /* @__PURE__ */ new Date(),
|
|
1413
|
+
lastAccessed: /* @__PURE__ */ new Date()
|
|
1414
|
+
};
|
|
1415
|
+
this.namespaces.set(name, namespace);
|
|
1416
|
+
this.emit("namespaceCreated", name);
|
|
1417
|
+
return namespace;
|
|
1418
|
+
}
|
|
1419
|
+
/**
|
|
1420
|
+
* Delete a namespace
|
|
1421
|
+
*/
|
|
1422
|
+
deleteNamespace(name) {
|
|
1423
|
+
if (name === "shared") {
|
|
1424
|
+
throw new Error("Cannot delete shared namespace");
|
|
1425
|
+
}
|
|
1426
|
+
if (!this.namespaces.has(name)) {
|
|
1427
|
+
return false;
|
|
1428
|
+
}
|
|
1429
|
+
this.namespaces.delete(name);
|
|
1430
|
+
this.emit("namespaceDeleted", name);
|
|
1431
|
+
return true;
|
|
1432
|
+
}
|
|
1433
|
+
/**
|
|
1434
|
+
* Clear a namespace
|
|
1435
|
+
*/
|
|
1436
|
+
clearNamespace(name, agent) {
|
|
1437
|
+
const namespace = this.namespaces.get(name);
|
|
1438
|
+
if (namespace) {
|
|
1439
|
+
namespace.data.clear();
|
|
1440
|
+
namespace.lastAccessed = /* @__PURE__ */ new Date();
|
|
1441
|
+
if (this.config.trackHistory) {
|
|
1442
|
+
this.addToHistory({
|
|
1443
|
+
type: "clear",
|
|
1444
|
+
agent,
|
|
1445
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
1446
|
+
});
|
|
1447
|
+
}
|
|
1448
|
+
}
|
|
1449
|
+
}
|
|
1450
|
+
/**
|
|
1451
|
+
* Get namespace names
|
|
1452
|
+
*/
|
|
1453
|
+
getNamespaces() {
|
|
1454
|
+
return Array.from(this.namespaces.keys());
|
|
1455
|
+
}
|
|
1456
|
+
// ============ Low-Level Operations ============
|
|
1457
|
+
/**
|
|
1458
|
+
* Set a value in a namespace
|
|
1459
|
+
*/
|
|
1460
|
+
set(namespace, key, value, agent) {
|
|
1461
|
+
const ns = this.namespaces.get(namespace);
|
|
1462
|
+
if (!ns) {
|
|
1463
|
+
throw new Error(`Namespace not found: ${namespace}`);
|
|
1464
|
+
}
|
|
1465
|
+
if (ns.data.size >= this.config.maxEntriesPerNamespace && !ns.data.has(key)) {
|
|
1466
|
+
const firstKey = ns.data.keys().next().value;
|
|
1467
|
+
if (firstKey !== void 0) {
|
|
1468
|
+
ns.data.delete(firstKey);
|
|
1469
|
+
}
|
|
1470
|
+
}
|
|
1471
|
+
const previousValue = ns.data.get(key);
|
|
1472
|
+
ns.data.set(key, value);
|
|
1473
|
+
ns.lastAccessed = /* @__PURE__ */ new Date();
|
|
1474
|
+
if (this.config.trackHistory) {
|
|
1475
|
+
this.addToHistory({
|
|
1476
|
+
type: "set",
|
|
1477
|
+
key: `${namespace}:${key}`,
|
|
1478
|
+
value,
|
|
1479
|
+
previousValue,
|
|
1480
|
+
agent,
|
|
1481
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
1482
|
+
});
|
|
1483
|
+
}
|
|
1484
|
+
this.emit("change", {
|
|
1485
|
+
type: "set",
|
|
1486
|
+
key: `${namespace}:${key}`,
|
|
1487
|
+
value,
|
|
1488
|
+
previousValue,
|
|
1489
|
+
agent,
|
|
1490
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
1491
|
+
});
|
|
1492
|
+
}
|
|
1493
|
+
/**
|
|
1494
|
+
* Get a value from a namespace
|
|
1495
|
+
*/
|
|
1496
|
+
get(namespace, key) {
|
|
1497
|
+
const ns = this.namespaces.get(namespace);
|
|
1498
|
+
if (!ns) return void 0;
|
|
1499
|
+
ns.lastAccessed = /* @__PURE__ */ new Date();
|
|
1500
|
+
return ns.data.get(key);
|
|
1501
|
+
}
|
|
1502
|
+
/**
|
|
1503
|
+
* Delete a value from a namespace
|
|
1504
|
+
*/
|
|
1505
|
+
delete(namespace, key, agent) {
|
|
1506
|
+
const ns = this.namespaces.get(namespace);
|
|
1507
|
+
if (!ns) return false;
|
|
1508
|
+
const previousValue = ns.data.get(key);
|
|
1509
|
+
const deleted = ns.data.delete(key);
|
|
1510
|
+
ns.lastAccessed = /* @__PURE__ */ new Date();
|
|
1511
|
+
if (deleted && this.config.trackHistory) {
|
|
1512
|
+
this.addToHistory({
|
|
1513
|
+
type: "delete",
|
|
1514
|
+
key: `${namespace}:${key}`,
|
|
1515
|
+
previousValue,
|
|
1516
|
+
agent,
|
|
1517
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
1518
|
+
});
|
|
1519
|
+
}
|
|
1520
|
+
if (deleted) {
|
|
1521
|
+
this.emit("change", {
|
|
1522
|
+
type: "delete",
|
|
1523
|
+
key: `${namespace}:${key}`,
|
|
1524
|
+
previousValue,
|
|
1525
|
+
agent,
|
|
1526
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
1527
|
+
});
|
|
1528
|
+
}
|
|
1529
|
+
return deleted;
|
|
1530
|
+
}
|
|
1531
|
+
/**
|
|
1532
|
+
* Check if key exists in namespace
|
|
1533
|
+
*/
|
|
1534
|
+
has(namespace, key) {
|
|
1535
|
+
const ns = this.namespaces.get(namespace);
|
|
1536
|
+
if (!ns) return false;
|
|
1537
|
+
return ns.data.has(key);
|
|
1538
|
+
}
|
|
1539
|
+
/**
|
|
1540
|
+
* Get all keys in a namespace
|
|
1541
|
+
*/
|
|
1542
|
+
getKeys(namespace) {
|
|
1543
|
+
const ns = this.namespaces.get(namespace);
|
|
1544
|
+
if (!ns) return [];
|
|
1545
|
+
return Array.from(ns.data.keys());
|
|
1546
|
+
}
|
|
1547
|
+
// ============ History ============
|
|
1548
|
+
/**
|
|
1549
|
+
* Add to history
|
|
1550
|
+
*/
|
|
1551
|
+
addToHistory(event) {
|
|
1552
|
+
this.history.push(event);
|
|
1553
|
+
if (this.history.length > this.config.maxHistoryEntries) {
|
|
1554
|
+
this.history.shift();
|
|
1555
|
+
}
|
|
1556
|
+
}
|
|
1557
|
+
/**
|
|
1558
|
+
* Get change history
|
|
1559
|
+
*/
|
|
1560
|
+
getHistory(filter) {
|
|
1561
|
+
let events = [...this.history];
|
|
1562
|
+
if (filter) {
|
|
1563
|
+
if (filter.agent) {
|
|
1564
|
+
events = events.filter((e) => e.agent === filter.agent);
|
|
1565
|
+
}
|
|
1566
|
+
if (filter.key) {
|
|
1567
|
+
events = events.filter((e) => e.key?.includes(filter.key));
|
|
1568
|
+
}
|
|
1569
|
+
if (filter.type) {
|
|
1570
|
+
events = events.filter((e) => e.type === filter.type);
|
|
1571
|
+
}
|
|
1572
|
+
if (filter.since) {
|
|
1573
|
+
events = events.filter((e) => e.timestamp >= filter.since);
|
|
1574
|
+
}
|
|
1575
|
+
}
|
|
1576
|
+
return events;
|
|
1577
|
+
}
|
|
1578
|
+
/**
|
|
1579
|
+
* Clear history
|
|
1580
|
+
*/
|
|
1581
|
+
clearHistory() {
|
|
1582
|
+
this.history.length = 0;
|
|
1583
|
+
}
|
|
1584
|
+
// ============ Serialization ============
|
|
1585
|
+
/**
|
|
1586
|
+
* Export all data
|
|
1587
|
+
*/
|
|
1588
|
+
export() {
|
|
1589
|
+
const data = {};
|
|
1590
|
+
for (const [name, namespace] of this.namespaces) {
|
|
1591
|
+
data[name] = Object.fromEntries(namespace.data);
|
|
1592
|
+
}
|
|
1593
|
+
return data;
|
|
1594
|
+
}
|
|
1595
|
+
/**
|
|
1596
|
+
* Import data
|
|
1597
|
+
*/
|
|
1598
|
+
import(data) {
|
|
1599
|
+
for (const [name, entries] of Object.entries(data)) {
|
|
1600
|
+
if (!this.namespaces.has(name)) {
|
|
1601
|
+
this.createNamespace(name);
|
|
1602
|
+
}
|
|
1603
|
+
const namespace = this.namespaces.get(name);
|
|
1604
|
+
for (const [key, value] of Object.entries(entries)) {
|
|
1605
|
+
namespace.data.set(key, value);
|
|
1606
|
+
}
|
|
1607
|
+
}
|
|
1608
|
+
}
|
|
1609
|
+
/**
|
|
1610
|
+
* Clear all data
|
|
1611
|
+
*/
|
|
1612
|
+
clear() {
|
|
1613
|
+
for (const namespace of this.namespaces.values()) {
|
|
1614
|
+
namespace.data.clear();
|
|
1615
|
+
}
|
|
1616
|
+
this.history.length = 0;
|
|
1617
|
+
}
|
|
1618
|
+
/**
|
|
1619
|
+
* Get statistics
|
|
1620
|
+
*/
|
|
1621
|
+
getStatistics() {
|
|
1622
|
+
const entriesByNamespace = {};
|
|
1623
|
+
let totalEntries = 0;
|
|
1624
|
+
for (const [name, namespace] of this.namespaces) {
|
|
1625
|
+
entriesByNamespace[name] = namespace.data.size;
|
|
1626
|
+
totalEntries += namespace.data.size;
|
|
1627
|
+
}
|
|
1628
|
+
return {
|
|
1629
|
+
totalNamespaces: this.namespaces.size,
|
|
1630
|
+
totalEntries,
|
|
1631
|
+
entriesByNamespace,
|
|
1632
|
+
historyEntries: this.history.length
|
|
1633
|
+
};
|
|
1634
|
+
}
|
|
1635
|
+
};
|
|
1636
|
+
function createSharedMemory(config) {
|
|
1637
|
+
return new SharedMemory(config);
|
|
1638
|
+
}
|
|
1639
|
+
|
|
1640
|
+
// src/memory/ConversationHistory.ts
|
|
1641
|
+
import { nanoid as nanoid4 } from "nanoid";
|
|
1642
|
+
var ConversationHistory = class {
|
|
1643
|
+
agentMessages = /* @__PURE__ */ new Map();
|
|
1644
|
+
threads = /* @__PURE__ */ new Map();
|
|
1645
|
+
allMessages = [];
|
|
1646
|
+
config;
|
|
1647
|
+
currentThreadId;
|
|
1648
|
+
constructor(config = {}) {
|
|
1649
|
+
this.config = {
|
|
1650
|
+
maxMessagesPerAgent: config.maxMessagesPerAgent ?? 100,
|
|
1651
|
+
maxTotalMessages: config.maxTotalMessages ?? 1e3,
|
|
1652
|
+
enableThreads: config.enableThreads ?? true,
|
|
1653
|
+
autoSummarize: config.autoSummarize ?? false
|
|
1654
|
+
};
|
|
1655
|
+
}
|
|
1656
|
+
// ============ Message Operations ============
|
|
1657
|
+
/**
|
|
1658
|
+
* Add a message
|
|
1659
|
+
*/
|
|
1660
|
+
addMessage(agentName, message) {
|
|
1661
|
+
const fullMessage = {
|
|
1662
|
+
...message,
|
|
1663
|
+
id: nanoid4(),
|
|
1664
|
+
agentName,
|
|
1665
|
+
timestamp: /* @__PURE__ */ new Date(),
|
|
1666
|
+
threadId: this.currentThreadId
|
|
1667
|
+
};
|
|
1668
|
+
if (!this.agentMessages.has(agentName)) {
|
|
1669
|
+
this.agentMessages.set(agentName, []);
|
|
1670
|
+
}
|
|
1671
|
+
const agentMsgs = this.agentMessages.get(agentName);
|
|
1672
|
+
agentMsgs.push(fullMessage);
|
|
1673
|
+
if (agentMsgs.length > this.config.maxMessagesPerAgent) {
|
|
1674
|
+
agentMsgs.shift();
|
|
1675
|
+
}
|
|
1676
|
+
this.allMessages.push(fullMessage);
|
|
1677
|
+
if (this.allMessages.length > this.config.maxTotalMessages) {
|
|
1678
|
+
this.allMessages.shift();
|
|
1679
|
+
}
|
|
1680
|
+
if (this.config.enableThreads && this.currentThreadId) {
|
|
1681
|
+
const thread = this.threads.get(this.currentThreadId);
|
|
1682
|
+
if (thread) {
|
|
1683
|
+
thread.messages.push(fullMessage);
|
|
1684
|
+
thread.lastActivity = /* @__PURE__ */ new Date();
|
|
1685
|
+
if (!thread.participants.includes(agentName)) {
|
|
1686
|
+
thread.participants.push(agentName);
|
|
1687
|
+
}
|
|
1688
|
+
}
|
|
1689
|
+
}
|
|
1690
|
+
return fullMessage;
|
|
1691
|
+
}
|
|
1692
|
+
/**
|
|
1693
|
+
* Add a user message
|
|
1694
|
+
*/
|
|
1695
|
+
addUserMessage(content, agentName) {
|
|
1696
|
+
return this.addMessage(agentName ?? "user", {
|
|
1697
|
+
role: "user",
|
|
1698
|
+
content
|
|
1699
|
+
});
|
|
1700
|
+
}
|
|
1701
|
+
/**
|
|
1702
|
+
* Add an assistant message
|
|
1703
|
+
*/
|
|
1704
|
+
addAssistantMessage(agentName, content, metadata) {
|
|
1705
|
+
return this.addMessage(agentName, {
|
|
1706
|
+
role: "assistant",
|
|
1707
|
+
content,
|
|
1708
|
+
metadata
|
|
1709
|
+
});
|
|
1710
|
+
}
|
|
1711
|
+
/**
|
|
1712
|
+
* Add a system message
|
|
1713
|
+
*/
|
|
1714
|
+
addSystemMessage(agentName, content) {
|
|
1715
|
+
return this.addMessage(agentName, {
|
|
1716
|
+
role: "system",
|
|
1717
|
+
content
|
|
1718
|
+
});
|
|
1719
|
+
}
|
|
1720
|
+
/**
|
|
1721
|
+
* Add a tool message
|
|
1722
|
+
*/
|
|
1723
|
+
addToolMessage(agentName, toolName, input, output) {
|
|
1724
|
+
return this.addMessage(agentName, {
|
|
1725
|
+
role: "tool",
|
|
1726
|
+
content: typeof output === "string" ? output : JSON.stringify(output),
|
|
1727
|
+
metadata: {
|
|
1728
|
+
toolCall: {
|
|
1729
|
+
name: toolName,
|
|
1730
|
+
input,
|
|
1731
|
+
output
|
|
1732
|
+
}
|
|
1733
|
+
}
|
|
1734
|
+
});
|
|
1735
|
+
}
|
|
1736
|
+
// ============ Retrieval ============
|
|
1737
|
+
/**
|
|
1738
|
+
* Get messages for an agent
|
|
1739
|
+
*/
|
|
1740
|
+
getAgentHistory(agentName, options = {}) {
|
|
1741
|
+
let messages = this.agentMessages.get(agentName) ?? [];
|
|
1742
|
+
if (options.since) {
|
|
1743
|
+
messages = messages.filter((m) => m.timestamp >= options.since);
|
|
1744
|
+
}
|
|
1745
|
+
if (options.role) {
|
|
1746
|
+
messages = messages.filter((m) => m.role === options.role);
|
|
1747
|
+
}
|
|
1748
|
+
if (options.limit) {
|
|
1749
|
+
messages = messages.slice(-options.limit);
|
|
1750
|
+
}
|
|
1751
|
+
return [...messages];
|
|
1752
|
+
}
|
|
1753
|
+
/**
|
|
1754
|
+
* Get full history
|
|
1755
|
+
*/
|
|
1756
|
+
getFullHistory(options = {}) {
|
|
1757
|
+
let messages = [...this.allMessages];
|
|
1758
|
+
if (options.since) {
|
|
1759
|
+
messages = messages.filter((m) => m.timestamp >= options.since);
|
|
1760
|
+
}
|
|
1761
|
+
if (options.agents) {
|
|
1762
|
+
messages = messages.filter((m) => options.agents.includes(m.agentName));
|
|
1763
|
+
}
|
|
1764
|
+
if (options.limit) {
|
|
1765
|
+
messages = messages.slice(-options.limit);
|
|
1766
|
+
}
|
|
1767
|
+
return messages;
|
|
1768
|
+
}
|
|
1769
|
+
/**
|
|
1770
|
+
* Get conversation between agents
|
|
1771
|
+
*/
|
|
1772
|
+
getConversation(agent1, agent2, limit) {
|
|
1773
|
+
const messages = this.allMessages.filter(
|
|
1774
|
+
(m) => m.agentName === agent1 || m.agentName === agent2
|
|
1775
|
+
);
|
|
1776
|
+
if (limit) {
|
|
1777
|
+
return messages.slice(-limit);
|
|
1778
|
+
}
|
|
1779
|
+
return [...messages];
|
|
1780
|
+
}
|
|
1781
|
+
/**
|
|
1782
|
+
* Get recent messages
|
|
1783
|
+
*/
|
|
1784
|
+
getRecent(count = 10) {
|
|
1785
|
+
return this.allMessages.slice(-count);
|
|
1786
|
+
}
|
|
1787
|
+
/**
|
|
1788
|
+
* Search history
|
|
1789
|
+
*/
|
|
1790
|
+
searchHistory(query, options = {}) {
|
|
1791
|
+
const searchQuery = options.caseSensitive ? query : query.toLowerCase();
|
|
1792
|
+
let results = this.allMessages.filter((m) => {
|
|
1793
|
+
const content = options.caseSensitive ? m.content : m.content.toLowerCase();
|
|
1794
|
+
return content.includes(searchQuery);
|
|
1795
|
+
});
|
|
1796
|
+
if (options.agent) {
|
|
1797
|
+
results = results.filter((m) => m.agentName === options.agent);
|
|
1798
|
+
}
|
|
1799
|
+
if (options.limit) {
|
|
1800
|
+
results = results.slice(-options.limit);
|
|
1801
|
+
}
|
|
1802
|
+
return results;
|
|
1803
|
+
}
|
|
1804
|
+
// ============ Thread Operations ============
|
|
1805
|
+
/**
|
|
1806
|
+
* Create a new thread
|
|
1807
|
+
*/
|
|
1808
|
+
createThread(title, participants = []) {
|
|
1809
|
+
const thread = {
|
|
1810
|
+
id: nanoid4(),
|
|
1811
|
+
title,
|
|
1812
|
+
participants,
|
|
1813
|
+
messages: [],
|
|
1814
|
+
created: /* @__PURE__ */ new Date(),
|
|
1815
|
+
lastActivity: /* @__PURE__ */ new Date()
|
|
1816
|
+
};
|
|
1817
|
+
this.threads.set(thread.id, thread);
|
|
1818
|
+
this.currentThreadId = thread.id;
|
|
1819
|
+
return thread;
|
|
1820
|
+
}
|
|
1821
|
+
/**
|
|
1822
|
+
* Switch to a thread
|
|
1823
|
+
*/
|
|
1824
|
+
switchThread(threadId) {
|
|
1825
|
+
if (this.threads.has(threadId)) {
|
|
1826
|
+
this.currentThreadId = threadId;
|
|
1827
|
+
return true;
|
|
1828
|
+
}
|
|
1829
|
+
return false;
|
|
1830
|
+
}
|
|
1831
|
+
/**
|
|
1832
|
+
* Get current thread
|
|
1833
|
+
*/
|
|
1834
|
+
getCurrentThread() {
|
|
1835
|
+
if (!this.currentThreadId) return void 0;
|
|
1836
|
+
return this.threads.get(this.currentThreadId);
|
|
1837
|
+
}
|
|
1838
|
+
/**
|
|
1839
|
+
* Get a thread
|
|
1840
|
+
*/
|
|
1841
|
+
getThread(threadId) {
|
|
1842
|
+
return this.threads.get(threadId);
|
|
1843
|
+
}
|
|
1844
|
+
/**
|
|
1845
|
+
* Get all threads
|
|
1846
|
+
*/
|
|
1847
|
+
getThreads() {
|
|
1848
|
+
return Array.from(this.threads.values()).sort(
|
|
1849
|
+
(a, b) => b.lastActivity.getTime() - a.lastActivity.getTime()
|
|
1850
|
+
);
|
|
1851
|
+
}
|
|
1852
|
+
/**
|
|
1853
|
+
* Delete a thread
|
|
1854
|
+
*/
|
|
1855
|
+
deleteThread(threadId) {
|
|
1856
|
+
if (this.currentThreadId === threadId) {
|
|
1857
|
+
this.currentThreadId = void 0;
|
|
1858
|
+
}
|
|
1859
|
+
return this.threads.delete(threadId);
|
|
1860
|
+
}
|
|
1861
|
+
// ============ Context Building ============
|
|
1862
|
+
/**
|
|
1863
|
+
* Build context for an agent (recent conversation)
|
|
1864
|
+
*/
|
|
1865
|
+
buildAgentContext(agentName, maxMessages = 20) {
|
|
1866
|
+
return this.getAgentHistory(agentName, { limit: maxMessages });
|
|
1867
|
+
}
|
|
1868
|
+
/**
|
|
1869
|
+
* Build context for LLM (formatted messages)
|
|
1870
|
+
*/
|
|
1871
|
+
buildLLMContext(agentName, options = {}) {
|
|
1872
|
+
const messages = this.getAgentHistory(agentName, {
|
|
1873
|
+
limit: options.maxMessages ?? 20
|
|
1874
|
+
});
|
|
1875
|
+
return messages.filter((m) => {
|
|
1876
|
+
if (m.role === "system" && !options.includeSystem) return false;
|
|
1877
|
+
if (m.role === "tool" && !options.includeTools) return false;
|
|
1878
|
+
return true;
|
|
1879
|
+
}).map((m) => ({
|
|
1880
|
+
role: m.role,
|
|
1881
|
+
content: m.content
|
|
1882
|
+
}));
|
|
1883
|
+
}
|
|
1884
|
+
/**
|
|
1885
|
+
* Summarize conversation (placeholder for LLM summarization)
|
|
1886
|
+
*/
|
|
1887
|
+
summarize(messages) {
|
|
1888
|
+
const agentSummaries = {};
|
|
1889
|
+
let totalMessages = 0;
|
|
1890
|
+
let toolCalls = 0;
|
|
1891
|
+
for (const msg of messages) {
|
|
1892
|
+
totalMessages++;
|
|
1893
|
+
if (msg.agentName) {
|
|
1894
|
+
const agent = msg.agentName;
|
|
1895
|
+
agentSummaries[agent] = (agentSummaries[agent] ?? 0) + 1;
|
|
1896
|
+
}
|
|
1897
|
+
if (msg.metadata?.toolCall) {
|
|
1898
|
+
toolCalls++;
|
|
1899
|
+
}
|
|
1900
|
+
}
|
|
1901
|
+
const parts = [];
|
|
1902
|
+
parts.push(`Conversation with ${totalMessages} messages`);
|
|
1903
|
+
if (Object.keys(agentSummaries).length > 0) {
|
|
1904
|
+
const agentList = Object.entries(agentSummaries).map(([name, count]) => `${name} (${count})`).join(", ");
|
|
1905
|
+
parts.push(`Participants: ${agentList}`);
|
|
1906
|
+
}
|
|
1907
|
+
if (toolCalls > 0) {
|
|
1908
|
+
parts.push(`Tool calls: ${toolCalls}`);
|
|
1909
|
+
}
|
|
1910
|
+
return parts.join(". ");
|
|
1911
|
+
}
|
|
1912
|
+
// ============ Utilities ============
|
|
1913
|
+
/**
|
|
1914
|
+
* Get all participating agents
|
|
1915
|
+
*/
|
|
1916
|
+
getParticipants() {
|
|
1917
|
+
return Array.from(this.agentMessages.keys());
|
|
1918
|
+
}
|
|
1919
|
+
/**
|
|
1920
|
+
* Get message count
|
|
1921
|
+
*/
|
|
1922
|
+
getMessageCount(agentName) {
|
|
1923
|
+
if (agentName) {
|
|
1924
|
+
return this.agentMessages.get(agentName)?.length ?? 0;
|
|
1925
|
+
}
|
|
1926
|
+
return this.allMessages.length;
|
|
1927
|
+
}
|
|
1928
|
+
/**
|
|
1929
|
+
* Clear all history
|
|
1930
|
+
*/
|
|
1931
|
+
clear() {
|
|
1932
|
+
this.agentMessages.clear();
|
|
1933
|
+
this.threads.clear();
|
|
1934
|
+
this.allMessages.length = 0;
|
|
1935
|
+
this.currentThreadId = void 0;
|
|
1936
|
+
}
|
|
1937
|
+
/**
|
|
1938
|
+
* Clear history for an agent
|
|
1939
|
+
*/
|
|
1940
|
+
clearAgent(agentName) {
|
|
1941
|
+
this.agentMessages.delete(agentName);
|
|
1942
|
+
for (let i = this.allMessages.length - 1; i >= 0; i--) {
|
|
1943
|
+
if (this.allMessages[i].agentName === agentName) {
|
|
1944
|
+
this.allMessages.splice(i, 1);
|
|
1945
|
+
}
|
|
1946
|
+
}
|
|
1947
|
+
}
|
|
1948
|
+
/**
|
|
1949
|
+
* Export history
|
|
1950
|
+
*/
|
|
1951
|
+
export() {
|
|
1952
|
+
return {
|
|
1953
|
+
messages: [...this.allMessages],
|
|
1954
|
+
threads: Array.from(this.threads.values())
|
|
1955
|
+
};
|
|
1956
|
+
}
|
|
1957
|
+
/**
|
|
1958
|
+
* Import history
|
|
1959
|
+
*/
|
|
1960
|
+
import(data) {
|
|
1961
|
+
for (const msg of data.messages) {
|
|
1962
|
+
msg.timestamp = new Date(msg.timestamp);
|
|
1963
|
+
if (!this.agentMessages.has(msg.agentName)) {
|
|
1964
|
+
this.agentMessages.set(msg.agentName, []);
|
|
1965
|
+
}
|
|
1966
|
+
this.agentMessages.get(msg.agentName).push(msg);
|
|
1967
|
+
this.allMessages.push(msg);
|
|
1968
|
+
}
|
|
1969
|
+
if (data.threads) {
|
|
1970
|
+
for (const thread of data.threads) {
|
|
1971
|
+
thread.created = new Date(thread.created);
|
|
1972
|
+
thread.lastActivity = new Date(thread.lastActivity);
|
|
1973
|
+
for (const msg of thread.messages) {
|
|
1974
|
+
msg.timestamp = new Date(msg.timestamp);
|
|
1975
|
+
}
|
|
1976
|
+
this.threads.set(thread.id, thread);
|
|
1977
|
+
}
|
|
1978
|
+
}
|
|
1979
|
+
}
|
|
1980
|
+
/**
|
|
1981
|
+
* Get statistics
|
|
1982
|
+
*/
|
|
1983
|
+
getStatistics() {
|
|
1984
|
+
const messagesByAgent = {};
|
|
1985
|
+
const messagesByRole = {};
|
|
1986
|
+
for (const msg of this.allMessages) {
|
|
1987
|
+
messagesByAgent[msg.agentName] = (messagesByAgent[msg.agentName] ?? 0) + 1;
|
|
1988
|
+
messagesByRole[msg.role] = (messagesByRole[msg.role] ?? 0) + 1;
|
|
1989
|
+
}
|
|
1990
|
+
const agentCount = Object.keys(messagesByAgent).length;
|
|
1991
|
+
return {
|
|
1992
|
+
totalMessages: this.allMessages.length,
|
|
1993
|
+
messagesByAgent,
|
|
1994
|
+
messagesByRole,
|
|
1995
|
+
totalThreads: this.threads.size,
|
|
1996
|
+
averageMessagesPerAgent: agentCount > 0 ? this.allMessages.length / agentCount : 0
|
|
1997
|
+
};
|
|
1998
|
+
}
|
|
1999
|
+
};
|
|
2000
|
+
function createConversationHistory(config) {
|
|
2001
|
+
return new ConversationHistory(config);
|
|
2002
|
+
}
|
|
2003
|
+
|
|
2004
|
+
// src/memory/KnowledgeBase.ts
|
|
2005
|
+
import { nanoid as nanoid5 } from "nanoid";
|
|
2006
|
+
var KnowledgeBase = class {
|
|
2007
|
+
items = /* @__PURE__ */ new Map();
|
|
2008
|
+
tagIndex = /* @__PURE__ */ new Map();
|
|
2009
|
+
typeIndex = /* @__PURE__ */ new Map();
|
|
2010
|
+
contributorIndex = /* @__PURE__ */ new Map();
|
|
2011
|
+
config;
|
|
2012
|
+
constructor(config = {}) {
|
|
2013
|
+
this.config = {
|
|
2014
|
+
maxItems: config.maxItems ?? 1e4,
|
|
2015
|
+
autoIndex: config.autoIndex ?? true,
|
|
2016
|
+
minConfidence: config.minConfidence ?? 0,
|
|
2017
|
+
deduplicate: config.deduplicate ?? true
|
|
2018
|
+
};
|
|
2019
|
+
}
|
|
2020
|
+
// ============ CRUD Operations ============
|
|
2021
|
+
/**
|
|
2022
|
+
* Add knowledge item
|
|
2023
|
+
*/
|
|
2024
|
+
add(item) {
|
|
2025
|
+
if (item.confidence < this.config.minConfidence) {
|
|
2026
|
+
throw new Error(
|
|
2027
|
+
`Confidence ${item.confidence} below threshold ${this.config.minConfidence}`
|
|
2028
|
+
);
|
|
2029
|
+
}
|
|
2030
|
+
if (this.config.deduplicate) {
|
|
2031
|
+
const duplicate = this.findDuplicate(item.title, item.content);
|
|
2032
|
+
if (duplicate) {
|
|
2033
|
+
return this.update(duplicate.id, {
|
|
2034
|
+
...item,
|
|
2035
|
+
confidence: Math.max(duplicate.confidence, item.confidence)
|
|
2036
|
+
});
|
|
2037
|
+
}
|
|
2038
|
+
}
|
|
2039
|
+
if (this.items.size >= this.config.maxItems) {
|
|
2040
|
+
this.evictLeastUsed();
|
|
2041
|
+
}
|
|
2042
|
+
const fullItem = {
|
|
2043
|
+
...item,
|
|
2044
|
+
id: nanoid5(),
|
|
2045
|
+
created: /* @__PURE__ */ new Date(),
|
|
2046
|
+
updated: /* @__PURE__ */ new Date(),
|
|
2047
|
+
accessCount: 0
|
|
2048
|
+
};
|
|
2049
|
+
this.items.set(fullItem.id, fullItem);
|
|
2050
|
+
if (this.config.autoIndex) {
|
|
2051
|
+
this.indexItem(fullItem);
|
|
2052
|
+
}
|
|
2053
|
+
return fullItem;
|
|
2054
|
+
}
|
|
2055
|
+
/**
|
|
2056
|
+
* Get knowledge item
|
|
2057
|
+
*/
|
|
2058
|
+
get(id) {
|
|
2059
|
+
const item = this.items.get(id);
|
|
2060
|
+
if (item) {
|
|
2061
|
+
item.accessCount++;
|
|
2062
|
+
}
|
|
2063
|
+
return item;
|
|
2064
|
+
}
|
|
2065
|
+
/**
|
|
2066
|
+
* Update knowledge item
|
|
2067
|
+
*/
|
|
2068
|
+
update(id, updates) {
|
|
2069
|
+
const item = this.items.get(id);
|
|
2070
|
+
if (!item) {
|
|
2071
|
+
throw new Error(`Knowledge item not found: ${id}`);
|
|
2072
|
+
}
|
|
2073
|
+
if (this.config.autoIndex) {
|
|
2074
|
+
this.unindexItem(item);
|
|
2075
|
+
}
|
|
2076
|
+
const updatedItem = {
|
|
2077
|
+
...item,
|
|
2078
|
+
...updates,
|
|
2079
|
+
updated: /* @__PURE__ */ new Date()
|
|
2080
|
+
};
|
|
2081
|
+
this.items.set(id, updatedItem);
|
|
2082
|
+
if (this.config.autoIndex) {
|
|
2083
|
+
this.indexItem(updatedItem);
|
|
2084
|
+
}
|
|
2085
|
+
return updatedItem;
|
|
2086
|
+
}
|
|
2087
|
+
/**
|
|
2088
|
+
* Delete knowledge item
|
|
2089
|
+
*/
|
|
2090
|
+
delete(id) {
|
|
2091
|
+
const item = this.items.get(id);
|
|
2092
|
+
if (!item) return false;
|
|
2093
|
+
if (this.config.autoIndex) {
|
|
2094
|
+
this.unindexItem(item);
|
|
2095
|
+
}
|
|
2096
|
+
return this.items.delete(id);
|
|
2097
|
+
}
|
|
2098
|
+
// ============ Search & Query ============
|
|
2099
|
+
/**
|
|
2100
|
+
* Search knowledge base
|
|
2101
|
+
*/
|
|
2102
|
+
search(query, options = {}) {
|
|
2103
|
+
let results = this.textSearch(query);
|
|
2104
|
+
if (options.type) {
|
|
2105
|
+
results = results.filter((item) => item.type === options.type);
|
|
2106
|
+
}
|
|
2107
|
+
if (options.tags && options.tags.length > 0) {
|
|
2108
|
+
results = results.filter(
|
|
2109
|
+
(item) => options.tags.some((tag) => item.tags.includes(tag))
|
|
2110
|
+
);
|
|
2111
|
+
}
|
|
2112
|
+
if (options.contributor) {
|
|
2113
|
+
results = results.filter(
|
|
2114
|
+
(item) => item.contributor === options.contributor
|
|
2115
|
+
);
|
|
2116
|
+
}
|
|
2117
|
+
if (options.minConfidence !== void 0) {
|
|
2118
|
+
results = results.filter(
|
|
2119
|
+
(item) => item.confidence >= options.minConfidence
|
|
2120
|
+
);
|
|
2121
|
+
}
|
|
2122
|
+
results = this.sortResults(results, options.sortBy ?? "relevance", query);
|
|
2123
|
+
if (options.limit) {
|
|
2124
|
+
results = results.slice(0, options.limit);
|
|
2125
|
+
}
|
|
2126
|
+
for (const item of results) {
|
|
2127
|
+
item.accessCount++;
|
|
2128
|
+
}
|
|
2129
|
+
return results;
|
|
2130
|
+
}
|
|
2131
|
+
/**
|
|
2132
|
+
* Get by tag
|
|
2133
|
+
*/
|
|
2134
|
+
getByTag(tag, limit) {
|
|
2135
|
+
const ids = this.tagIndex.get(tag.toLowerCase());
|
|
2136
|
+
if (!ids) return [];
|
|
2137
|
+
let items = Array.from(ids).map((id) => this.items.get(id)).filter(Boolean);
|
|
2138
|
+
if (limit) {
|
|
2139
|
+
items = items.slice(0, limit);
|
|
2140
|
+
}
|
|
2141
|
+
return items;
|
|
2142
|
+
}
|
|
2143
|
+
/**
|
|
2144
|
+
* Get by type
|
|
2145
|
+
*/
|
|
2146
|
+
getByType(type, limit) {
|
|
2147
|
+
const ids = this.typeIndex.get(type);
|
|
2148
|
+
if (!ids) return [];
|
|
2149
|
+
let items = Array.from(ids).map((id) => this.items.get(id)).filter(Boolean);
|
|
2150
|
+
if (limit) {
|
|
2151
|
+
items = items.slice(0, limit);
|
|
2152
|
+
}
|
|
2153
|
+
return items;
|
|
2154
|
+
}
|
|
2155
|
+
/**
|
|
2156
|
+
* Get by contributor
|
|
2157
|
+
*/
|
|
2158
|
+
getByContributor(contributor, limit) {
|
|
2159
|
+
const ids = this.contributorIndex.get(contributor);
|
|
2160
|
+
if (!ids) return [];
|
|
2161
|
+
let items = Array.from(ids).map((id) => this.items.get(id)).filter(Boolean);
|
|
2162
|
+
if (limit) {
|
|
2163
|
+
items = items.slice(0, limit);
|
|
2164
|
+
}
|
|
2165
|
+
return items;
|
|
2166
|
+
}
|
|
2167
|
+
/**
|
|
2168
|
+
* Get related items
|
|
2169
|
+
*/
|
|
2170
|
+
getRelated(id, limit = 5) {
|
|
2171
|
+
const item = this.items.get(id);
|
|
2172
|
+
if (!item) return [];
|
|
2173
|
+
const relatedIds = /* @__PURE__ */ new Set();
|
|
2174
|
+
for (const tag of item.tags) {
|
|
2175
|
+
const taggedIds = this.tagIndex.get(tag.toLowerCase());
|
|
2176
|
+
if (taggedIds) {
|
|
2177
|
+
for (const relatedId of taggedIds) {
|
|
2178
|
+
if (relatedId !== id) {
|
|
2179
|
+
relatedIds.add(relatedId);
|
|
2180
|
+
}
|
|
2181
|
+
}
|
|
2182
|
+
}
|
|
2183
|
+
}
|
|
2184
|
+
const scored = Array.from(relatedIds).map((relatedId) => {
|
|
2185
|
+
const relatedItem = this.items.get(relatedId);
|
|
2186
|
+
const overlap = item.tags.filter(
|
|
2187
|
+
(t) => relatedItem.tags.includes(t)
|
|
2188
|
+
).length;
|
|
2189
|
+
return { item: relatedItem, score: overlap };
|
|
2190
|
+
});
|
|
2191
|
+
return scored.sort((a, b) => b.score - a.score).slice(0, limit).map((s) => s.item);
|
|
2192
|
+
}
|
|
2193
|
+
// ============ Agent Contributions ============
|
|
2194
|
+
/**
|
|
2195
|
+
* Contribute knowledge from an agent
|
|
2196
|
+
*/
|
|
2197
|
+
contributeKnowledge(agentName, type, title, content, tags = [], confidence = 0.8) {
|
|
2198
|
+
return this.add({
|
|
2199
|
+
type,
|
|
2200
|
+
title,
|
|
2201
|
+
content,
|
|
2202
|
+
tags,
|
|
2203
|
+
contributor: agentName,
|
|
2204
|
+
confidence
|
|
2205
|
+
});
|
|
2206
|
+
}
|
|
2207
|
+
/**
|
|
2208
|
+
* Add a fact
|
|
2209
|
+
*/
|
|
2210
|
+
addFact(title, content, tags = [], source) {
|
|
2211
|
+
return this.add({
|
|
2212
|
+
type: "fact",
|
|
2213
|
+
title,
|
|
2214
|
+
content,
|
|
2215
|
+
tags,
|
|
2216
|
+
source,
|
|
2217
|
+
confidence: 0.9
|
|
2218
|
+
});
|
|
2219
|
+
}
|
|
2220
|
+
/**
|
|
2221
|
+
* Add a procedure
|
|
2222
|
+
*/
|
|
2223
|
+
addProcedure(title, steps, tags = []) {
|
|
2224
|
+
return this.add({
|
|
2225
|
+
type: "procedure",
|
|
2226
|
+
title,
|
|
2227
|
+
content: steps.map((s, i) => `${i + 1}. ${s}`).join("\n"),
|
|
2228
|
+
tags,
|
|
2229
|
+
confidence: 0.85
|
|
2230
|
+
});
|
|
2231
|
+
}
|
|
2232
|
+
/**
|
|
2233
|
+
* Add an insight
|
|
2234
|
+
*/
|
|
2235
|
+
addInsight(title, content, contributor, tags = []) {
|
|
2236
|
+
return this.add({
|
|
2237
|
+
type: "insight",
|
|
2238
|
+
title,
|
|
2239
|
+
content,
|
|
2240
|
+
tags,
|
|
2241
|
+
contributor,
|
|
2242
|
+
confidence: 0.7
|
|
2243
|
+
});
|
|
2244
|
+
}
|
|
2245
|
+
/**
|
|
2246
|
+
* Add a warning
|
|
2247
|
+
*/
|
|
2248
|
+
addWarning(title, content, tags = []) {
|
|
2249
|
+
return this.add({
|
|
2250
|
+
type: "warning",
|
|
2251
|
+
title,
|
|
2252
|
+
content,
|
|
2253
|
+
tags,
|
|
2254
|
+
confidence: 0.95
|
|
2255
|
+
});
|
|
2256
|
+
}
|
|
2257
|
+
// ============ Indexing ============
|
|
2258
|
+
/**
|
|
2259
|
+
* Index an item
|
|
2260
|
+
*/
|
|
2261
|
+
indexItem(item) {
|
|
2262
|
+
for (const tag of item.tags) {
|
|
2263
|
+
const tagLower = tag.toLowerCase();
|
|
2264
|
+
if (!this.tagIndex.has(tagLower)) {
|
|
2265
|
+
this.tagIndex.set(tagLower, /* @__PURE__ */ new Set());
|
|
2266
|
+
}
|
|
2267
|
+
this.tagIndex.get(tagLower).add(item.id);
|
|
2268
|
+
}
|
|
2269
|
+
if (!this.typeIndex.has(item.type)) {
|
|
2270
|
+
this.typeIndex.set(item.type, /* @__PURE__ */ new Set());
|
|
2271
|
+
}
|
|
2272
|
+
this.typeIndex.get(item.type).add(item.id);
|
|
2273
|
+
if (item.contributor) {
|
|
2274
|
+
if (!this.contributorIndex.has(item.contributor)) {
|
|
2275
|
+
this.contributorIndex.set(item.contributor, /* @__PURE__ */ new Set());
|
|
2276
|
+
}
|
|
2277
|
+
this.contributorIndex.get(item.contributor).add(item.id);
|
|
2278
|
+
}
|
|
2279
|
+
}
|
|
2280
|
+
/**
|
|
2281
|
+
* Remove item from indices
|
|
2282
|
+
*/
|
|
2283
|
+
unindexItem(item) {
|
|
2284
|
+
for (const tag of item.tags) {
|
|
2285
|
+
const tagLower = tag.toLowerCase();
|
|
2286
|
+
this.tagIndex.get(tagLower)?.delete(item.id);
|
|
2287
|
+
}
|
|
2288
|
+
this.typeIndex.get(item.type)?.delete(item.id);
|
|
2289
|
+
if (item.contributor) {
|
|
2290
|
+
this.contributorIndex.get(item.contributor)?.delete(item.id);
|
|
2291
|
+
}
|
|
2292
|
+
}
|
|
2293
|
+
/**
|
|
2294
|
+
* Text search
|
|
2295
|
+
*/
|
|
2296
|
+
textSearch(query) {
|
|
2297
|
+
const queryLower = query.toLowerCase();
|
|
2298
|
+
const queryWords = queryLower.split(/\s+/);
|
|
2299
|
+
const scored = [];
|
|
2300
|
+
for (const item of this.items.values()) {
|
|
2301
|
+
let score = 0;
|
|
2302
|
+
const titleLower = item.title.toLowerCase();
|
|
2303
|
+
const contentLower = item.content.toLowerCase();
|
|
2304
|
+
if (titleLower.includes(queryLower)) {
|
|
2305
|
+
score += 3;
|
|
2306
|
+
}
|
|
2307
|
+
if (contentLower.includes(queryLower)) {
|
|
2308
|
+
score += 1;
|
|
2309
|
+
}
|
|
2310
|
+
for (const word of queryWords) {
|
|
2311
|
+
if (titleLower.includes(word)) score += 0.5;
|
|
2312
|
+
if (contentLower.includes(word)) score += 0.2;
|
|
2313
|
+
if (item.tags.some((t) => t.toLowerCase().includes(word))) score += 0.3;
|
|
2314
|
+
}
|
|
2315
|
+
if (score > 0) {
|
|
2316
|
+
scored.push({ item, score });
|
|
2317
|
+
}
|
|
2318
|
+
}
|
|
2319
|
+
return scored.sort((a, b) => b.score - a.score).map((s) => s.item);
|
|
2320
|
+
}
|
|
2321
|
+
/**
|
|
2322
|
+
* Sort results
|
|
2323
|
+
*/
|
|
2324
|
+
sortResults(items, sortBy, _query) {
|
|
2325
|
+
switch (sortBy) {
|
|
2326
|
+
case "recency":
|
|
2327
|
+
return items.sort((a, b) => b.updated.getTime() - a.updated.getTime());
|
|
2328
|
+
case "confidence":
|
|
2329
|
+
return items.sort((a, b) => b.confidence - a.confidence);
|
|
2330
|
+
case "access":
|
|
2331
|
+
return items.sort((a, b) => b.accessCount - a.accessCount);
|
|
2332
|
+
case "relevance":
|
|
2333
|
+
default:
|
|
2334
|
+
return items;
|
|
2335
|
+
}
|
|
2336
|
+
}
|
|
2337
|
+
/**
|
|
2338
|
+
* Find duplicate
|
|
2339
|
+
*/
|
|
2340
|
+
findDuplicate(title, content) {
|
|
2341
|
+
const titleLower = title.toLowerCase();
|
|
2342
|
+
const contentLower = content.toLowerCase().substring(0, 200);
|
|
2343
|
+
for (const item of this.items.values()) {
|
|
2344
|
+
if (item.title.toLowerCase() === titleLower || item.content.toLowerCase().substring(0, 200) === contentLower) {
|
|
2345
|
+
return item;
|
|
2346
|
+
}
|
|
2347
|
+
}
|
|
2348
|
+
return void 0;
|
|
2349
|
+
}
|
|
2350
|
+
/**
|
|
2351
|
+
* Evict least used item
|
|
2352
|
+
*/
|
|
2353
|
+
evictLeastUsed() {
|
|
2354
|
+
let leastUsed;
|
|
2355
|
+
let minAccess = Infinity;
|
|
2356
|
+
for (const item of this.items.values()) {
|
|
2357
|
+
if (item.accessCount < minAccess) {
|
|
2358
|
+
minAccess = item.accessCount;
|
|
2359
|
+
leastUsed = item;
|
|
2360
|
+
}
|
|
2361
|
+
}
|
|
2362
|
+
if (leastUsed) {
|
|
2363
|
+
this.delete(leastUsed.id);
|
|
2364
|
+
}
|
|
2365
|
+
}
|
|
2366
|
+
// ============ Utilities ============
|
|
2367
|
+
/**
|
|
2368
|
+
* Get all tags
|
|
2369
|
+
*/
|
|
2370
|
+
getTags() {
|
|
2371
|
+
return Array.from(this.tagIndex.keys());
|
|
2372
|
+
}
|
|
2373
|
+
/**
|
|
2374
|
+
* Get all types
|
|
2375
|
+
*/
|
|
2376
|
+
getTypes() {
|
|
2377
|
+
return Array.from(this.typeIndex.keys());
|
|
2378
|
+
}
|
|
2379
|
+
/**
|
|
2380
|
+
* Get all contributors
|
|
2381
|
+
*/
|
|
2382
|
+
getContributors() {
|
|
2383
|
+
return Array.from(this.contributorIndex.keys());
|
|
2384
|
+
}
|
|
2385
|
+
/**
|
|
2386
|
+
* Get all items
|
|
2387
|
+
*/
|
|
2388
|
+
getAll() {
|
|
2389
|
+
return Array.from(this.items.values());
|
|
2390
|
+
}
|
|
2391
|
+
/**
|
|
2392
|
+
* Get count
|
|
2393
|
+
*/
|
|
2394
|
+
getCount() {
|
|
2395
|
+
return this.items.size;
|
|
2396
|
+
}
|
|
2397
|
+
/**
|
|
2398
|
+
* Clear all
|
|
2399
|
+
*/
|
|
2400
|
+
clear() {
|
|
2401
|
+
this.items.clear();
|
|
2402
|
+
this.tagIndex.clear();
|
|
2403
|
+
this.typeIndex.clear();
|
|
2404
|
+
this.contributorIndex.clear();
|
|
2405
|
+
}
|
|
2406
|
+
/**
|
|
2407
|
+
* Export
|
|
2408
|
+
*/
|
|
2409
|
+
export() {
|
|
2410
|
+
return Array.from(this.items.values());
|
|
2411
|
+
}
|
|
2412
|
+
/**
|
|
2413
|
+
* Import
|
|
2414
|
+
*/
|
|
2415
|
+
import(items) {
|
|
2416
|
+
let count = 0;
|
|
2417
|
+
for (const item of items) {
|
|
2418
|
+
item.created = new Date(item.created);
|
|
2419
|
+
item.updated = new Date(item.updated);
|
|
2420
|
+
this.items.set(item.id, item);
|
|
2421
|
+
if (this.config.autoIndex) {
|
|
2422
|
+
this.indexItem(item);
|
|
2423
|
+
}
|
|
2424
|
+
count++;
|
|
2425
|
+
}
|
|
2426
|
+
return count;
|
|
2427
|
+
}
|
|
2428
|
+
/**
|
|
2429
|
+
* Get statistics
|
|
2430
|
+
*/
|
|
2431
|
+
getStatistics() {
|
|
2432
|
+
const itemsByType = {};
|
|
2433
|
+
const itemsByContributor = {};
|
|
2434
|
+
let totalConfidence = 0;
|
|
2435
|
+
for (const item of this.items.values()) {
|
|
2436
|
+
itemsByType[item.type] = (itemsByType[item.type] ?? 0) + 1;
|
|
2437
|
+
if (item.contributor) {
|
|
2438
|
+
itemsByContributor[item.contributor] = (itemsByContributor[item.contributor] ?? 0) + 1;
|
|
2439
|
+
}
|
|
2440
|
+
totalConfidence += item.confidence;
|
|
2441
|
+
}
|
|
2442
|
+
const mostUsedTags = Array.from(this.tagIndex.entries()).map(([tag, ids]) => ({ tag, count: ids.size })).sort((a, b) => b.count - a.count).slice(0, 10);
|
|
2443
|
+
return {
|
|
2444
|
+
totalItems: this.items.size,
|
|
2445
|
+
itemsByType,
|
|
2446
|
+
itemsByContributor,
|
|
2447
|
+
totalTags: this.tagIndex.size,
|
|
2448
|
+
averageConfidence: this.items.size > 0 ? totalConfidence / this.items.size : 0,
|
|
2449
|
+
mostUsedTags
|
|
2450
|
+
};
|
|
2451
|
+
}
|
|
2452
|
+
};
|
|
2453
|
+
function createKnowledgeBase(config) {
|
|
2454
|
+
return new KnowledgeBase(config);
|
|
2455
|
+
}
|
|
2456
|
+
export {
|
|
2457
|
+
AgentCapabilities,
|
|
2458
|
+
AgentRegistry,
|
|
2459
|
+
AuctionStrategy,
|
|
2460
|
+
BaseDelegationStrategy,
|
|
2461
|
+
BestMatchStrategy,
|
|
2462
|
+
BranchBuilder,
|
|
2463
|
+
CheckpointManager,
|
|
2464
|
+
CodeReviewTasks,
|
|
2465
|
+
CollaborationManager,
|
|
2466
|
+
ConflictResolver,
|
|
2467
|
+
ConsensusStrategy,
|
|
2468
|
+
ConversationHistory,
|
|
2469
|
+
Crew,
|
|
2470
|
+
CrewAgent,
|
|
2471
|
+
CrewDashboard,
|
|
2472
|
+
CustomerSupportTasks,
|
|
2473
|
+
DAGExecutor,
|
|
2474
|
+
DebugMode,
|
|
2475
|
+
DelegationCoordinator,
|
|
2476
|
+
DelegationError,
|
|
2477
|
+
ExecutionContext,
|
|
2478
|
+
HierarchicalStrategy,
|
|
2479
|
+
InMemoryCheckpointStorage,
|
|
2480
|
+
KnowledgeBase,
|
|
2481
|
+
LoopBuilder,
|
|
2482
|
+
PRIORITY_WEIGHTS,
|
|
2483
|
+
PROFICIENCY_WEIGHTS,
|
|
2484
|
+
ParallelExecutor,
|
|
2485
|
+
ResearchTasks,
|
|
2486
|
+
Role,
|
|
2487
|
+
RoundRobinStrategy,
|
|
2488
|
+
STRATEGY_TYPES,
|
|
2489
|
+
SharedMemory,
|
|
2490
|
+
Task,
|
|
2491
|
+
TaskQueue,
|
|
2492
|
+
WorkflowBuilder,
|
|
2493
|
+
WritingTasks,
|
|
2494
|
+
createAgentRegistry,
|
|
2495
|
+
createAuctionStrategy,
|
|
2496
|
+
createBestMatchStrategy,
|
|
2497
|
+
createCheckpointManager,
|
|
2498
|
+
createCodeReviewCrew,
|
|
2499
|
+
createCodeReviewCrewConfig,
|
|
2500
|
+
createCollaborationManager,
|
|
2501
|
+
createConflictResolver,
|
|
2502
|
+
createConsensusStrategy,
|
|
2503
|
+
createConversationHistory,
|
|
2504
|
+
createCrew,
|
|
2505
|
+
createCrewAgent,
|
|
2506
|
+
createCustomerSupportCrew,
|
|
2507
|
+
createCustomerSupportCrewConfig,
|
|
2508
|
+
createDAGExecutor,
|
|
2509
|
+
createDAGFromSteps,
|
|
2510
|
+
createDashboard,
|
|
2511
|
+
createDebugMode,
|
|
2512
|
+
createDelegationCoordinator,
|
|
2513
|
+
createExecutionContext,
|
|
2514
|
+
createHierarchicalStrategy,
|
|
2515
|
+
createKnowledgeBase,
|
|
2516
|
+
createParallelExecutor,
|
|
2517
|
+
createResearchCrew,
|
|
2518
|
+
createResearchCrewConfig,
|
|
2519
|
+
createRole,
|
|
2520
|
+
createRoundRobinStrategy,
|
|
2521
|
+
createSharedMemory,
|
|
2522
|
+
createStrategy,
|
|
2523
|
+
createTask,
|
|
2524
|
+
createTaskQueue,
|
|
2525
|
+
createWritingCrew,
|
|
2526
|
+
createWritingCrewConfig,
|
|
2527
|
+
workflow
|
|
2528
|
+
};
|