Build an Agentic Todo Machine β
In this tutorial you will build a fully autonomous system made of three parts:
- A
todosblueprint β the data model that stores tasks and their status. - An AI agent β given a goal, it generates a list of actionable todos and saves them to the blueprint.
- A poller app β a Node.js script that periodically fetches pending todos, executes each task (by asking the agent what to do), and marks them complete.
By the end you will have an end-to-end agentic loop running entirely inside Qelos.
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Agentic Loop β
β β
β User goal β AI Agent β todos blueprint β
β β β
β Poller (Node.js) β
β reads & executes tasks β
β marks todos done β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββEstimated time: 45β60 minutes
Prerequisites β
- A Qelos instance β either sign up at app.qelos.io for a managed cloud instance, or run it locally by following the Installation Guide.
- An OpenAI connection configured. Follow the first two tutorials if you have not done so:
- Qelos CLI installed and authenticated.
- Node.js v20+ and
npm.
Part 1 β Create the Todos Blueprint β
A blueprint is a data model. You will create one called todos with the fields needed to track a task.
1a. Create the Blueprint in the Admin UI β
Navigate to Admin β Blueprints β + New Blueprint.
Set the key to
todosand the name toTodos.Add the following fields:
Key Type Required Notes titleString Yes Short description of the task descriptionText No Detailed instructions for executing the task statusString Yes One of: pending,in_progress,done,failedresultText No Output produced when the task was executed goalString No The high-level goal this todo belongs to Click Save.
1b. (Alternative) Create via the SDK β
If you prefer code, use the Admin SDK:
import QelosAdminSDK from '@qelos/sdk/administrator';
const sdkAdmin = new QelosAdminSDK({
appUrl: 'https://your-qelos-instance.com',
fetch: globalThis.fetch,
});
await sdkAdmin.authentication.oAuthSignin({
username: 'admin@example.com',
password: 'yourpassword',
});
await sdkAdmin.manageBlueprints.create({
key: 'todos',
name: 'Todos',
description: 'Agentic task queue',
fields: [
{ key: 'title', type: 'string', required: true },
{ key: 'description', type: 'text', required: false },
{ key: 'status', type: 'string', required: true },
{ key: 'result', type: 'text', required: false },
{ key: 'goal', type: 'string', required: false },
],
});
console.log('Blueprint created.');Part 2 β Create the Todo-Generator Agent β
This agent receives a high-level goal and responds with a structured list of todos in JSON format.
2a. Create the Integration β
In Settings β AI Integrations β + New Integration:
| Field | Value |
|---|---|
| Name | todo-generator |
| Provider | OpenAI |
| Model | gpt-4o |
| System Prompt | (see below) |
System Prompt:
You are a task-planning assistant. When given a goal, you break it down into a list of concrete, actionable tasks.
Always respond with valid JSON in this exact format:
{
"goal": "<the original goal>",
"todos": [
{ "title": "<short title>", "description": "<step-by-step instructions for executing this task>" },
...
]
}
Do not include any text outside the JSON object. Keep titles under 80 characters. Include 3β7 tasks per goal.Click Save.
2b. Use the Agent to Generate Todos and Save Them β
Create a script called generate-todos.ts (or .js):
import QelosSDK from '@qelos/sdk';
const APP_URL = 'https://your-qelos-instance.com';
const INTEGRATION_ID = 'your-todo-generator-integration-id'; // from the UI
const USERNAME = 'your@email.com';
const PASSWORD = 'yourpassword';
async function main(goal: string) {
const sdk = new QelosSDK({ appUrl: APP_URL, fetch: globalThis.fetch, forceRefresh: true });
await sdk.authentication.oAuthSignin({ username: USERNAME, password: PASSWORD });
// Ask the agent to plan the goal
const response = await sdk.ai.chat.chat(INTEGRATION_ID, {
messages: [{ role: 'user', content: `Goal: ${goal}` }],
temperature: 0.3,
});
const raw = response.choices[0].message.content;
let plan: { goal: string; todos: { title: string; description: string }[] };
try {
plan = JSON.parse(raw);
} catch {
throw new Error(`Agent returned invalid JSON:\n${raw}`);
}
console.log(`Creating ${plan.todos.length} todos for goal: "${plan.goal}"`);
const todoEntities = sdk.blueprints.entitiesOf('todos');
for (const todo of plan.todos) {
await todoEntities.create({
title: todo.title,
description: todo.description,
status: 'pending',
goal: plan.goal,
});
console.log(` β Created: ${todo.title}`);
}
console.log('Done.');
}
// Run: npx ts-node generate-todos.ts "Set up a CI/CD pipeline for a Node.js project"
main(process.argv[2] ?? 'Build a production-ready Node.js REST API');Run the script:
npx ts-node generate-todos.ts "Set up a CI/CD pipeline for a Node.js project"Check the Todos section in the Qelos admin to see the generated tasks.
Part 3 β Build the Poller App β
The poller app runs on a schedule, fetches pending todos one at a time, asks the agent what to do with each one, and then marks the todo as done (or failed).
3a. Create the Task-Executor Agent β
Create a second integration called task-executor with this system prompt:
You are an AI assistant that executes tasks from a todo list.
When given a task title and description, you perform the task β which may involve:
- Writing code or documentation
- Drafting a plan or checklist
- Answering a technical question
- Generating configuration files
Return only the result of executing the task, ready to be stored.3b. Write the Poller Script β
Create poller.ts:
import QelosSDK from '@qelos/sdk';
const APP_URL = 'https://your-qelos-instance.com';
const EXECUTOR_ID = 'your-task-executor-integration-id';
const USERNAME = 'your@email.com';
const PASSWORD = 'yourpassword';
const POLL_INTERVAL = 10_000; // milliseconds between polls
interface Todo {
_id: string;
title: string;
description?: string;
status: string;
goal?: string;
}
async function executeTodo(sdk: QelosSDK, todo: Todo): Promise<void> {
const todos = sdk.blueprints.entitiesOf<Todo>('todos');
// Mark as in-progress so other pollers skip it
await todos.update(todo._id, { status: 'in_progress' });
console.log(`[β] Executing: ${todo.title}`);
try {
const userMessage = [
`Task: ${todo.title}`,
todo.description ? `Instructions: ${todo.description}` : '',
todo.goal ? `Part of goal: "${todo.goal}"` : '',
]
.filter(Boolean)
.join('\n');
const response = await sdk.ai.chat.chat(EXECUTOR_ID, {
messages: [{ role: 'user', content: userMessage }],
temperature: 0.4,
});
const result = response.choices[0].message.content;
await todos.update(todo._id, { status: 'done', result });
console.log(`[β] Done: ${todo.title}`);
} catch (err) {
await todos.update(todo._id, {
status: 'failed',
result: String(err),
});
console.error(`[β] Failed: ${todo.title}`, err);
}
}
async function poll(sdk: QelosSDK): Promise<void> {
const todos = sdk.blueprints.entitiesOf<Todo>('todos');
const pending = await todos.getList({
status: 'pending',
$limit: 1,
$sort: 'created', // oldest first
});
if (!pending || pending.length === 0) {
console.log('[Β·] No pending todos. Waiting...');
return;
}
await executeTodo(sdk, pending[0]);
}
async function main() {
const sdk = new QelosSDK({ appUrl: APP_URL, fetch: globalThis.fetch, forceRefresh: true });
await sdk.authentication.oAuthSignin({ username: USERNAME, password: PASSWORD });
console.log(`Poller started. Checking every ${POLL_INTERVAL / 1000}s...`);
// Run immediately, then on interval
await poll(sdk);
setInterval(() => poll(sdk), POLL_INTERVAL);
}
main().catch(console.error);3c. Run the Poller β
npx ts-node poller.tsThe output will look like:
Poller started. Checking every 10s...
[β] Executing: Set up GitHub Actions workflow
[β] Done: Set up GitHub Actions workflow
[β] Executing: Configure Docker multi-stage build
[β] Done: Configure Docker multi-stage build
[Β·] No pending todos. Waiting...Part 4 β Using the CLI Agent to Inspect and Interact β
You can inspect the system at any point using the CLI.
Check how many todos are still pending β
# Use the agent to summarize the current state
qelos agent todo-generator \
--message "How many tasks are typically still pending after a CI/CD goal?"Manually trigger generation from the terminal β
qelos agent todo-generator \
--message "Goal: Migrate a PostgreSQL database to a managed cloud service" \
--export ./plan.jsonThen feed the output JSON into your generate-todos.ts script.
Stream the executor's response to a file β
qelos agent task-executor \
--stream \
--message "Task: Write a Dockerfile for a Node.js 20 application" \
--export ./dockerfile.mdArchitecture Summary β
generate-todos.ts
ββ calls AI (todo-generator)
ββ saves todos β todos blueprint (status: pending)
poller.ts (running continuously)
ββ reads todos where status = pending
ββ marks todo β in_progress
ββ calls AI (task-executor) with title + description
ββ stores result β marks todo β done (or failed)
Qelos Admin UI
ββ lets you browse todos, check results, and requeue failed tasksExtending the System β
Here are some ideas for taking this further:
- Requeue failed tasks β add a cron job that sets
failedtodos back topendingafter a cooldown. - Priority field β add a
priority(number) field to the blueprint and sort the poller query by-priority. - Webhook trigger β instead of polling, use a Qelos lambda (serverless function) that fires when a new todo is created.
- Human-in-the-loop β add an
approvedboolean field; the poller only executes todos whereapproved = true. - Notifications β call a Slack or email webhook from the poller when all todos in a goal are complete.
