diff --git a/typescript/src/core/InProcessRuntime.ts b/typescript/src/core/InProcessRuntime.ts index ee278d8577a..8d348746b3b 100644 --- a/typescript/src/core/InProcessRuntime.ts +++ b/typescript/src/core/InProcessRuntime.ts @@ -31,13 +31,24 @@ export class InProcessRuntime implements IAgentRuntime { async stop(): Promise { if (!this.isRunning) { - throw new Error("Runtime not running"); + return; // Change from throwing to just returning } + if (this.messageProcessor) { clearInterval(this.messageProcessor); this.messageProcessor = undefined; } + this.isRunning = false; + + // Process any remaining messages + while (this.messageDeliveryQueue.length > 0) { + await this.processNextMessage(); + } + + // Clear queue and subscriptions + this.messageDeliveryQueue = []; + this.subscriptions.clear(); } private async publishMessageServicer(envelope: MessageEnvelope, deliveryToken?: AbortSignal): Promise { diff --git a/typescript/test/core/Agent.test.ts b/typescript/test/core/Agent.test.ts index 7cf0b4ee12d..0e76bf9e335 100644 --- a/typescript/test/core/Agent.test.ts +++ b/typescript/test/core/Agent.test.ts @@ -31,6 +31,8 @@ describe('Agent', () => { // Wait for message processing await new Promise(resolve => setTimeout(resolve, 100)); expect(Object.keys(agent!.ReceivedMessages).length).toBe(0); + + await runtime.stop(); // Add cleanup }); it('should receive messages when subscribed', async () => { diff --git a/typescript/test/core/InProcessRuntime.test.ts b/typescript/test/core/InProcessRuntime.test.ts index 39f8b2c1b23..d0e3a346fc9 100644 --- a/typescript/test/core/InProcessRuntime.test.ts +++ b/typescript/test/core/InProcessRuntime.test.ts @@ -1,4 +1,4 @@ -import { describe, it, expect } from '@jest/globals'; +import { describe, it, expect, afterEach, jest } from '@jest/globals'; import { InProcessRuntime } from '../../src/core/InProcessRuntime'; import { TopicId, AgentId, IAgentRuntime } from '../../src/contracts/IAgentRuntime'; import { SubscribedSaveLoadAgent, SubscribedSelfPublishAgent } from './TestAgent'; @@ -33,6 +33,12 @@ class ThirdSubscribedAgent extends BaseAgent { } describe('InProcessRuntime', () => { + // Add afterEach cleanup for all tests + afterEach(async () => { + // Force cleanup any hanging runtimes + jest.clearAllTimers(); + }); + it('should not deliver to self by default', async () => { const runtime = new InProcessRuntime(); console.log('Starting runtime...'); @@ -79,36 +85,52 @@ describe('InProcessRuntime', () => { // Verify the text remains default (self-message wasn't delivered) expect(agent.Text.source).toBe("DefaultTopic"); expect(agent.Text.content).toBe("DefaultContent"); + + await runtime.stop(); // Add cleanup }); it('should deliver to self when deliverToSelf is true', async () => { const runtime = new InProcessRuntime(); runtime.deliverToSelf = true; + await runtime.start(); // Add runtime start let agent: SubscribedSelfPublishAgent; // Create and register agent const agentId = { type: "MyAgent", key: "test" }; await runtime.registerAgentFactoryAsync("MyAgent", async (id, runtime) => { agent = new SubscribedSelfPublishAgent(id, runtime); + console.log('Created agent:', { id, agent: agent.constructor.name }); return agent; }); + await runtime.getAgentMetadataAsync(agentId); // Ensure agent is created + console.log('Initial agent state:', { Text: agent!.Text }); + // Add subscription await runtime.addSubscriptionAsync(new TypeSubscriptionAttribute("TestTopic").bind("MyAgent")); + console.log('Added subscription for TestTopic'); // Send message that will trigger self-publish + console.log('Publishing message...'); await runtime.publishMessageAsync("SelfMessage", { type: "TestTopic", source: "test" }); - await new Promise(resolve => setTimeout(resolve, 500)); - + + // Wait for message processing to complete - increase timeout since we have cascading messages + await new Promise(resolve => setTimeout(resolve, 1000)); + + console.log('Final agent state:', { Text: agent!.Text }); + // Verify the text was updated (self-message was delivered) expect(agent!.Text.source).toBe("TestTopic"); expect(agent!.Text.content).toBe("SelfMessage"); - }); + + await runtime.stop(); // Add cleanup + }, 15000); // Test for save/load state functionality it('should save and load state correctly', async () => { // Create first runtime and set up agent const runtime = new InProcessRuntime(); + await runtime.start(); let agent: SubscribedSaveLoadAgent; await runtime.registerAgentFactoryAsync("MyAgent", async (id, runtime) => { @@ -141,5 +163,9 @@ describe('InProcessRuntime', () => { // Verify state was restored expect(newAgent!.ReceivedMessages).toEqual(agent!.ReceivedMessages); + + await runtime.stop(); + // Also stop the new runtime + await newRuntime.stop(); }); });