@nebulaos/core 0.1.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/README.md +206 -0
- package/dist/__tests__/mocks/mock-provider.d.ts +15 -0
- package/dist/__tests__/mocks/mock-provider.js +44 -0
- package/dist/agent/Agent.d.ts +96 -0
- package/dist/agent/Agent.js +861 -0
- package/dist/agent/BaseAgent.d.ts +53 -0
- package/dist/agent/BaseAgent.js +126 -0
- package/dist/agent/events/events.d.ts +14 -0
- package/dist/agent/events/events.js +2 -0
- package/dist/agent/events/events.spec.d.ts +1 -0
- package/dist/agent/events/events.spec.js +75 -0
- package/dist/agent/instruction/index.d.ts +23 -0
- package/dist/agent/instruction/index.js +76 -0
- package/dist/agent/memory/in-memory.d.ts +24 -0
- package/dist/agent/memory/in-memory.js +78 -0
- package/dist/agent/memory/index.d.ts +2 -0
- package/dist/agent/memory/index.js +18 -0
- package/dist/agent/memory/memory.d.ts +43 -0
- package/dist/agent/memory/memory.js +7 -0
- package/dist/agent/provider/file-parts.spec.d.ts +1 -0
- package/dist/agent/provider/file-parts.spec.js +83 -0
- package/dist/agent/provider/index.d.ts +130 -0
- package/dist/agent/provider/index.js +8 -0
- package/dist/agent/skills/index.d.ts +61 -0
- package/dist/agent/skills/index.js +9 -0
- package/dist/agent/tools/index.d.ts +35 -0
- package/dist/agent/tools/index.js +87 -0
- package/dist/cost/add-cost.d.ts +10 -0
- package/dist/cost/add-cost.js +80 -0
- package/dist/cost/add-cost.spec.d.ts +1 -0
- package/dist/cost/add-cost.spec.js +36 -0
- package/dist/cost/index.d.ts +1 -0
- package/dist/cost/index.js +17 -0
- package/dist/domain-events/index.d.ts +16 -0
- package/dist/domain-events/index.js +38 -0
- package/dist/eval/index.d.ts +19 -0
- package/dist/eval/index.js +24 -0
- package/dist/events/base.d.ts +5 -0
- package/dist/events/base.js +2 -0
- package/dist/events/schemas.d.ts +3463 -0
- package/dist/events/schemas.js +244 -0
- package/dist/execution-context/index.d.ts +21 -0
- package/dist/execution-context/index.js +17 -0
- package/dist/index.cjs +2958 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +3425 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.js +53 -0
- package/dist/index.js.map +1 -0
- package/dist/lgpd/index.d.ts +7 -0
- package/dist/lgpd/index.js +21 -0
- package/dist/logger/agent-logger.d.ts +16 -0
- package/dist/logger/agent-logger.js +110 -0
- package/dist/logger/formatters.d.ts +32 -0
- package/dist/logger/formatters.js +146 -0
- package/dist/logger/index.d.ts +30 -0
- package/dist/logger/index.js +88 -0
- package/dist/logger/styles.d.ts +46 -0
- package/dist/logger/styles.js +53 -0
- package/dist/logger/workflow-logger.d.ts +16 -0
- package/dist/logger/workflow-logger.js +79 -0
- package/dist/multi-agent/agent-as-tool/AgentAsTool.d.ts +16 -0
- package/dist/multi-agent/agent-as-tool/AgentAsTool.js +54 -0
- package/dist/multi-agent/agent-as-tool/AgentAsTool.spec.d.ts +1 -0
- package/dist/multi-agent/agent-as-tool/AgentAsTool.spec.js +76 -0
- package/dist/multi-agent/committee-team/CommitteeTeam.d.ts +16 -0
- package/dist/multi-agent/committee-team/CommitteeTeam.js +150 -0
- package/dist/multi-agent/committee-team/CommitteeTeam.spec.d.ts +1 -0
- package/dist/multi-agent/committee-team/CommitteeTeam.spec.js +43 -0
- package/dist/multi-agent/handoff-team/HandoffTeam.d.ts +16 -0
- package/dist/multi-agent/handoff-team/HandoffTeam.js +185 -0
- package/dist/multi-agent/handoff-team/HandoffTeam.spec.d.ts +1 -0
- package/dist/multi-agent/handoff-team/HandoffTeam.spec.js +105 -0
- package/dist/multi-agent/hierarchical-team/HierarchicalTeam.d.ts +18 -0
- package/dist/multi-agent/hierarchical-team/HierarchicalTeam.js +164 -0
- package/dist/multi-agent/hierarchical-team/HierarchicalTeam.spec.d.ts +1 -0
- package/dist/multi-agent/hierarchical-team/HierarchicalTeam.spec.js +53 -0
- package/dist/multi-agent/index.d.ts +10 -0
- package/dist/multi-agent/index.js +26 -0
- package/dist/multi-agent/pipeline-team/PipelineTeam.d.ts +13 -0
- package/dist/multi-agent/pipeline-team/PipelineTeam.js +104 -0
- package/dist/multi-agent/pipeline-team/PipelineTeam.spec.d.ts +1 -0
- package/dist/multi-agent/pipeline-team/PipelineTeam.spec.js +54 -0
- package/dist/multi-agent/router-team/RouterTeam.d.ts +15 -0
- package/dist/multi-agent/router-team/RouterTeam.js +153 -0
- package/dist/multi-agent/router-team/RouterTeam.spec.d.ts +1 -0
- package/dist/multi-agent/router-team/RouterTeam.spec.js +69 -0
- package/dist/multi-agent/types/index.d.ts +349 -0
- package/dist/multi-agent/types/index.js +79 -0
- package/dist/multi-agent/utils/guardrails.d.ts +6 -0
- package/dist/multi-agent/utils/guardrails.js +34 -0
- package/dist/multi-agent/utils/memory.d.ts +8 -0
- package/dist/multi-agent/utils/memory.js +40 -0
- package/dist/multi-agent/utils/prompts.d.ts +4 -0
- package/dist/multi-agent/utils/prompts.js +25 -0
- package/dist/tracing/index.d.ts +89 -0
- package/dist/tracing/index.js +188 -0
- package/dist/tsup.config.d.ts +2 -0
- package/dist/tsup.config.js +11 -0
- package/dist/utils/schema-to-zod.d.ts +7 -0
- package/dist/utils/schema-to-zod.js +36 -0
- package/dist/workflow/Workflow.d.ts +106 -0
- package/dist/workflow/Workflow.js +204 -0
- package/dist/workflow/adapters.d.ts +61 -0
- package/dist/workflow/adapters.js +29 -0
- package/dist/workflow/definition/DefinitionBuilder.d.ts +9 -0
- package/dist/workflow/definition/DefinitionBuilder.js +91 -0
- package/dist/workflow/definition/DefinitionBuilder.spec.d.ts +1 -0
- package/dist/workflow/definition/DefinitionBuilder.spec.js +66 -0
- package/dist/workflow/definition/DefinitionHasher.d.ts +8 -0
- package/dist/workflow/definition/DefinitionHasher.js +11 -0
- package/dist/workflow/definition/DefinitionHasher.spec.d.ts +1 -0
- package/dist/workflow/definition/DefinitionHasher.spec.js +28 -0
- package/dist/workflow/definition/types.d.ts +27 -0
- package/dist/workflow/definition/types.js +2 -0
- package/dist/workflow/events.d.ts +9 -0
- package/dist/workflow/events.js +2 -0
- package/dist/workflow/execution/AgentNodeIntegration.spec.d.ts +1 -0
- package/dist/workflow/execution/AgentNodeIntegration.spec.js +50 -0
- package/dist/workflow/execution/NodeExecutor.d.ts +9 -0
- package/dist/workflow/execution/NodeExecutor.js +43 -0
- package/dist/workflow/execution/NodeExecutor.spec.d.ts +1 -0
- package/dist/workflow/execution/NodeExecutor.spec.js +45 -0
- package/dist/workflow/execution/WorkflowEventBus.d.ts +14 -0
- package/dist/workflow/execution/WorkflowEventBus.js +42 -0
- package/dist/workflow/execution/WorkflowEventBus.spec.d.ts +1 -0
- package/dist/workflow/execution/WorkflowEventBus.spec.js +78 -0
- package/dist/workflow/execution/WorkflowRunner.d.ts +26 -0
- package/dist/workflow/execution/WorkflowRunner.js +212 -0
- package/dist/workflow/execution/WorkflowRunner.spec.d.ts +1 -0
- package/dist/workflow/execution/WorkflowRunner.spec.js +92 -0
- package/dist/workflow/execution/WorkflowTelemetry.d.ts +13 -0
- package/dist/workflow/execution/WorkflowTelemetry.js +43 -0
- package/dist/workflow/execution/WorkflowTelemetry.spec.d.ts +1 -0
- package/dist/workflow/execution/WorkflowTelemetry.spec.js +31 -0
- package/dist/workflow/graph/NodeNameRegistry.d.ts +20 -0
- package/dist/workflow/graph/NodeNameRegistry.js +21 -0
- package/dist/workflow/graph/NodeNameRegistry.spec.d.ts +1 -0
- package/dist/workflow/graph/NodeNameRegistry.spec.js +18 -0
- package/dist/workflow/graph/WorkflowGraph.d.ts +14 -0
- package/dist/workflow/graph/WorkflowGraph.js +23 -0
- package/dist/workflow/graph/nodes.d.ts +26 -0
- package/dist/workflow/graph/nodes.js +2 -0
- package/dist/workflow/queue/WorkflowQueueService.d.ts +22 -0
- package/dist/workflow/queue/WorkflowQueueService.js +47 -0
- package/dist/workflow/state/WorkflowStateService.d.ts +7 -0
- package/dist/workflow/state/WorkflowStateService.js +20 -0
- package/dist/workflow/types.d.ts +16 -0
- package/dist/workflow/types.js +2 -0
- package/package.json +56 -0
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AgentAsTool = void 0;
|
|
4
|
+
const index_js_1 = require("../../agent/tools/index.js");
|
|
5
|
+
const memory_js_1 = require("../utils/memory.js");
|
|
6
|
+
class AgentAsTool extends index_js_1.Tool {
|
|
7
|
+
callingMemory;
|
|
8
|
+
target;
|
|
9
|
+
memoryConfig;
|
|
10
|
+
extraPrompt;
|
|
11
|
+
constructor(config) {
|
|
12
|
+
const toolId = config.id ??
|
|
13
|
+
`delegate_${config.agent.name.toLowerCase().replace(/\s+/g, "_")}`;
|
|
14
|
+
const description = config.description ??
|
|
15
|
+
`Delegate to specialist agent: ${config.agent.name}`;
|
|
16
|
+
super({
|
|
17
|
+
id: toolId,
|
|
18
|
+
description,
|
|
19
|
+
inputSchema: config.inputSchema,
|
|
20
|
+
handler: async (_, input) => {
|
|
21
|
+
return this.runDelegated(input);
|
|
22
|
+
},
|
|
23
|
+
});
|
|
24
|
+
this.target = config.agent;
|
|
25
|
+
this.memoryConfig = config.memory;
|
|
26
|
+
this.extraPrompt = config.additionalPrompt;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Allows orchestrators to set the calling agent/team memory when using
|
|
30
|
+
* `memory: { type: "shared" }`.
|
|
31
|
+
*/
|
|
32
|
+
setCallingMemory(memory) {
|
|
33
|
+
this.callingMemory = memory;
|
|
34
|
+
}
|
|
35
|
+
async runDelegated(input) {
|
|
36
|
+
const { restore } = await (0, memory_js_1.resolveDelegationMemory)(this.target, this.memoryConfig, this.callingMemory);
|
|
37
|
+
try {
|
|
38
|
+
const formattedInput = typeof input === "string" ? input : JSON.stringify(input);
|
|
39
|
+
const userContent = this.extraPrompt
|
|
40
|
+
? `${this.extraPrompt}\nInput: ${formattedInput}`
|
|
41
|
+
: formattedInput;
|
|
42
|
+
await this.target.config.memory.addMessage({
|
|
43
|
+
role: "user",
|
|
44
|
+
content: userContent,
|
|
45
|
+
});
|
|
46
|
+
const result = await this.target.execute();
|
|
47
|
+
return result.content;
|
|
48
|
+
}
|
|
49
|
+
finally {
|
|
50
|
+
restore();
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
exports.AgentAsTool = AgentAsTool;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const Agent_js_1 = require("../../agent/Agent.js");
|
|
4
|
+
const in_memory_js_1 = require("../../agent/memory/in-memory.js");
|
|
5
|
+
const AgentAsTool_js_1 = require("./AgentAsTool.js");
|
|
6
|
+
const mock_provider_js_1 = require("../../__tests__/mocks/mock-provider.js");
|
|
7
|
+
const zod_1 = require("zod");
|
|
8
|
+
function agentWithResponse(name, content, memory) {
|
|
9
|
+
const provider = new mock_provider_js_1.MockProvider();
|
|
10
|
+
provider.enqueueResponse({ content });
|
|
11
|
+
return new Agent_js_1.Agent({
|
|
12
|
+
id: name,
|
|
13
|
+
name,
|
|
14
|
+
model: provider,
|
|
15
|
+
memory: memory ?? new in_memory_js_1.InMemory(),
|
|
16
|
+
instructions: `${name} instructions`,
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
describe("AgentAsTool", () => {
|
|
20
|
+
it("delegates to target agent and returns its content", async () => {
|
|
21
|
+
const target = agentWithResponse("Target", "target-content");
|
|
22
|
+
const tool = new AgentAsTool_js_1.AgentAsTool({ agent: target, id: "delegate_target" });
|
|
23
|
+
const output = await tool.execute({}, { question: "hi" });
|
|
24
|
+
expect(output).toBe("target-content");
|
|
25
|
+
});
|
|
26
|
+
it("supports custom memory for delegation", async () => {
|
|
27
|
+
const customMemory = new in_memory_js_1.InMemory();
|
|
28
|
+
const target = agentWithResponse("Target", "custom-memory");
|
|
29
|
+
const tool = new AgentAsTool_js_1.AgentAsTool({
|
|
30
|
+
agent: target,
|
|
31
|
+
memory: { type: "custom", memory: customMemory },
|
|
32
|
+
});
|
|
33
|
+
await tool.execute({}, "hello");
|
|
34
|
+
const stored = await customMemory.getMessages();
|
|
35
|
+
expect(stored[0]?.content).toContain("hello");
|
|
36
|
+
});
|
|
37
|
+
it("propagates calling memory when using shared delegation", async () => {
|
|
38
|
+
const callingMemory = new in_memory_js_1.InMemory();
|
|
39
|
+
await callingMemory.addMessage({ role: "user", content: "history" });
|
|
40
|
+
const targetMemory = new in_memory_js_1.InMemory();
|
|
41
|
+
const target = agentWithResponse("Target", "ok", targetMemory);
|
|
42
|
+
const tool = new AgentAsTool_js_1.AgentAsTool({
|
|
43
|
+
agent: target,
|
|
44
|
+
memory: { type: "shared" },
|
|
45
|
+
});
|
|
46
|
+
tool.setCallingMemory(callingMemory);
|
|
47
|
+
await tool.execute({}, "question");
|
|
48
|
+
const stored = await targetMemory.getMessages();
|
|
49
|
+
// history from calling memory + new input
|
|
50
|
+
expect(stored.some((m) => m.content === "history")).toBe(true);
|
|
51
|
+
expect(stored.some((m) => String(m.content).includes("question"))).toBe(true);
|
|
52
|
+
});
|
|
53
|
+
it("isolated delegation should not leak calling memory", async () => {
|
|
54
|
+
const callingMemory = new in_memory_js_1.InMemory();
|
|
55
|
+
await callingMemory.addMessage({ role: "user", content: "history" });
|
|
56
|
+
const targetMemory = new in_memory_js_1.InMemory();
|
|
57
|
+
const target = agentWithResponse("Target", "ok", targetMemory);
|
|
58
|
+
const tool = new AgentAsTool_js_1.AgentAsTool({
|
|
59
|
+
agent: target,
|
|
60
|
+
memory: { type: "isolated" },
|
|
61
|
+
});
|
|
62
|
+
tool.setCallingMemory(callingMemory);
|
|
63
|
+
await tool.execute({}, "question");
|
|
64
|
+
const stored = await targetMemory.getMessages();
|
|
65
|
+
expect(stored.some((m) => m.content === "history")).toBe(false);
|
|
66
|
+
});
|
|
67
|
+
it("validates input via inputSchema", async () => {
|
|
68
|
+
const target = agentWithResponse("Target", "ok");
|
|
69
|
+
const tool = new AgentAsTool_js_1.AgentAsTool({
|
|
70
|
+
agent: target,
|
|
71
|
+
id: "delegate_target",
|
|
72
|
+
inputSchema: zod_1.z.object({ question: zod_1.z.string() }),
|
|
73
|
+
});
|
|
74
|
+
await expect(tool.execute({}, 123)).rejects.toThrow(/Invalid input/);
|
|
75
|
+
});
|
|
76
|
+
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { AgentResult } from "../../agent/Agent.js";
|
|
2
|
+
import { BaseAgent } from "../../agent/BaseAgent.js";
|
|
3
|
+
import type { AgentExecuteOptions } from "../../agent/Agent.js";
|
|
4
|
+
import type { CommitteeTeamConfig } from "../types/index.js";
|
|
5
|
+
export declare class CommitteeTeam extends BaseAgent {
|
|
6
|
+
private readonly config;
|
|
7
|
+
private readonly coordinator;
|
|
8
|
+
private readonly members;
|
|
9
|
+
private readonly strategy;
|
|
10
|
+
private readonly teamMemory?;
|
|
11
|
+
constructor(config: CommitteeTeamConfig);
|
|
12
|
+
execute(input?: string, options?: AgentExecuteOptions): Promise<AgentResult>;
|
|
13
|
+
private collectOpinions;
|
|
14
|
+
private aggregate;
|
|
15
|
+
private buildResult;
|
|
16
|
+
}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CommitteeTeam = void 0;
|
|
4
|
+
const BaseAgent_js_1 = require("../../agent/BaseAgent.js");
|
|
5
|
+
const memory_js_1 = require("../utils/memory.js");
|
|
6
|
+
const index_js_1 = require("../../tracing/index.js");
|
|
7
|
+
class CommitteeTeam extends BaseAgent_js_1.BaseAgent {
|
|
8
|
+
config;
|
|
9
|
+
coordinator;
|
|
10
|
+
members;
|
|
11
|
+
strategy;
|
|
12
|
+
teamMemory;
|
|
13
|
+
constructor(config) {
|
|
14
|
+
super(config.id, config.name, undefined, config.memory ?? config.coordinator.config.memory);
|
|
15
|
+
this.config = config;
|
|
16
|
+
this.coordinator = config.coordinator;
|
|
17
|
+
this.members = config.members;
|
|
18
|
+
this.strategy = config.strategy;
|
|
19
|
+
this.teamMemory = config.memory;
|
|
20
|
+
}
|
|
21
|
+
async execute(input, options) {
|
|
22
|
+
const correlationId = `committee_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
|
|
23
|
+
const startTime = Date.now();
|
|
24
|
+
return index_js_1.Tracing.withSpan({
|
|
25
|
+
kind: "agent",
|
|
26
|
+
name: `team:committee:${this.name}`,
|
|
27
|
+
correlationId,
|
|
28
|
+
executionId: options?.executionId,
|
|
29
|
+
data: { agentName: this.name, input },
|
|
30
|
+
}, async (teamSpan) => {
|
|
31
|
+
this.emit("agent:execution:start", {
|
|
32
|
+
correlationId,
|
|
33
|
+
executionId: options?.executionId,
|
|
34
|
+
data: {
|
|
35
|
+
agentId: this.id,
|
|
36
|
+
agentName: this.name,
|
|
37
|
+
input: input,
|
|
38
|
+
},
|
|
39
|
+
});
|
|
40
|
+
try {
|
|
41
|
+
const teamMemory = this.teamMemory ?? this.coordinator.config.memory;
|
|
42
|
+
const opinions = await this.collectOpinions(teamMemory, input, options);
|
|
43
|
+
const aggregated = this.aggregate(opinions);
|
|
44
|
+
const restoreMemory = (0, memory_js_1.overrideAgentMemory)(this.coordinator, teamMemory);
|
|
45
|
+
let result;
|
|
46
|
+
try {
|
|
47
|
+
await this.addMessage({
|
|
48
|
+
role: "user",
|
|
49
|
+
content: `Members opinions:\n${opinions
|
|
50
|
+
.map((o) => `- ${o.agent}: ${o.content}`)
|
|
51
|
+
.join("\n")}\nStrategy: ${this.strategy}`,
|
|
52
|
+
});
|
|
53
|
+
if (input) {
|
|
54
|
+
await this.addMessage({ role: "user", content: input });
|
|
55
|
+
}
|
|
56
|
+
const coordinatorResult = await this.coordinator.execute(undefined, {
|
|
57
|
+
...(options ?? {}),
|
|
58
|
+
executionId: options?.executionId,
|
|
59
|
+
});
|
|
60
|
+
result = coordinatorResult ?? this.buildResult(aggregated);
|
|
61
|
+
}
|
|
62
|
+
finally {
|
|
63
|
+
restoreMemory();
|
|
64
|
+
}
|
|
65
|
+
this.emit("agent:execution:end", {
|
|
66
|
+
correlationId,
|
|
67
|
+
executionId: options?.executionId,
|
|
68
|
+
data: {
|
|
69
|
+
agentId: this.id,
|
|
70
|
+
agentName: this.name,
|
|
71
|
+
status: result.truncated ? "truncated" : "success",
|
|
72
|
+
output: result.content,
|
|
73
|
+
durationMs: Date.now() - startTime,
|
|
74
|
+
},
|
|
75
|
+
});
|
|
76
|
+
await teamSpan.end({
|
|
77
|
+
status: result.truncated ? "cancelled" : "success",
|
|
78
|
+
data: { output: result.content },
|
|
79
|
+
});
|
|
80
|
+
return result;
|
|
81
|
+
}
|
|
82
|
+
catch (error) {
|
|
83
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
84
|
+
this.emit("agent:execution:error", {
|
|
85
|
+
correlationId,
|
|
86
|
+
executionId: options?.executionId,
|
|
87
|
+
data: {
|
|
88
|
+
agentId: this.id,
|
|
89
|
+
agentName: this.name,
|
|
90
|
+
error: {
|
|
91
|
+
message: err.message,
|
|
92
|
+
stack: err.stack,
|
|
93
|
+
name: err.name,
|
|
94
|
+
},
|
|
95
|
+
durationMs: Date.now() - startTime,
|
|
96
|
+
},
|
|
97
|
+
});
|
|
98
|
+
await teamSpan.end({ status: "error" });
|
|
99
|
+
throw error;
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
async collectOpinions(memory, input, options) {
|
|
104
|
+
const opinions = [];
|
|
105
|
+
for (const member of this.members) {
|
|
106
|
+
const restoreMemory = (0, memory_js_1.overrideAgentMemory)(member, memory);
|
|
107
|
+
try {
|
|
108
|
+
if (input) {
|
|
109
|
+
await memory.addMessage({ role: "user", content: input });
|
|
110
|
+
}
|
|
111
|
+
const result = await member.execute(undefined, {
|
|
112
|
+
...(options ?? {}),
|
|
113
|
+
executionId: options?.executionId,
|
|
114
|
+
});
|
|
115
|
+
opinions.push({ agent: member.name, content: result.content });
|
|
116
|
+
}
|
|
117
|
+
finally {
|
|
118
|
+
restoreMemory();
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
return opinions;
|
|
122
|
+
}
|
|
123
|
+
aggregate(opinions) {
|
|
124
|
+
if (this.strategy === "first") {
|
|
125
|
+
return opinions[0]?.content;
|
|
126
|
+
}
|
|
127
|
+
if (this.strategy === "vote") {
|
|
128
|
+
const counts = opinions.reduce((acc, o) => {
|
|
129
|
+
const key = String(o.content);
|
|
130
|
+
acc[key] = (acc[key] || 0) + 1;
|
|
131
|
+
return acc;
|
|
132
|
+
}, {});
|
|
133
|
+
return Object.entries(counts).sort((a, b) => b[1] - a[1])[0]?.[0];
|
|
134
|
+
}
|
|
135
|
+
if (this.strategy === "confidence") {
|
|
136
|
+
return opinions[0]?.content;
|
|
137
|
+
}
|
|
138
|
+
return opinions.map((o) => `${o.agent}: ${o.content}`).join(" | ");
|
|
139
|
+
}
|
|
140
|
+
buildResult(content) {
|
|
141
|
+
return {
|
|
142
|
+
content,
|
|
143
|
+
toolExecutions: [],
|
|
144
|
+
llmCalls: 0,
|
|
145
|
+
totalDurationMs: 0,
|
|
146
|
+
truncated: false,
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
exports.CommitteeTeam = CommitteeTeam;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const Agent_js_1 = require("../../agent/Agent.js");
|
|
4
|
+
const in_memory_js_1 = require("../../agent/memory/in-memory.js");
|
|
5
|
+
const mock_provider_js_1 = require("../../__tests__/mocks/mock-provider.js");
|
|
6
|
+
const CommitteeTeam_js_1 = require("./CommitteeTeam.js");
|
|
7
|
+
function agentWithResponse(name, content) {
|
|
8
|
+
const provider = new mock_provider_js_1.MockProvider();
|
|
9
|
+
provider.enqueueResponse({ content });
|
|
10
|
+
return new Agent_js_1.Agent({
|
|
11
|
+
id: name,
|
|
12
|
+
name,
|
|
13
|
+
model: provider,
|
|
14
|
+
memory: new in_memory_js_1.InMemory(),
|
|
15
|
+
instructions: `${name} instructions`,
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
describe("CommitteeTeam", () => {
|
|
19
|
+
it("aggregates member opinions and returns coordinator response", async () => {
|
|
20
|
+
const coordinatorProvider = new mock_provider_js_1.MockProvider();
|
|
21
|
+
coordinatorProvider.enqueueResponse({ content: "coordinator-final" });
|
|
22
|
+
const coordinator = new Agent_js_1.Agent({
|
|
23
|
+
id: "Coordinator",
|
|
24
|
+
name: "Coordinator",
|
|
25
|
+
model: coordinatorProvider,
|
|
26
|
+
memory: new in_memory_js_1.InMemory(),
|
|
27
|
+
instructions: "Aggregate",
|
|
28
|
+
});
|
|
29
|
+
const legal = agentWithResponse("Legal", "approve");
|
|
30
|
+
const tech = agentWithResponse("Tech", "approve");
|
|
31
|
+
const risk = agentWithResponse("Risk", "deny");
|
|
32
|
+
const team = new CommitteeTeam_js_1.CommitteeTeam({
|
|
33
|
+
id: "test-committee",
|
|
34
|
+
name: "test-committee",
|
|
35
|
+
coordinator,
|
|
36
|
+
members: [legal, tech, risk],
|
|
37
|
+
strategy: "summarize",
|
|
38
|
+
memory: new in_memory_js_1.InMemory(),
|
|
39
|
+
});
|
|
40
|
+
const result = await team.execute("Approve contract?");
|
|
41
|
+
expect(result.content).toBe("coordinator-final");
|
|
42
|
+
});
|
|
43
|
+
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { AgentResult } from "../../agent/Agent.js";
|
|
2
|
+
import { BaseAgent } from "../../agent/BaseAgent.js";
|
|
3
|
+
import type { AgentExecuteOptions } from "../../agent/Agent.js";
|
|
4
|
+
import type { HandoffTeamConfig } from "../types/index.js";
|
|
5
|
+
export declare class HandoffTeam extends BaseAgent {
|
|
6
|
+
private readonly config;
|
|
7
|
+
readonly kind: "team:handoff";
|
|
8
|
+
private readonly edgeMap;
|
|
9
|
+
private readonly agentByName;
|
|
10
|
+
private readonly stickyStateKey;
|
|
11
|
+
constructor(config: HandoffTeamConfig);
|
|
12
|
+
execute(input?: string, options?: AgentExecuteOptions): Promise<AgentResult>;
|
|
13
|
+
private buildEdgeMap;
|
|
14
|
+
private attachHandoffTools;
|
|
15
|
+
private findHandoffSignal;
|
|
16
|
+
}
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.HandoffTeam = void 0;
|
|
4
|
+
const BaseAgent_js_1 = require("../../agent/BaseAgent.js");
|
|
5
|
+
const index_js_1 = require("../../agent/tools/index.js");
|
|
6
|
+
const zod_1 = require("zod");
|
|
7
|
+
const guardrails_js_1 = require("../utils/guardrails.js");
|
|
8
|
+
const memory_js_1 = require("../utils/memory.js");
|
|
9
|
+
const index_js_2 = require("../../tracing/index.js");
|
|
10
|
+
class HandoffTeam extends BaseAgent_js_1.BaseAgent {
|
|
11
|
+
config;
|
|
12
|
+
kind = 'team:handoff';
|
|
13
|
+
edgeMap;
|
|
14
|
+
agentByName;
|
|
15
|
+
stickyStateKey;
|
|
16
|
+
constructor(config) {
|
|
17
|
+
super(config.id, config.name, undefined, config.memory ?? config.initialAgent.config.memory);
|
|
18
|
+
this.config = config;
|
|
19
|
+
this.edgeMap = this.buildEdgeMap(config.edges);
|
|
20
|
+
this.agentByName = new Map(config.agents.map((a) => [a.name.toLowerCase(), a]));
|
|
21
|
+
this.stickyStateKey = `nebulaos:handoff:${this.name}:activeAgent`;
|
|
22
|
+
}
|
|
23
|
+
async execute(input, options) {
|
|
24
|
+
const correlationId = `handoff_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
|
|
25
|
+
const startTime = Date.now();
|
|
26
|
+
return index_js_2.Tracing.withSpan({
|
|
27
|
+
kind: "agent",
|
|
28
|
+
name: `team:handoff:${this.name}`,
|
|
29
|
+
correlationId,
|
|
30
|
+
executionId: options?.executionId,
|
|
31
|
+
data: { agentName: this.name, input },
|
|
32
|
+
}, async (teamSpan) => {
|
|
33
|
+
this.emit("agent:execution:start", {
|
|
34
|
+
correlationId,
|
|
35
|
+
executionId: options?.executionId,
|
|
36
|
+
data: {
|
|
37
|
+
agentId: this.id,
|
|
38
|
+
agentName: this.name,
|
|
39
|
+
input: input,
|
|
40
|
+
},
|
|
41
|
+
});
|
|
42
|
+
try {
|
|
43
|
+
const teamMemory = this.config.memory ?? this.config.initialAgent.config.memory;
|
|
44
|
+
let current = this.config.initialAgent;
|
|
45
|
+
let transfers = 0;
|
|
46
|
+
// Resume from last active agent if the configured memory supports state storage.
|
|
47
|
+
// This makes handoff "stick" across subsequent execute() calls (process-local memory).
|
|
48
|
+
if (typeof teamMemory.getState === "function") {
|
|
49
|
+
const lastAgentName = await teamMemory.getState(this.stickyStateKey);
|
|
50
|
+
if (lastAgentName) {
|
|
51
|
+
const last = this.agentByName.get(lastAgentName.toLowerCase());
|
|
52
|
+
if (last)
|
|
53
|
+
current = last;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
if (input) {
|
|
57
|
+
await this.addMessage({ role: "user", content: input });
|
|
58
|
+
}
|
|
59
|
+
let lastResult;
|
|
60
|
+
while (true) {
|
|
61
|
+
(0, guardrails_js_1.enforceMaxTransfers)(transfers, this.config.maxTransfers);
|
|
62
|
+
const { tools, restoreTools } = this.attachHandoffTools(current);
|
|
63
|
+
const restoreMemory = (0, memory_js_1.overrideAgentMemory)(current, teamMemory);
|
|
64
|
+
try {
|
|
65
|
+
const result = await (0, guardrails_js_1.enforceTimeout)(current.execute(undefined, {
|
|
66
|
+
...(options ?? {}),
|
|
67
|
+
executionId: options?.executionId,
|
|
68
|
+
}), this.config.timeoutMs, "handoff-execution");
|
|
69
|
+
lastResult = result;
|
|
70
|
+
const handoff = this.findHandoffSignal(result, tools);
|
|
71
|
+
if (!handoff) {
|
|
72
|
+
// Persist the last active agent for resume (if supported by memory).
|
|
73
|
+
if (typeof teamMemory.setState === "function") {
|
|
74
|
+
await teamMemory.setState(this.stickyStateKey, current.name);
|
|
75
|
+
}
|
|
76
|
+
this.emit("agent:execution:end", {
|
|
77
|
+
correlationId,
|
|
78
|
+
executionId: options?.executionId,
|
|
79
|
+
data: {
|
|
80
|
+
agentId: this.id,
|
|
81
|
+
agentName: this.name,
|
|
82
|
+
status: result.truncated ? "truncated" : "success",
|
|
83
|
+
output: result.content,
|
|
84
|
+
durationMs: Date.now() - startTime,
|
|
85
|
+
},
|
|
86
|
+
});
|
|
87
|
+
await teamSpan.end({
|
|
88
|
+
status: result.truncated ? "cancelled" : "success",
|
|
89
|
+
data: { output: result.content },
|
|
90
|
+
});
|
|
91
|
+
return result;
|
|
92
|
+
}
|
|
93
|
+
transfers++;
|
|
94
|
+
(0, guardrails_js_1.enforceAllowedTransition)(current.name, handoff.to, this.config.allowedTransitions);
|
|
95
|
+
if (this.config.onTransfer) {
|
|
96
|
+
const target = this.agentByName.get(handoff.to.toLowerCase());
|
|
97
|
+
if (target) {
|
|
98
|
+
await this.config.onTransfer(current, target);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
const next = this.agentByName.get(handoff.to.toLowerCase());
|
|
102
|
+
if (!next) {
|
|
103
|
+
throw new Error(`Handoff target not found: ${handoff.to}`);
|
|
104
|
+
}
|
|
105
|
+
current = next;
|
|
106
|
+
// Persist current agent immediately after handoff so the next external execute()
|
|
107
|
+
// call resumes from the correct agent.
|
|
108
|
+
if (typeof teamMemory.setState === "function") {
|
|
109
|
+
await teamMemory.setState(this.stickyStateKey, current.name);
|
|
110
|
+
}
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
finally {
|
|
114
|
+
restoreTools();
|
|
115
|
+
restoreMemory();
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
catch (error) {
|
|
120
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
121
|
+
this.emit("agent:execution:error", {
|
|
122
|
+
correlationId,
|
|
123
|
+
executionId: options?.executionId,
|
|
124
|
+
data: {
|
|
125
|
+
agentId: this.id,
|
|
126
|
+
agentName: this.name,
|
|
127
|
+
error: {
|
|
128
|
+
message: err.message,
|
|
129
|
+
stack: err.stack,
|
|
130
|
+
name: err.name,
|
|
131
|
+
},
|
|
132
|
+
durationMs: Date.now() - startTime,
|
|
133
|
+
},
|
|
134
|
+
});
|
|
135
|
+
await teamSpan.end({ status: "error" });
|
|
136
|
+
throw error;
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
buildEdgeMap(edges) {
|
|
141
|
+
const map = new Map();
|
|
142
|
+
edges.forEach((edge) => {
|
|
143
|
+
const list = map.get(edge.from) ?? [];
|
|
144
|
+
list.push(edge);
|
|
145
|
+
map.set(edge.from, list);
|
|
146
|
+
});
|
|
147
|
+
return map;
|
|
148
|
+
}
|
|
149
|
+
attachHandoffTools(agent) {
|
|
150
|
+
const edges = this.edgeMap.get(agent) ?? [];
|
|
151
|
+
const newTools = edges.map((edge) => new index_js_1.Tool({
|
|
152
|
+
id: edge.toolId,
|
|
153
|
+
description: edge.description ??
|
|
154
|
+
`Transfer control to ${edge.to.name} (${edge.to.config.instructions})`,
|
|
155
|
+
inputSchema: zod_1.z.object({
|
|
156
|
+
context: zod_1.z.string().optional().describe("Additional context to pass to the receiving agent"),
|
|
157
|
+
}),
|
|
158
|
+
handler: async (_ctx, input) => ({
|
|
159
|
+
handoff: true,
|
|
160
|
+
to: edge.to.name,
|
|
161
|
+
context: input?.context,
|
|
162
|
+
}),
|
|
163
|
+
}));
|
|
164
|
+
const originalTools = agent.config.tools ?? [];
|
|
165
|
+
agent.config.tools = [...originalTools, ...newTools];
|
|
166
|
+
return {
|
|
167
|
+
tools: newTools,
|
|
168
|
+
restoreTools: () => {
|
|
169
|
+
agent.config.tools = originalTools;
|
|
170
|
+
},
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
findHandoffSignal(result, tools) {
|
|
174
|
+
if (!result.toolExecutions?.length)
|
|
175
|
+
return undefined;
|
|
176
|
+
const toolIds = new Set(tools.map((t) => t.id));
|
|
177
|
+
for (const exec of result.toolExecutions) {
|
|
178
|
+
if (toolIds.has(exec.name) && exec.output && exec.output.handoff) {
|
|
179
|
+
return exec.output;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
return undefined;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
exports.HandoffTeam = HandoffTeam;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const Agent_js_1 = require("../../agent/Agent.js");
|
|
4
|
+
const in_memory_js_1 = require("../../agent/memory/in-memory.js");
|
|
5
|
+
const HandoffTeam_js_1 = require("./HandoffTeam.js");
|
|
6
|
+
const mock_provider_js_1 = require("../../__tests__/mocks/mock-provider.js");
|
|
7
|
+
function agentWithToolCall(name, toolName, toSelf = false) {
|
|
8
|
+
const provider = new mock_provider_js_1.MockProvider();
|
|
9
|
+
provider.enqueueResponse({
|
|
10
|
+
content: "",
|
|
11
|
+
toolCalls: [
|
|
12
|
+
{
|
|
13
|
+
id: "call-1",
|
|
14
|
+
type: "function",
|
|
15
|
+
function: { name: toolName, arguments: "{}" },
|
|
16
|
+
},
|
|
17
|
+
],
|
|
18
|
+
});
|
|
19
|
+
return new Agent_js_1.Agent({
|
|
20
|
+
id: name,
|
|
21
|
+
name,
|
|
22
|
+
model: provider,
|
|
23
|
+
memory: new in_memory_js_1.InMemory(),
|
|
24
|
+
instructions: `${name} instructions`,
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
function agentWithFinal(name, content) {
|
|
28
|
+
const provider = new mock_provider_js_1.MockProvider();
|
|
29
|
+
provider.enqueueResponse({ content });
|
|
30
|
+
return new Agent_js_1.Agent({
|
|
31
|
+
id: name,
|
|
32
|
+
name,
|
|
33
|
+
model: provider,
|
|
34
|
+
memory: new in_memory_js_1.InMemory(),
|
|
35
|
+
instructions: `${name} instructions`,
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
describe("HandoffTeam", () => {
|
|
39
|
+
it("transfers control via handoff tool to target agent and returns final output", async () => {
|
|
40
|
+
const a = agentWithToolCall("A", "to_b");
|
|
41
|
+
const b = agentWithFinal("B", "done-b");
|
|
42
|
+
const team = new HandoffTeam_js_1.HandoffTeam({
|
|
43
|
+
id: "test-handoff",
|
|
44
|
+
name: "test-handoff",
|
|
45
|
+
agents: [a, b],
|
|
46
|
+
edges: [{ from: a, to: b, toolId: "to_b" }],
|
|
47
|
+
initialAgent: a,
|
|
48
|
+
maxTransfers: 3,
|
|
49
|
+
});
|
|
50
|
+
const result = await team.execute("hi");
|
|
51
|
+
expect(result.content).toBe("done-b");
|
|
52
|
+
});
|
|
53
|
+
it("enforces allowedTransitions and throws on disallowed handoff", async () => {
|
|
54
|
+
const a = agentWithToolCall("A", "to_c");
|
|
55
|
+
const b = agentWithFinal("B", "ok-b");
|
|
56
|
+
const c = agentWithFinal("C", "ok-c");
|
|
57
|
+
const team = new HandoffTeam_js_1.HandoffTeam({
|
|
58
|
+
id: "test-transitions",
|
|
59
|
+
name: "test-transitions",
|
|
60
|
+
agents: [a, b, c],
|
|
61
|
+
edges: [
|
|
62
|
+
{ from: a, to: b, toolId: "to_b" },
|
|
63
|
+
{ from: a, to: c, toolId: "to_c" },
|
|
64
|
+
],
|
|
65
|
+
allowedTransitions: { A: ["B"] }, // disallow A -> C
|
|
66
|
+
initialAgent: a,
|
|
67
|
+
maxTransfers: 2,
|
|
68
|
+
});
|
|
69
|
+
await expect(team.execute("x")).rejects.toThrow(/not allowed/);
|
|
70
|
+
});
|
|
71
|
+
it("calls onTransfer when handoff occurs", async () => {
|
|
72
|
+
const a = agentWithToolCall("A", "to_b");
|
|
73
|
+
const b = agentWithFinal("B", "done-b");
|
|
74
|
+
const calls = [];
|
|
75
|
+
const team = new HandoffTeam_js_1.HandoffTeam({
|
|
76
|
+
id: "test-callback",
|
|
77
|
+
name: "test-callback",
|
|
78
|
+
agents: [a, b],
|
|
79
|
+
edges: [{ from: a, to: b, toolId: "to_b" }],
|
|
80
|
+
initialAgent: a,
|
|
81
|
+
onTransfer: (from, to) => {
|
|
82
|
+
calls.push(`${from.name}->${to.name}`);
|
|
83
|
+
},
|
|
84
|
+
});
|
|
85
|
+
const result = await team.execute("y");
|
|
86
|
+
expect(result.content).toBe("done-b");
|
|
87
|
+
expect(calls).toEqual(["A->B"]);
|
|
88
|
+
});
|
|
89
|
+
it("respects maxTransfers and throws when exceeded", async () => {
|
|
90
|
+
const a = agentWithToolCall("A", "to_b");
|
|
91
|
+
const b = agentWithToolCall("B", "to_a");
|
|
92
|
+
const team = new HandoffTeam_js_1.HandoffTeam({
|
|
93
|
+
id: "test-memory",
|
|
94
|
+
name: "test-memory",
|
|
95
|
+
agents: [a, b],
|
|
96
|
+
edges: [
|
|
97
|
+
{ from: a, to: b, toolId: "to_b" },
|
|
98
|
+
{ from: b, to: a, toolId: "to_a" },
|
|
99
|
+
],
|
|
100
|
+
initialAgent: a,
|
|
101
|
+
maxTransfers: 1,
|
|
102
|
+
});
|
|
103
|
+
await expect(team.execute("loop")).rejects.toThrow(/Max transfers/);
|
|
104
|
+
});
|
|
105
|
+
});
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { AgentResult } from "../../agent/Agent.js";
|
|
2
|
+
import { BaseAgent } from "../../agent/BaseAgent.js";
|
|
3
|
+
import type { AgentExecuteOptions } from "../../agent/Agent.js";
|
|
4
|
+
import type { HierarchicalTeamConfig } from "../types/index.js";
|
|
5
|
+
export declare class HierarchicalTeam extends BaseAgent {
|
|
6
|
+
private readonly config;
|
|
7
|
+
readonly kind: "team:hierarchical";
|
|
8
|
+
private readonly manager;
|
|
9
|
+
private readonly workers;
|
|
10
|
+
private readonly teamMemory?;
|
|
11
|
+
private readonly allowHandoff;
|
|
12
|
+
private readonly workerCollaboration;
|
|
13
|
+
constructor(config: HierarchicalTeamConfig);
|
|
14
|
+
execute(input?: string, options?: AgentExecuteOptions): Promise<AgentResult>;
|
|
15
|
+
private executeDelegationOnly;
|
|
16
|
+
private executeWithHandoff;
|
|
17
|
+
private buildWorkerTools;
|
|
18
|
+
}
|