chore(deps): upgrade claude-code-acp to v0.8.5 and improve test lifecycle management

This commit is contained in:
Mohamed Boudra
2025-10-26 09:51:18 +01:00
parent 7d1ef63c8f
commit fdaf403f13
4 changed files with 112 additions and 61 deletions
+12
View File
@@ -214,5 +214,17 @@
"createdAt": "2025-10-26T07:59:07.111Z",
"lastActivityAt": "2025-10-26T07:59:17.913Z",
"cwd": "/Users/moboudra/dev/voice-dev"
},
{
"id": "69af3b81-92d0-48a6-abd6-e357a4cf67b5",
"title": "Manage Test Agent Lifecycle",
"sessionId": "9f844826-2c8e-414f-a236-a9279233b053",
"options": {
"type": "claude",
"sessionId": "9f844826-2c8e-414f-a236-a9279233b053"
},
"createdAt": "2025-10-26T08:32:55.726Z",
"lastActivityAt": "2025-10-26T08:37:47.226Z",
"cwd": "/Users/moboudra/dev/voice-dev"
}
]
+1 -1
View File
@@ -19,7 +19,7 @@
"dependencies": {
"@agentclientprotocol/sdk": "^0.4.9",
"@ai-sdk/openai": "^2.0.52",
"@boudra/claude-code-acp": "^0.8.4",
"@boudra/claude-code-acp": "^0.8.5",
"@deepgram/sdk": "^3.4.0",
"@modelcontextprotocol/sdk": "^1.20.1",
"@openrouter/ai-sdk-provider": "^1.2.0",
@@ -1,4 +1,4 @@
import { describe, it, expect, beforeAll, afterAll } from "vitest";
import { describe, it, expect, beforeAll, afterEach, afterAll } from "vitest";
import { AgentManager } from "./agent-manager.js";
import type { AgentUpdate, AgentNotification } from "./types.js";
import type { RequestPermissionRequest } from "@agentclientprotocol/sdk";
@@ -7,19 +7,31 @@ import { tmpdir } from "os";
import { join } from "path";
describe("AgentManager", () => {
let manager: AgentManager;
let tmpDir: string;
const createdAgents: Array<{ manager: AgentManager; agentId: string }> = [];
beforeAll(async () => {
manager = new AgentManager();
tmpDir = await mkdtemp(join(tmpdir(), "acp-test-"));
});
afterEach(async () => {
// Clean up all agents created during tests
for (const { manager, agentId } of createdAgents) {
try {
await manager.deleteAgent(agentId);
} catch (error) {
console.error(`Failed to delete agent ${agentId}:`, error);
}
}
createdAgents.length = 0;
});
afterAll(async () => {
await rm(tmpDir, { recursive: true, force: true });
});
it("should create file after accepting plan", async () => {
const manager = new AgentManager();
let permissionRequest: RequestPermissionRequest | null = null;
let requestId: string | null = null;
const testFile = join(tmpDir, "test.txt");
@@ -28,6 +40,7 @@ describe("AgentManager", () => {
cwd: tmpDir,
initialMode: "plan",
});
createdAgents.push({ manager, agentId });
const unsubscribe = manager.subscribeToUpdates(
agentId,
@@ -79,75 +92,82 @@ describe("AgentManager", () => {
expect(agentInfo?.currentModeId).toBe("acceptEdits");
} finally {
unsubscribe();
}
}, 120000);
describe("persistence", () => {
it.only("should load persisted agent and send new prompt", async () => {
const manager = new AgentManager();
const agentId = await manager.createAgent({
cwd: tmpDir,
});
createdAgents.push({ manager, agentId });
await manager.sendPrompt(agentId, "echo 'first message'");
let status = manager.getAgentStatus(agentId);
let attempts = 0;
while (status !== "completed" && status !== "failed" && attempts < 60) {
await new Promise((resolve) => setTimeout(resolve, 1000));
status = manager.getAgentStatus(agentId);
attempts++;
}
expect(status).toBe("completed");
const agentBeforeKill = manager.listAgents().find((a) => a.id === agentId);
const claudeSessionId = manager.getClaudeSessionId(agentId);
console.log("Before kill - ACP Session ID:", agentBeforeKill?.sessionId);
console.log("Before kill - Claude Session ID:", claudeSessionId);
console.log("Working directory:", tmpDir);
await manager.killAgent(agentId);
}
}, 120000);
it.only("should load persisted agent and send new prompt", async () => {
const agentId = await manager.createAgent({
cwd: tmpDir,
});
const newManager = new AgentManager();
await newManager.initialize();
await manager.sendPrompt(agentId, "echo 'first message'");
// Update the tracking to use the new manager instance
const trackingIndex = createdAgents.findIndex((a) => a.agentId === agentId);
if (trackingIndex >= 0) {
createdAgents[trackingIndex] = { manager: newManager, agentId };
}
let status = manager.getAgentStatus(agentId);
let attempts = 0;
while (status !== "completed" && status !== "failed" && attempts < 60) {
await new Promise((resolve) => setTimeout(resolve, 1000));
status = manager.getAgentStatus(agentId);
attempts++;
}
const agents = newManager.listAgents();
const loadedAgent = agents.find((a) => a.id === agentId);
const loadedClaudeSessionId = newManager.getClaudeSessionId(agentId);
expect(status).toBe("completed");
console.log("After reload - ACP Session ID:", loadedAgent?.sessionId);
console.log("After reload - Claude Session ID:", loadedClaudeSessionId);
const agentBeforeKill = manager.listAgents().find((a) => a.id === agentId);
const claudeSessionId = manager.getClaudeSessionId(agentId);
console.log("Before kill - ACP Session ID:", agentBeforeKill?.sessionId);
console.log("Before kill - Claude Session ID:", claudeSessionId);
console.log("Working directory:", tmpDir);
expect(loadedAgent).toBeDefined();
expect(loadedAgent?.id).toBe(agentId);
expect(loadedAgent?.cwd).toBe(tmpDir);
await manager.killAgent(agentId);
await newManager.initializeAgentAndGetHistory(agentId);
await new Promise((resolve) => setTimeout(resolve, 2000));
const persistedUpdates = newManager.getAgentUpdates(agentId);
const newManager = new AgentManager();
await newManager.initialize();
console.log(
"Persisted updates:",
JSON.stringify(persistedUpdates, null, 2)
);
const agents = newManager.listAgents();
const loadedAgent = agents.find((a) => a.id === agentId);
const loadedClaudeSessionId = newManager.getClaudeSessionId(agentId);
await newManager.sendPrompt(agentId, "echo 'second message'");
console.log("After reload - ACP Session ID:", loadedAgent?.sessionId);
console.log("After reload - Claude Session ID:", loadedClaudeSessionId);
expect(loadedAgent).toBeDefined();
expect(loadedAgent?.id).toBe(agentId);
expect(loadedAgent?.cwd).toBe(tmpDir);
await newManager.initializeAgentAndGetHistory(agentId);
await new Promise((resolve) => setTimeout(resolve, 2000));
const persistedUpdates = newManager.getAgentUpdates(agentId);
console.log(
"Persisted updates:",
JSON.stringify(persistedUpdates, null, 2)
);
await newManager.sendPrompt(agentId, "echo 'second message'");
status = newManager.getAgentStatus(agentId);
attempts = 0;
while (status !== "completed" && status !== "failed" && attempts < 60) {
await new Promise((resolve) => setTimeout(resolve, 1000));
status = newManager.getAgentStatus(agentId);
attempts++;
}
attempts = 0;
while (status !== "completed" && status !== "failed" && attempts < 60) {
await new Promise((resolve) => setTimeout(resolve, 1000));
status = newManager.getAgentStatus(agentId);
attempts++;
}
expect(status).toBe("completed");
expect(status).toBe("completed");
const finalUpdates = newManager.getAgentUpdates(agentId);
expect(finalUpdates.length).toBeGreaterThan(0);
const finalUpdates = newManager.getAgentUpdates(agentId);
expect(finalUpdates.length).toBeGreaterThan(0);
console.log("Final updates:", finalUpdates);
await newManager.killAgent(agentId);
}, 120000);
console.log("Final updates:", finalUpdates);
}, 120000);
});
});
@@ -556,6 +556,25 @@ export class AgentManager {
}, 100);
}
/**
* Delete an agent completely
* Kills the process and removes it from persistence
*/
async deleteAgent(agentId: string): Promise<void> {
const agent = this.agents.get(agentId);
if (!agent) {
throw new Error(`Agent ${agentId} not found`);
}
// Kill the agent process
await this.killAgent(agentId);
// Remove from persistence
await this.persistence.remove(agentId);
console.log(`[Agent ${agentId}] Deleted from persistence`);
}
/**
* Get the status of an agent
*/