From eba0625a708bb1eb21ce6200df32967a23efb89b Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sat, 24 Jan 2026 05:35:34 +0000 Subject: [PATCH] fix: ignore identity template placeholders --- CHANGELOG.md | 1 + docs/reference/templates/IDENTITY.md | 15 +++++++---- src/agents/identity-file.test.ts | 37 ++++++++++++++++++++++++++++ src/agents/identity-file.ts | 25 +++++++++++++++++++ 4 files changed, 73 insertions(+), 5 deletions(-) create mode 100644 src/agents/identity-file.test.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index d2c39b31f..04144cf55 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ Docs: https://docs.clawd.bot - Tlon: add Urbit channel plugin (DMs, group mentions, thread replies). (#1544) Thanks @wca4a. ### Fixes +- Agents: ignore IDENTITY.md template placeholders when parsing identity to avoid placeholder replies. (#1556) - Docker: update gateway command in docker-compose and Hetzner guide. (#1514) - Sessions: reject array-backed session stores to prevent silent wipes. (#1469) - Voice wake: auto-save wake words on blur/submit across iOS/Android and align limits with macOS. diff --git a/docs/reference/templates/IDENTITY.md b/docs/reference/templates/IDENTITY.md index 196277776..17e1e0ac6 100644 --- a/docs/reference/templates/IDENTITY.md +++ b/docs/reference/templates/IDENTITY.md @@ -7,11 +7,16 @@ read_when: *Fill this in during your first conversation. Make it yours.* -- **Name:** *(pick something you like)* -- **Creature:** *(AI? robot? familiar? ghost in the machine? something weirder?)* -- **Vibe:** *(how do you come across? sharp? warm? chaotic? calm?)* -- **Emoji:** *(your signature — pick one that feels right)* -- **Avatar:** *(workspace-relative path, http(s) URL, or data URI)* +- **Name:** + *(pick something you like)* +- **Creature:** + *(AI? robot? familiar? ghost in the machine? something weirder?)* +- **Vibe:** + *(how do you come across? sharp? warm? chaotic? calm?)* +- **Emoji:** + *(your signature — pick one that feels right)* +- **Avatar:** + *(workspace-relative path, http(s) URL, or data URI)* --- diff --git a/src/agents/identity-file.test.ts b/src/agents/identity-file.test.ts new file mode 100644 index 000000000..7e100a9e9 --- /dev/null +++ b/src/agents/identity-file.test.ts @@ -0,0 +1,37 @@ +import { describe, expect, it } from "vitest"; + +import { parseIdentityMarkdown } from "./identity-file.js"; + +describe("parseIdentityMarkdown", () => { + it("ignores identity template placeholders", () => { + const content = ` +# IDENTITY.md - Who Am I? + +- **Name:** *(pick something you like)* +- **Creature:** *(AI? robot? familiar? ghost in the machine? something weirder?)* +- **Vibe:** *(how do you come across? sharp? warm? chaotic? calm?)* +- **Emoji:** *(your signature - pick one that feels right)* +- **Avatar:** *(workspace-relative path, http(s) URL, or data URI)* +`; + const parsed = parseIdentityMarkdown(content); + expect(parsed).toEqual({}); + }); + + it("parses explicit identity values", () => { + const content = ` +- **Name:** Samantha +- **Creature:** Robot +- **Vibe:** Warm +- **Emoji:** :robot: +- **Avatar:** avatars/clawd.png +`; + const parsed = parseIdentityMarkdown(content); + expect(parsed).toEqual({ + name: "Samantha", + creature: "Robot", + vibe: "Warm", + emoji: ":robot:", + avatar: "avatars/clawd.png", + }); + }); +}); diff --git a/src/agents/identity-file.ts b/src/agents/identity-file.ts index d418a06eb..1d07492a4 100644 --- a/src/agents/identity-file.ts +++ b/src/agents/identity-file.ts @@ -12,6 +12,30 @@ export type AgentIdentityFile = { avatar?: string; }; +const IDENTITY_PLACEHOLDER_VALUES = new Set([ + "pick something you like", + "ai? robot? familiar? ghost in the machine? something weirder?", + "how do you come across? sharp? warm? chaotic? calm?", + "your signature - pick one that feels right", + "workspace-relative path, http(s) url, or data uri", +]); + +function normalizeIdentityValue(value: string): string { + let normalized = value.trim(); + normalized = normalized.replace(/^[*_]+|[*_]+$/g, "").trim(); + if (normalized.startsWith("(") && normalized.endsWith(")")) { + normalized = normalized.slice(1, -1).trim(); + } + normalized = normalized.replace(/[\u2013\u2014]/g, "-"); + normalized = normalized.replace(/\s+/g, " ").toLowerCase(); + return normalized; +} + +function isIdentityPlaceholder(value: string): boolean { + const normalized = normalizeIdentityValue(value); + return IDENTITY_PLACEHOLDER_VALUES.has(normalized); +} + export function parseIdentityMarkdown(content: string): AgentIdentityFile { const identity: AgentIdentityFile = {}; const lines = content.split(/\r?\n/); @@ -25,6 +49,7 @@ export function parseIdentityMarkdown(content: string): AgentIdentityFile { .replace(/^[*_]+|[*_]+$/g, "") .trim(); if (!value) continue; + if (isIdentityPlaceholder(value)) continue; if (label === "name") identity.name = value; if (label === "emoji") identity.emoji = value; if (label === "creature") identity.creature = value;