Clear legacy prompt templates for bundled agents

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
DottaandPaperclip committed 2026-04-28 09:15:53 -05:00
1 parent 930b717ba5
commit e7d32f19cd
5 files changed
+79 -6

No files matched your search

@@ -504,6 +504,37 @@ describe.sequential("agent skill routes", () => {
});
});
it("clears legacy prompt templates when directly created local agents already provide instructions", async () => {
const res = await requestApp(await createApp(), (baseUrl) => request(baseUrl)
.post("/api/companies/company-1/agents")
.send({
name: "QA Agent",
role: "engineer",
adapterType: "claude_local",
adapterConfig: {
instructionsFilePath: "/tmp/existing/AGENTS.md",
promptTemplate: "You are QA.",
bootstrapPromptTemplate: "Bootstrap QA.",
},
}));
expect([200, 201], JSON.stringify(res.body)).toContain(res.status);
expect(mockAgentInstructionsService.materializeManagedBundle).not.toHaveBeenCalled();
expect(mockAgentService.update).toHaveBeenCalledWith(
"11111111-1111-4111-8111-111111111111",
expect.objectContaining({
adapterConfig: expect.objectContaining({
instructionsFilePath: "/tmp/existing/AGENTS.md",
}),
}),
);
const updateInput = mockAgentService.update.mock.calls.at(-1)?.[1] as
| { adapterConfig?: Record<string, unknown> }
| undefined;
expect(updateInput?.adapterConfig?.promptTemplate).toBeUndefined();
expect(updateInput?.adapterConfig?.bootstrapPromptTemplate).toBeUndefined();
});
it("materializes the bundled CEO instruction set for default CEO agents", async () => {
const res = await requestApp(await createApp(), (baseUrl) => request(baseUrl)
.post("/api/companies/company-1/agents")
@@ -675,4 +706,37 @@ describe.sequential("agent skill routes", () => {
| undefined;
expect(approvalInput?.payload?.adapterConfig?.promptTemplate).toBeUndefined();
});
it("clears legacy prompt templates from hire approval payloads that already provide instructions", async () => {
const res = await request(await createApp(createDb(true)))
.post("/api/companies/company-1/agent-hires")
.send({
name: "QA Agent",
role: "engineer",
adapterType: "claude_local",
adapterConfig: {
instructionsFilePath: "/tmp/existing/AGENTS.md",
promptTemplate: "You are QA.",
bootstrapPromptTemplate: "Bootstrap QA.",
},
});
expect(res.status, JSON.stringify(res.body)).toBe(201);
expect(mockAgentInstructionsService.materializeManagedBundle).not.toHaveBeenCalled();
expect(mockApprovalService.create).toHaveBeenCalledWith(
"company-1",
expect.objectContaining({
payload: expect.objectContaining({
adapterConfig: expect.objectContaining({
instructionsFilePath: "/tmp/existing/AGENTS.md",
}),
}),
}),
);
const approvalInput = mockApprovalService.create.mock.calls.at(-1)?.[1] as
| { payload?: { adapterConfig?: Record<string, unknown> } }
| undefined;
expect(approvalInput?.payload?.adapterConfig?.promptTemplate).toBeUndefined();
expect(approvalInput?.payload?.adapterConfig?.bootstrapPromptTemplate).toBeUndefined();
});
});
+10 -1
View File
@@ -703,7 +703,16 @@ export function agentRoutes(
|| Boolean(asNonEmptyString(adapterConfig.instructionsFilePath))
|| Boolean(asNonEmptyString(adapterConfig.agentsMdPath));
if (hasExplicitInstructionsBundle) {
return agent;
const nextAdapterConfig = { ...adapterConfig };
const hadLegacyPrompt =
Object.prototype.hasOwnProperty.call(nextAdapterConfig, "promptTemplate")
|| Object.prototype.hasOwnProperty.call(nextAdapterConfig, "bootstrapPromptTemplate");
delete nextAdapterConfig.promptTemplate;
delete nextAdapterConfig.bootstrapPromptTemplate;
if (!hadLegacyPrompt) return agent;
const updated = await svc.update(agent.id, { adapterConfig: nextAdapterConfig });
return (updated as T | null) ?? { ...agent, adapterConfig: nextAdapterConfig };
}
const promptTemplate = typeof adapterConfig.promptTemplate === "string"