chore(deps): upgrade claude-code-acp to v0.8.5 and improve test lifecycle management
This commit is contained in:
@@ -214,5 +214,17 @@
|
|||||||
"createdAt": "2025-10-26T07:59:07.111Z",
|
"createdAt": "2025-10-26T07:59:07.111Z",
|
||||||
"lastActivityAt": "2025-10-26T07:59:17.913Z",
|
"lastActivityAt": "2025-10-26T07:59:17.913Z",
|
||||||
"cwd": "/Users/moboudra/dev/voice-dev"
|
"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"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -19,7 +19,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@agentclientprotocol/sdk": "^0.4.9",
|
"@agentclientprotocol/sdk": "^0.4.9",
|
||||||
"@ai-sdk/openai": "^2.0.52",
|
"@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",
|
"@deepgram/sdk": "^3.4.0",
|
||||||
"@modelcontextprotocol/sdk": "^1.20.1",
|
"@modelcontextprotocol/sdk": "^1.20.1",
|
||||||
"@openrouter/ai-sdk-provider": "^1.2.0",
|
"@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 { AgentManager } from "./agent-manager.js";
|
||||||
import type { AgentUpdate, AgentNotification } from "./types.js";
|
import type { AgentUpdate, AgentNotification } from "./types.js";
|
||||||
import type { RequestPermissionRequest } from "@agentclientprotocol/sdk";
|
import type { RequestPermissionRequest } from "@agentclientprotocol/sdk";
|
||||||
@@ -7,19 +7,31 @@ import { tmpdir } from "os";
|
|||||||
import { join } from "path";
|
import { join } from "path";
|
||||||
|
|
||||||
describe("AgentManager", () => {
|
describe("AgentManager", () => {
|
||||||
let manager: AgentManager;
|
|
||||||
let tmpDir: string;
|
let tmpDir: string;
|
||||||
|
const createdAgents: Array<{ manager: AgentManager; agentId: string }> = [];
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
manager = new AgentManager();
|
|
||||||
tmpDir = await mkdtemp(join(tmpdir(), "acp-test-"));
|
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 () => {
|
afterAll(async () => {
|
||||||
await rm(tmpDir, { recursive: true, force: true });
|
await rm(tmpDir, { recursive: true, force: true });
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should create file after accepting plan", async () => {
|
it("should create file after accepting plan", async () => {
|
||||||
|
const manager = new AgentManager();
|
||||||
let permissionRequest: RequestPermissionRequest | null = null;
|
let permissionRequest: RequestPermissionRequest | null = null;
|
||||||
let requestId: string | null = null;
|
let requestId: string | null = null;
|
||||||
const testFile = join(tmpDir, "test.txt");
|
const testFile = join(tmpDir, "test.txt");
|
||||||
@@ -28,6 +40,7 @@ describe("AgentManager", () => {
|
|||||||
cwd: tmpDir,
|
cwd: tmpDir,
|
||||||
initialMode: "plan",
|
initialMode: "plan",
|
||||||
});
|
});
|
||||||
|
createdAgents.push({ manager, agentId });
|
||||||
|
|
||||||
const unsubscribe = manager.subscribeToUpdates(
|
const unsubscribe = manager.subscribeToUpdates(
|
||||||
agentId,
|
agentId,
|
||||||
@@ -79,75 +92,82 @@ describe("AgentManager", () => {
|
|||||||
expect(agentInfo?.currentModeId).toBe("acceptEdits");
|
expect(agentInfo?.currentModeId).toBe("acceptEdits");
|
||||||
} finally {
|
} finally {
|
||||||
unsubscribe();
|
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);
|
await manager.killAgent(agentId);
|
||||||
}
|
|
||||||
}, 120000);
|
|
||||||
|
|
||||||
it.only("should load persisted agent and send new prompt", async () => {
|
const newManager = new AgentManager();
|
||||||
const agentId = await manager.createAgent({
|
await newManager.initialize();
|
||||||
cwd: tmpDir,
|
|
||||||
});
|
|
||||||
|
|
||||||
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);
|
const agents = newManager.listAgents();
|
||||||
let attempts = 0;
|
const loadedAgent = agents.find((a) => a.id === agentId);
|
||||||
while (status !== "completed" && status !== "failed" && attempts < 60) {
|
const loadedClaudeSessionId = newManager.getClaudeSessionId(agentId);
|
||||||
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
||||||
status = manager.getAgentStatus(agentId);
|
|
||||||
attempts++;
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
expect(loadedAgent).toBeDefined();
|
||||||
const claudeSessionId = manager.getClaudeSessionId(agentId);
|
expect(loadedAgent?.id).toBe(agentId);
|
||||||
console.log("Before kill - ACP Session ID:", agentBeforeKill?.sessionId);
|
expect(loadedAgent?.cwd).toBe(tmpDir);
|
||||||
console.log("Before kill - Claude Session ID:", claudeSessionId);
|
|
||||||
console.log("Working directory:", 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();
|
console.log(
|
||||||
await newManager.initialize();
|
"Persisted updates:",
|
||||||
|
JSON.stringify(persistedUpdates, null, 2)
|
||||||
|
);
|
||||||
|
|
||||||
const agents = newManager.listAgents();
|
await newManager.sendPrompt(agentId, "echo 'second message'");
|
||||||
const loadedAgent = agents.find((a) => a.id === agentId);
|
|
||||||
const loadedClaudeSessionId = newManager.getClaudeSessionId(agentId);
|
|
||||||
|
|
||||||
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);
|
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);
|
const finalUpdates = newManager.getAgentUpdates(agentId);
|
||||||
expect(finalUpdates.length).toBeGreaterThan(0);
|
expect(finalUpdates.length).toBeGreaterThan(0);
|
||||||
|
|
||||||
console.log("Final updates:", finalUpdates);
|
console.log("Final updates:", finalUpdates);
|
||||||
|
}, 120000);
|
||||||
await newManager.killAgent(agentId);
|
});
|
||||||
}, 120000);
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -556,6 +556,25 @@ export class AgentManager {
|
|||||||
}, 100);
|
}, 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
|
* Get the status of an agent
|
||||||
*/
|
*/
|
||||||
|
|||||||
Reference in New Issue
Block a user