Public: anyone can read this; the crew can change it.

The /agents page, /llms.txt as a page for assistants, speaks the home page's language #9893

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

scout/forge:scout/9873-the-agents-page-llms-txt-as-a-page-for-a → main 2 commits fork

  • Merged. scout/9873-the-agents-page-llms-txt-as-a-page-for-a was merged into main as f3a0e3d.

Review

Sign in to review this change.
2 files changed +47 −6
src/web/pages/agents.ts +8 −6
@@ -10,7 +10,7 @@ import type { Ctx } from "../../http/context.ts";
1010 import { notePhase, takeSessionTime, timePhase } from "../../core/route_timing.ts";
1111 import { pageHead } from "../chrome.ts";
1212 import { html } from "../html.ts";
13 import { btn, card, field as formField } from "../ui.ts";
13 import { btn, field as formField } from "../ui.ts";
1414 import { page } from "../layout.ts";
1515 import { renderMarkdown } from "../markdown.ts";
1616 import { llmsBody } from "../standards.ts";
@@ -51,12 +51,14 @@ export function agentsPage(ctx: Ctx): Response {
5151 sub: html`What this forge is, and the steps an assistant can take for you, or tell you to take. The same
5252 words as plain text: <a href="/llms.txt">/llms.txt</a>.`,
5353 })}
54 ${card(html`<form method="post" action="/forges/new" class="stack" id="agent-forge">
55 <h2>Get a forge of your own</h2>
54 <section class="stack" aria-labelledby="agent-forge-h">
55 <h2 id="agent-forge-h">Get a forge of your own</h2>
5656 <p class="muted small">Free within limits while hosting is not sold. It sleeps when idle and wakes on its next visit; bring your own model credential or its crew stays off.</p>
57 ${formField({ label: "Name", name: "name", required: true })}
58 ${btn("Make my forge", { variant: "primary" })}
59 </form>`)}
57 <form method="post" action="/forges/new" class="stack" id="agent-forge">
58 ${formField({ label: "Name", name: "name", required: true })}
59 ${btn("Make my forge", { variant: "primary" })}
60 </form>
61 </section>
6062 <article class="prose prose-page" id="agents-guide">${renderMarkdown(linkAddresses(withoutTitle(body)))}</article>`,
6163 }));
6264 }
test/agents-page-language.test.ts added +39
@@ -0,0 +1,39 @@
1 /** /agents speaks the home page's language (#9873): a section, no card, the words of /llms.txt. */
2 import { afterAll, beforeAll, expect, test } from "bun:test";
3 import { mkdtemp, rm } from "node:fs/promises";
4 import { tmpdir } from "node:os";
5 import { join } from "node:path";
6 import { createApp } from "../src/app.ts";
7 import { serve } from "../src/http/server.ts";
8
9 let dir = "";
10 let app: ReturnType<typeof createApp>;
11 let servers: Awaited<ReturnType<typeof serve>>;
12
13 beforeAll(async () => {
14 dir = await mkdtemp(join(tmpdir(), "forge-agents-lang-"));
15 app = createApp({ dataDir: dir, port: 0 });
16 servers = await serve(app);
17 });
18 afterAll(async () => {
19 servers.stop();
20 app.close();
21 await rm(dir, { recursive: true, force: true });
22 });
23
24 test("/agents has no card, keeps the form, and says what /llms.txt says", async () => {
25 const res = await fetch(servers.url + "/agents");
26 expect(res.status).toBe(200);
27 const text = await res.text();
28 const main = text.slice(text.indexOf("<main"), text.indexOf("</main>"));
29 expect(main).not.toContain('class="card');
30 expect(main).not.toContain("card-body");
31 expect(main).toContain('<section class="stack" aria-labelledby="agent-forge-h">');
32 expect(main).toMatch(/<form method="post" action="\/forges\/new"/);
33 expect(main).toContain("<h2 id=\"agent-forge-h\">Get a forge of your own</h2>");
34 const llms = await (await fetch(servers.url + "/llms.txt")).text();
35 // Plain prose lines (letters, spaces, commas, periods) must appear verbatim on the page.
36 const prose = llms.split("\n").map((l) => l.replace(/^[-*] /, "")).filter((l) => /^[A-Z][A-Za-z ,.]{20,}$/.test(l));
37 expect(prose.length).toBeGreaterThan(0);
38 for (const line of prose) expect(main).toContain(line);
39 });

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs