mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-23 09:07:46 +02:00
Clear legacy prompt templates for bundled agents
Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
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();
|
||||
});
|
||||
});
|
||||
@@ -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"
|
||||
|
||||
@@ -83,9 +83,9 @@ curl -sS "$PAPERCLIP_API_URL/llms/agent-icons.txt" \
|
||||
- leave timer heartbeats off by default; only set `runtimeConfig.heartbeat.enabled=true` with an `intervalSec` when the role genuinely needs scheduled recurring work or the user explicitly asked for it
|
||||
- if the role may handle private advisories or sensitive disclosures, confirm a confidential workflow exists first (dedicated skill or documented manual process)
|
||||
- capabilities
|
||||
- run prompt in adapter config (`promptTemplate` where applicable)
|
||||
- managed instructions bundle (`AGENTS.md`) for adapters that support it; avoid durable `promptTemplate` config
|
||||
- for coding or execution agents, include the Paperclip execution contract: start actionable work in the same heartbeat; do not stop at a plan unless planning was requested; leave durable progress with a clear next action; use child issues for long or parallel delegated work instead of polling; mark blocked work with owner/action; respect budget, pause/cancel, approval gates, and company boundaries
|
||||
- instruction text such as `AGENTS.md` built from step 4; for local managed-bundle adapters, put the adapted `AGENTS.md` content in `adapterConfig.promptTemplate` unless you are a board user intentionally managing bundle paths/files
|
||||
- instruction text such as `AGENTS.md` built from step 4; for local managed-bundle adapters, agent-authenticated hire requests may send the adapted `AGENTS.md` in `adapterConfig.promptTemplate` only as a creation-time transport because the server materializes it into managed `AGENTS.md` and removes the legacy field. Do not set `promptTemplate` when you are already setting an instructions bundle/path.
|
||||
- source issue linkage (`sourceIssueId` or `sourceIssueIds`) when this hire came from an issue
|
||||
|
||||
### 7. Review the draft against the quality checklist
|
||||
|
||||
@@ -41,7 +41,7 @@ If you are hiring a role that is not in this index, do not force a fit. Use the
|
||||
## How to apply an exact template
|
||||
|
||||
1. Open the matching reference in `references/agents/`.
|
||||
2. Copy that template into the new agent's instruction bundle (usually `AGENTS.md`). For hire requests using local managed-bundle adapters, set the adapted template as `adapterConfig.promptTemplate`; Paperclip materializes it into `AGENTS.md`.
|
||||
2. Copy that template into the new agent's instruction bundle (usually `AGENTS.md`). For agent-authenticated hire requests using local managed-bundle adapters, `adapterConfig.promptTemplate` is only a creation-time transport for the adapted template; Paperclip materializes it into managed `AGENTS.md` and removes the legacy field. Do not combine `promptTemplate` with an explicit instructions bundle/path.
|
||||
3. Replace placeholders like `{{companyName}}`, `{{managerTitle}}`, `{{issuePrefix}}`, and URLs.
|
||||
4. Remove tools or workflows the target adapter cannot use.
|
||||
5. Keep the Paperclip heartbeat requirement and the task-comment requirement.
|
||||
|
||||
@@ -57,13 +57,13 @@ Use it for every path: exact template, adjacent template, or generic fallback.
|
||||
- [ ] `sourceIssueId` (or `sourceIssueIds`) is set when the hire was triggered by an issue
|
||||
- [ ] `desiredSkills` lists only skills that already exist in the company library, or will be installed first via the company-skills workflow
|
||||
- [ ] Adapter config matches this Paperclip instance (cwd, model, credentials) per `/llms/agent-configuration/<adapter>.txt`
|
||||
- [ ] `adapterConfig.promptTemplate` holds the adapted `AGENTS.md` for local managed-bundle adapters (unless you are intentionally managing bundle paths)
|
||||
- [ ] Local managed-bundle adapters end up with managed `AGENTS.md` config, not durable `adapterConfig.promptTemplate`; use `promptTemplate` only as the creation-time transport when the API caller cannot set bundle files directly
|
||||
- [ ] Placeholders like `{{companyName}}`, `{{managerTitle}}`, `{{issuePrefix}}`, and any URL stubs are replaced with real values
|
||||
|
||||
## H. Safety and permissions (least privilege)
|
||||
|
||||
- [ ] The hire grants only the access the role needs — no "just in case" permissions
|
||||
- [ ] No secrets are embedded in plain text in `adapterConfig` or `promptTemplate` unless the adapter explicitly requires it; prefer environment-injected credentials or scoped skills
|
||||
- [ ] No secrets are embedded in plain text in `adapterConfig`, creation-time instruction text, or any legacy prompt field; prefer environment-injected credentials or scoped skills
|
||||
- [ ] Any `desiredSkills` or adapter settings that expand external-system access, browser/network reach, filesystem scope, or secret-handling capability are individually justified in the hire comment
|
||||
- [ ] `runtimeConfig.heartbeat.enabled` is `false` unless the role genuinely needs scheduled recurring work AND `intervalSec` is justified in the hire comment
|
||||
- [ ] `AGENTS.md` explicitly names anything the role must never do (external posts, shared infra changes, destructive ops without approval)
|
||||
|
||||
Reference in new issue
Block a user