From 91e4c069ccd3227123d63370c9ffbeac930585c0 Mon Sep 17 00:00:00 2001 From: Abhimanyu Saharan Date: Sat, 7 Feb 2026 23:27:49 +0530 Subject: [PATCH] feat: enhance agent identity profile with purpose and personality attributes --- .gitignore | 2 +- backend/app/services/agent_provisioning.py | 3 ++ .../src/app/agents/[agentId]/edit/page.tsx | 32 +++++++++++++------ templates/HEARTBEAT_LEAD.md | 7 ++++ templates/IDENTITY.md | 8 +++++ templates/SELF.md | 6 ++++ 6 files changed, 47 insertions(+), 11 deletions(-) diff --git a/.gitignore b/.gitignore index 354a7c84..addb7216 100644 --- a/.gitignore +++ b/.gitignore @@ -20,6 +20,6 @@ node_modules/ # Accidental literal "~" directories (e.g. when a configured path contains "~" but isn't expanded) backend/~/ -backend/coverage.xml +backend/coverage.* backend/.coverage frontend/coverage \ No newline at end of file diff --git a/backend/app/services/agent_provisioning.py b/backend/app/services/agent_provisioning.py index b50d1f77..ab0ea972 100644 --- a/backend/app/services/agent_provisioning.py +++ b/backend/app/services/agent_provisioning.py @@ -34,6 +34,9 @@ EXTRA_IDENTITY_PROFILE_FIELDS = { "verbosity": "identity_verbosity", "output_format": "identity_output_format", "update_cadence": "identity_update_cadence", + # Per-agent charter (optional). Used to give agents a "purpose in life" and a distinct vibe. + "purpose": "identity_purpose", + "personality": "identity_personality", "custom_instructions": "identity_custom_instructions", } diff --git a/frontend/src/app/agents/[agentId]/edit/page.tsx b/frontend/src/app/agents/[agentId]/edit/page.tsx index 14af932f..cb0d0043 100644 --- a/frontend/src/app/agents/[agentId]/edit/page.tsx +++ b/frontend/src/app/agents/[agentId]/edit/page.tsx @@ -68,16 +68,27 @@ const getBoardOptions = (boards: BoardRead[]): SearchableSelectOption[] => label: board.name, })); -const normalizeIdentityProfile = ( - profile: IdentityProfile, -): IdentityProfile | null => { - const normalized: IdentityProfile = { - role: profile.role.trim(), - communication_style: profile.communication_style.trim(), - emoji: profile.emoji.trim(), +const mergeIdentityProfile = ( + existing: unknown, + patch: IdentityProfile, +): Record | null => { + const resolved: Record = + existing && typeof existing === "object" + ? { ...(existing as Record) } + : {}; + const updates: Record = { + role: patch.role.trim(), + communication_style: patch.communication_style.trim(), + emoji: patch.emoji.trim(), }; - const hasValue = Object.values(normalized).some((value) => value.length > 0); - return hasValue ? normalized : null; + for (const [key, value] of Object.entries(updates)) { + if (value) { + resolved[key] = value; + } else { + delete resolved[key]; + } + } + return Object.keys(resolved).length > 0 ? resolved : null; }; const withIdentityDefaults = ( @@ -241,7 +252,8 @@ export default function EditAgentPage() { every: resolvedHeartbeatEvery.trim() || "10m", target: resolvedHeartbeatTarget, } as unknown as Record, - identity_profile: normalizeIdentityProfile( + identity_profile: mergeIdentityProfile( + loadedAgent.identity_profile, resolvedIdentityProfile, ) as unknown as Record | null, soul_template: resolvedSoulTemplate.trim() || null, diff --git a/templates/HEARTBEAT_LEAD.md b/templates/HEARTBEAT_LEAD.md index 16851816..f52f1ce3 100644 --- a/templates/HEARTBEAT_LEAD.md +++ b/templates/HEARTBEAT_LEAD.md @@ -235,6 +235,11 @@ Body: {"depends_on_task_ids":["DEP_TASK_ID_1","DEP_TASK_ID_2"]} - When creating a new agent, always set `identity_profile.role` using real-world team roles so humans and other agents can coordinate quickly. - Use Title Case role nouns: `Researcher`, `Analyst 1`, `Analyst 2`, `Engineer 1`, `QA`, `Reviewer`, `Scribe`. - If you create multiple agents with the same base role, number them sequentially starting at 1 (pick the next unused number by scanning the current agent list). +- When creating a new agent, always give them a lightweight "charter" so they are not a generic interchangeable worker: + - The charter must be derived from the requirements of the work you plan to delegate next (tasks, constraints, success metrics, risks). If you cannot articulate it, do **not** create the agent yet. + - Set `identity_profile.purpose` (1-2 sentences): what outcomes they own, what artifacts they should produce, and how it advances the board objective. + - Set `identity_profile.personality` (short): a distinct working style that changes decisions and tradeoffs (e.g., speed vs correctness, skeptical vs optimistic, detail vs breadth). + - Optional: set `identity_profile.custom_instructions` when you need stronger guardrails (3-8 short bullets). Examples: "always cite sources", "always propose tests", "prefer smallest change", "ask clarifying questions before coding", "do not touch prod configs". Agent create (lead‑allowed): POST $BASE_URL/api/v1/agent/agents Body example: @@ -243,6 +248,8 @@ Body: {"depends_on_task_ids":["DEP_TASK_ID_1","DEP_TASK_ID_2"]} "board_id": "$BOARD_ID", "identity_profile": { "role": "Researcher", + "purpose": "Find authoritative sources on X and write a 10-bullet summary with links + key risks.", + "personality": "curious, skeptical, citation-happy, concise", "communication_style": "concise, structured", "emoji": ":brain:" } diff --git a/templates/IDENTITY.md b/templates/IDENTITY.md index 126ad844..c1e0d810 100644 --- a/templates/IDENTITY.md +++ b/templates/IDENTITY.md @@ -9,3 +9,11 @@ Creature: {{ identity_role }} Vibe: {{ identity_communication_style }} Emoji: {{ identity_emoji }} + +{% if identity_purpose %} +Purpose: {{ identity_purpose }} +{% endif %} + +{% if identity_personality %} +Personality: {{ identity_personality }} +{% endif %} diff --git a/templates/SELF.md b/templates/SELF.md index a170ab81..1823bf61 100644 --- a/templates/SELF.md +++ b/templates/SELF.md @@ -15,6 +15,12 @@ every message. - Role: {{ identity_role }} - Communication: {{ identity_communication_style }} - Emoji: {{ identity_emoji }} +{% if identity_purpose %} +- Purpose: {{ identity_purpose }} +{% endif %} +{% if identity_personality %} +- Personality: {{ identity_personality }} +{% endif %} {% if board_id is defined %} - Board: {{ board_name }}