Chapter 13 of 13
Appendix: the same loop, in TypeScript
Chapter 2 defined an agent as four things: a model, tools, state, and a stopping rule. Nothing in that definition is Python. This appendix implements the same loop in TypeScript to prove it, because the most common misreading of a from-scratch course is that the pattern belongs to the language it was taught in. The code below is idiomatic strict-mode TypeScript with a real type surface: an interface for a transcript entry, a discriminated union for a tool result, and a Record of tools. It runs the identical task from chapter 2, the temperature in Paris plus 10, and produces the identical transcript and the identical 32. The types buy something concrete here. Declaring ToolResult as a union of { ok: true, value } and { ok: false, error } means the compiler refuses to let a tool failure be read as a value, which is a class of agent bug the Python labs have to catch with a test instead.
The same loop, in TypeScript
// The same loop as chapter 2, in TypeScript. Four parts, no new ideas:
// a model, tools, state, and a stopping rule.
interface Turn { role: "thought" | "action" | "observation"; text: string }
type ToolResult = { ok: true; value: number } | { ok: false; error: string };
type Tool = (arg: string) => ToolResult;
const tools: Record<string, Tool> = {
weather: (city) => city.toLowerCase().includes("paris")
? { ok: true, value: 22 }
: { ok: false, error: `no weather for ${city}` },
calculator: (expr) => {
const m = expr.match(/(-?\d+)\s*\+\s*(-?\d+)/);
return m ? { ok: true, value: +m[1]! + +m[2]! } : { ok: false, error: `cannot parse ${expr}` };
},
};
// The stand-in "model": it reads the ask and names a tool, exactly like
// academy_llm.tool_route. Deterministic so the loop is reproducible.
function toolRoute(prompt: string): string | null {
const p = prompt.toLowerCase();
if (/weather|temperature/.test(p)) return "weather";
if (/\+|plus|compute/.test(p)) return "calculator";
return null;
}
function runAgent(goal: string, plan: string[], maxSteps = 5): { answer: number | null; transcript: Turn[] } {
const transcript: Turn[] = [{ role: "thought", text: `goal: ${goal}` }];
let answer: number | null = null;
for (const [i, subgoal] of plan.entries()) {
if (i >= maxSteps) break; // the stopping rule
const thought = answer === null ? subgoal : subgoal.replace("{prev}", String(answer));
const tool = toolRoute(thought); // observe, decide
if (!tool) { transcript.push({ role: "thought", text: `no tool for: ${thought}` }); break; }
const result = tools[tool]!(thought); // act
transcript.push({ role: "action", text: `${tool}(${thought})` });
if (!result.ok) { transcript.push({ role: "observation", text: result.error }); break; }
transcript.push({ role: "observation", text: String(result.value) }); // append
answer = result.value;
}
return { answer, transcript };
}
const { answer, transcript } = runAgent(
"the temperature in Paris plus 10",
["look up the weather temperature in Paris", "compute {prev} + 10"],
);
for (const t of transcript) console.log(`${t.role.padEnd(12)} ${t.text}`);
console.log(`\nanswer: ${answer}`);
console.log(answer === 32 ? "SAME LOOP, DIFFERENT SYNTAX: YES" : "SAME LOOP, DIFFERENT SYNTAX: NO");
Same four parts. The model is toolRoute, the tools are the Record, the state is the transcript array, and the stopping rule is maxSteps plus the break when no tool routes. Same failure modes too: remove the step cap and it runs away exactly as chapter 2's would, share one mutable transcript between two concurrent agents and you have chapter 9's race, let the transcript grow unbounded and you have chapter 11's cost curve. The syntax changed and nothing else did.
Everything else in this certification stays Python, deliberately. The from-scratch machine learning material is genuinely Python-native: the tensor libraries, the tokenizers, and nearly every reference implementation worth reading live there, and rewriting that material in TypeScript would cost real teaching clarity to make a point this single page already makes. So this appendix exists to prove the concept is universal, not to open a second track. Build your production agents in whatever language your team already ships, and carry the four parts with you.
Check your understanding
1. Why does this certification include one TypeScript chapter?
2. What does the ToolResult union type add over returning a plain number?