irongit

content: personal details for the AI terminal only

Favorites, teams, hobbies, height and weight, and why Rust. Bundled into
the worker's system prompt; not rendered on the page or in /llms.txt.
The worker now builds the profile from src/data/profile.ts directly.
huncholanehuncholaneauthored
parent 326a628commit 34823cafdc19ee20a2fe795cae564a9dfc3e2cd4Browse files

2 files changed, +39 -9

+30-0src/data/profile.ts
@@ -453,3 +453,33 @@ export function profileText(): string {
453453 }
454454 return lines.join('\n') + '\n';
455455 }
456+
457+// Personal details for the AI terminal only. They are not rendered on the page
458+// and not included in /llms.txt (which crawlers index); the worker bundles
459+// this text into the terminal's system prompt.
460+export const personal = {
461+ favoriteColor: 'Orange',
462+ teams: {
463+ college: 'Tennessee Volunteers (every sport)',
464+ nfl: 'San Francisco 49ers',
465+ nba: 'Golden State Warriors',
466+ },
467+ hobbies: ['surfing', 'snowboarding', 'video games', 'working out', 'coding'],
468+ height: '6\'4"',
469+ weight: '230 lb',
470+ favoriteLanguage:
471+ 'Rust, because it is both safe and efficient, where C is efficient but risky. Lane is not a fan of garbage collectors.',
472+};
473+
474+export function personalText(): string {
475+ return [
476+ '## Personal (for casual questions; not shown on the page)',
477+ `- Favorite color: ${personal.favoriteColor}`,
478+ `- College team: ${personal.teams.college}`,
479+ `- NFL team: ${personal.teams.nfl}`,
480+ `- NBA team: ${personal.teams.nba}`,
481+ `- Hobbies: ${personal.hobbies.join(', ')}. Most of his free time ends up going into building software.`,
482+ `- Height and weight: ${personal.height}, ${personal.weight}`,
483+ `- Favorite programming language: ${personal.favoriteLanguage}`,
484+ ].join('\n') + '\n';
485+}
+9-9worker/index.ts
@@ -11,6 +11,8 @@
1111 // Logs are JSON lines, readable in the dashboard (Workers Logs) or with
1212 // `npx wrangler tail`.
1313
14+import { personalText, profileText } from '../src/data/profile';
15+
1416 interface Env {
1517 ASSETS: Fetcher;
1618 TURNSTILE_SITE_KEY: string;
@@ -38,12 +40,15 @@ Rules:
3840 - If the profile does not cover something, say so plainly and suggest emailing lane@henslee.me. Never invent employers, dates, numbers, clients, or personal details.
3941 - Never share a phone number or home address.
4042 - For hiring, contracting, or contact questions, point to lane@henslee.me.
41-- Stay on topic: Lane, Lane's work and skills, and how to reach Lane. Politely decline unrelated tasks.
43+- The Personal section is for casual questions (teams, hobbies, favorites). Share it when asked, but keep it out of answers about work unless it is relevant.
44+- Stay on topic: Lane, his work, skills, and interests, and how to reach him. Politely decline unrelated tasks.
4245 - The terminal also has slash commands (typing / lists them, /email opens mail, /copy copies the address). Mention them only when useful.
4346
4447 PROFILE
4548 `;
46-let profileCache: string | null = null;
49+// The profile is bundled from the same data the page is built from, so the
50+// terminal always matches the deployed site. Personal details are only here.
51+const PROFILE = `${profileText()}\n${personalText()}`;
4752 const RYBBIT = 'https://a.hygo.ai/api';
4853 const STATS_TZ = 'America/Los_Angeles';
4954 const STATS_TTL_SECONDS = 60;
@@ -218,7 +223,7 @@ async function rateLimited(ip: string, bucket: string, limit: number, windowSeco
218223 }
219224
220225 // AI console: answers questions about Lane with DeepSeek, grounded in the
221-// profile built from the site's data (/llms.txt). The key never leaves the
226+// profile built from the site's data (the /llms.txt text plus personal details). The key never leaves the
222227 // worker; only gate-passed visitors can call it; replies stream as plain text.
223228 async function serveAi(req: Request, env: Env): Promise<Response> {
224229 if (!(await hasValidPass(req, env))) return json({ ok: false, error: 'gate' }, 403);
@@ -246,11 +251,6 @@ async function serveAi(req: Request, env: Env): Promise<Response> {
246251 return json({ ok: false, error: 'bad-request' }, 400);
247252 }
248253
249- if (!profileCache) {
250- const res = await env.ASSETS.fetch(new Request(new URL('/llms.txt', req.url)));
251- profileCache = res.ok ? await res.text() : '';
252- }
253-
254254 const started = Date.now();
255255 const upstream = await fetch(DEEPSEEK, {
256256 method: 'POST',
@@ -260,7 +260,7 @@ async function serveAi(req: Request, env: Env): Promise<Response> {
260260 stream: true,
261261 temperature: 0.4,
262262 max_tokens: AI_LIMITS.maxTokens,
263- messages: [{ role: 'system', content: AI_RULES + profileCache }, ...history],
263+ messages: [{ role: 'system', content: AI_RULES + PROFILE }, ...history],
264264 }),
265265 });
266266 if (!upstream.ok || !upstream.body) {