Example 4 of 4
Give it a goal and let it work it out
When you can't write the steps down, hand the model a goal and a web reader and let it pick what to open. You still set the limits: 15 steps, 25 cents, five picks.
reading.ts
import { isStepCount } from "ai";
import { z } from "zod";
import { defineAgent, defineJob } from "@chloejs/core";
import { deliverEmail } from "@chloejs/core/services";
import * as web from "@chloejs/core/tools/web";
const Picks = z.object({
picks: z.array(z.object({ title: z.string(), url: z.string(), why: z.string() })).max(5),
});
const digest = defineJob({
id: "reading",
description: "Finds the few things worth reading and emails them.",
run: async (work) => {
const { picks } = await work.agent("find what's worth reading", {
prompt:
"Start at https://news.ycombinator.com. Find up to five things a TypeScript developer " +
"should read this week. Open the ones that look good before you pick them.",
tools: { webReadPage: web.readPage() },
output: Picks,
stopWhen: isStepCount(15),
budget: 0.25,
});
const body = picks.map((one) => `- [${one.title}](${one.url}): ${one.why}`).join("\n");
await work.step("email it", () =>
deliverEmail({ from: "Chloe <chloe@example.com>", to: ["you@example.com"], markdown: true }, "Worth reading", body),
);
return { picks: picks.length };
},
});
const reader = defineAgent({
id: "reader",
description: "Reads the web so you don't have to.",
instructions: "Pick less, not more. Say why in one line.",
model: "anthropic/claude-sonnet-5",
jobs: [digest],
});
const result = await reader.run({
job: digest,
settings: { connections: { resend: { api_key: process.env.RESEND_API_KEY } } },
});
console.log(result.text);$ node reading.tsCopy it into an empty folder, npm install @chloejs/core, and put any keys in .env next to it.
That's all four. The start page shows the same thing split into folders.