diff --git a/server/src/__tests__/tool-access-service.test.ts b/server/src/__tests__/tool-access-service.test.ts index 0d6e1e4fe20..bac38f91298 100644 --- a/server/src/__tests__/tool-access-service.test.ts +++ b/server/src/__tests__/tool-access-service.test.ts @@ -7140,6 +7140,25 @@ describeEmbeddedPostgres("tool access service", () => { expect(audits).toHaveLength(2); expect(audits.every((audit) => audit.actorType === "user" && audit.actorId === member.userId)).toBe(true); }); + + it("removes orphaned agent installs during replacement", async () => { + const company = await createCompany(db); + const agent = await createAgent(db, company.id); + const { connection } = await createRemoteToolFixture(db, company.id); + const service = toolAccessService(db); + await service.putConnectionInstalls(connection.id, { + installs: [{ targetType: "agent", targetId: agent.id }], + }); + await db.delete(agents).where(eq(agents.id, agent.id)); + + const response = await request(createRouteApp(db)) + .put(`/api/tool-connections/${connection.id}/installs`) + .send({ installs: [] }); + + expect(response.status).toBe(200); + expect(response.body).toMatchObject({ connectionId: connection.id, installs: [] }); + await expect(db.select().from(toolConnectionInstalls)).resolves.toEqual([]); + }); }); describe("classifyRisk", () => { diff --git a/server/src/routes/tool-access.ts b/server/src/routes/tool-access.ts index dcfc4d49b75..2df274da93b 100644 --- a/server/src/routes/tool-access.ts +++ b/server/src/routes/tool-access.ts @@ -733,12 +733,16 @@ export function toolAccessRoutes( .map((install) => install.targetId), )]; for (const agentId of changedAgentIds) { + const installKey = `agent:${agentId}`; const [agent] = await db .select({ id: agents.id, companyId: agents.companyId }) .from(agents) .where(and(eq(agents.id, agentId), eq(agents.companyId, connection.companyId))) .limit(1); - if (!agent) throw forbidden("The target agent is not available in this company"); + if (!agent) { + if (!requestedKeys.has(installKey)) continue; + throw forbidden("The target agent is not available in this company"); + } const decision = await access.decide({ actor: req.actor, action: "agent_config:update",