Custom agent API#
The Custom agent provider lets you put your own service between Alfred and whatever model or orchestration you like. Alfred POSTs a task envelope and validates the response.
Configuration#
Settings → AI layer → Custom agent:
| Field | Notes |
|---|---|
| Endpoint | The URL Alfred POSTs to |
| Agent name | Optional; passed through as agent so one endpoint can host several |
| Bearer token | Optional; sent as Authorization: Bearer <token> |
Request#
{
"task": "analyze_job", // which operation is running
"agent": "job-coach", // from Settings, when set
"system": "You are Alfred…", // the system prompt
"prompt": "<candidate>…</job>", // candidate and job context
"responseType": "object", // or "text"
"schema": {/* JSON Schema */}, // only when responseType is "object"
"schemaName": "job_analysis",
"maxTokens": 16000,
}
Tasks#
task |
When |
|---|---|
analyze_job |
Fit analysis |
generate_actionables |
Prep plan |
generate_questionnaire |
Interview questions |
parse_job_posting |
Extracting fields from a pasted posting |
triage_email |
Each new email during a mailbox sync |
chat |
Ask Alfred |
connectivity_check |
The Test connection button |
Response#
Any of these shapes works:
{ "object": { /* matching the schema */ } } // structured tasks
{ "object": "{\"fitScore\": 72, …}" } // a JSON string is parsed too
{ "text": "…" } // `output` and `content` also accepted
{ "error": "what went wrong" } // surfaced to the user verbatim
A model field, if present, is recorded against the analysis so you can see
later what produced it. A non-JSON content-type is treated as plain text, so a
text-only agent needs no JSON at all for responseType: "text".
If the response doesn't validate against the schema, Alfred reports the offending field paths rather than writing a partial record.
A minimal agent#
import http from "node:http";
http
.createServer(async (req, res) => {
let body = "";
for await (const chunk of req) body += chunk;
const { task, system, prompt, responseType, schema } = JSON.parse(body);
const answer = await yourModel({
system:
responseType === "object"
? `${system}\n\nReply with JSON matching:\n${JSON.stringify(schema)}`
: system,
prompt,
});
res.writeHead(200, { "content-type": "application/json" });
res.end(
JSON.stringify(
responseType === "object"
? { object: answer, model: "my-agent-v2" }
: { text: answer, model: "my-agent-v2" },
),
);
})
.listen(4997);
Point Alfred at http://localhost:4997 and press Test connection — it sends
a connectivity_check and expects the reply to contain ALFRED OK.
Driving a local CLI
This is also the way to use a locally installed coding agent: have the endpoint spawn it in non-interactive mode and return its output.