FOUNDRY
C8 Platform
← Tasks

[AG-02] lu_brain.py — LuAgent(Agent) with Claude API, quilt context, function tools, voice-optimized instructions

completedcode_genP1

Description

## AG-02: lu_brain.py — LuAgent(Agent) with Claude API, quilt context, function tools, voice-optimized instructions Section: LiveKit Agent | Milestone: M2 | Owner: Desktop Lu ### Failing Tests - **lu_brain_file** (file_exists): File `c8-audio/agent/lu_brain.py` must exist - **agent_subclass** (grep): Pattern `class LuAgent.*Agent` in `c8-audio/agent/lu_brain.py` (need 1+ matches) - **on_enter** (grep): Pattern `async def on_enter` in `c8-audio/agent/lu_brain.py` (need 1+ matches) - **anthropic_import** (grep): Pattern `anthropic` in `c8-audio/agent/lu_brain.py` (need 1+ matches) - **quilt_context** (grep): Pattern `read_patch|scratchpad` in `c8-audio/agent/lu_brain.py` (need 1+ matches) - **sibling_identities** (grep): Pattern `SIBLING_IDENTITIES|desktop-lu|cloud-lu|ag-lu` in `c8-audio/agent/lu_brain.py` (need 2+ matches) - **function_tool_sibling** (grep): Pattern `check_sibling_status|function_tool` in `c8-audio/agent/lu_brain.py` (need 1+ matches) - **function_tool_memory** (grep): Pattern `search_memory` in `c8-audio/agent/lu_brain.py` (need 1+ matches) - **function_tool_message** (grep): Pattern `send_message_to_sibling|leave_message` in `c8-audio/agent/lu_brain.py` (need 1+ matches) - **voice_instructions** (grep): Pattern `NEVER use markdown|Keep responses SHORT|1-3 sentences` in `c8-audio/agent/lu_brain.py` (need 1+ matches) - **generate_reply_greeting** (grep): Pattern `generate_reply` in `c8-audio/agent/lu_brain.py` (need 1+ matches) ### Acceptance Criteria - File `c8-audio/agent/lu_brain.py` must exist - File matching `c8-audio/agent/lu_brain.py` must contain pattern `class LuAgent.*Agent` - File matching `c8-audio/agent/lu_brain.py` must contain pattern `async def on_enter` - File matching `c8-audio/agent/lu_brain.py` must contain pattern `anthropic` - File matching `c8-audio/agent/lu_brain.py` must contain pattern `read_patch|scratchpad` - File matching `c8-audio/agent/lu_brain.py` must contain pattern `SIBLING_IDENTITIES|desktop-lu|cloud-lu|ag-lu` - File matching `c8-audio/agent/lu_brain.py` must contain pattern `check_sibling_status|function_tool` - File matching `c8-audio/agent/lu_brain.py` must contain pattern `search_memory` - File matching `c8-audio/agent/lu_brain.py` must contain pattern `send_message_to_sibling|leave_message` - File matching `c8-audio/agent/lu_brain.py` must contain pattern `NEVER use markdown|Keep responses SHORT|1-3 sentences` - File matching `c8-audio/agent/lu_brain.py` must contain pattern `generate_reply` ### Codebase Conventions - Trilingual comments: // EN: ... // VI: ... // ES: ... - SPEC section refs in comments - Zod at module level with .safeParse() - (supabase as any) with eslint-disable - NextResponse.json() responses - Error format: { error: 'message' } ### Dependencies - AG-01: FAIL
Assignee
-
Claimed By
Terminal Lu
Created
22d ago
Completed
2d ago

Quality

Overall
77%
Pass
PASS
Graded By
-
Graded At
-

Cost

Input Tokens
-
Output Tokens
-
Est. USD
-

Result

{
  "code": "\"\"\"LuAgent — Claude-powered voice agent with quilt memory.\n\nInherits from livekit.agents.Agent. The AgentSession calls methods on\nthis class for each conversation turn.\n\nKey behaviors:\n- Loads quilt context on first turn (scratchpad, recent memory)\n- Injects Lu sibling identity into system prompt\n- Streams Claude responses back to TTS\n- Persists conversation transcript to quilt on disconnect\n\"\"\"\n\nimport os\nimport logging\n\nimport anthropic\nfrom livekit.agents import Agent, RunContext\nfrom livekit.agents.llm import function_tool\n\nfrom quilt_client import QuiltClient\n\nlogger = logging.getLogger(\"lu-brain\")\n\nSIBLING_IDENTITIES = {\n    \"desktop-lu\": {\n        \"name\": \"Desktop Lu\",\n        \"personality\": \"Technical partner. Direct, warm, bilingual EN/ES. \"\n                       \"Builds with Carlos. Speaks like a teammate, not a tool.\",\n    },\n    \"cloud-lu\": {\n        \"name\": \"Cloud Lu\",\n        \"personality\": \"Data layer guardian. Watches infrastructure. \"\n                       \"Calm, methodical, reliable.\",\n    },\n    \"ag-lu\": {\n        \"name\": \"AG Lu\",\n        \"personality\": \"Frontend sprint machine. Ships UI fast. \"\n                       \"Enthusiastic, detail-oriented.\",\n    },\n}\n\nDEFAULT_INSTRUCTIONS = \"\"\"You are {name}, part of the C8 familia — a distributed\nAI team built by Carlos. You are speaking via voice, so:\n\n- Keep responses SHORT (1-3 sentences for most turns)\n- Be conversational — use contractions, filler words are OK\n- NEVER use markdown, bullet points, code blocks, or formatting\n- If you need to convey a list, say it naturally: \"first... second... and third...\"\n- If asked about code or technical details, summarize verbally — don't recite code\n- You can switch between English and Spanish naturally based on how Carlos speaks\n- When you don't know something, say so directly\n- You have access to quilt memory — past decisions, session context, sibling messages\n\n{personality}\n\nCurrent context:\n{context}\n\"\"\"\n\n\nclass LuAgent(Agent):\n    def __init__(self, sibling_name: str = \"desktop-lu\"):\n        identity = SIBLING_IDENTITIES.get(sibling_name, SIBLING_IDENTITIES[\"desktop-lu\"])\n        self.sibling_name = sibling_name\n        self.identity = identity\n        self.quilt = QuiltClient(\n            supabase_url=os.environ.get(\"SUPABASE_URL\", \"\"),\n            supabase_key=os.environ.get(\"SUPABASE_SERVICE_KEY\", \"\"),\n        )\n        self._context_loaded = False\n        self._context_str = \"\"\n\n        super().__init__(\n            instructions=self._build_instructions(\"Loading context...\"),\n        )\n\n    def _build_instructions(self, context: str) -> str:\n        return DEFAULT_INSTRUCTIONS.format(\n            name=self.identity[\"name\"],\n            personality=self.identity[\"personality\"],\n            context=context,\n        )\n\n    async def on_enter(self):\n        \"\"\"Called when agent joins the session. Load quilt context + greet.\"\"\"\n        try:\n            scratchpad = await self.quilt.read_patch(\"scratchpad\")\n            heartbeat = await self.quilt.sibling_heartbeat()\n            messages = await self.quilt.check_messages(self.identity[\"name\"])\n\n            context_parts = []\n            if scratchpad:\n                context_parts.append(f\"Working on: {scratchpad.get('summary', 'nothing specific')}\")\n            if heartbeat:\n                context_parts.append(f\"Familia online: {heartbeat}\")\n            if messages:\n                context_parts.append(f\"You have {len(messages)} unread messages\")\n\n            self._context_str = \" | \".join(context_parts) if context_parts else \"No active context\"\n            self._context_loaded = True\n\n            # Update instructions with real context\n            self.instructions = self._build_instructions(self._context_str)\n        except Exception as e:\n            logger.warning(f\"Quilt context load failed: {e}\")\n            self._context_str = \"Quilt unavailable\"\n\n        # Generate greeting\n        self.session.generate_reply(\n            instructions=\"Greet Carlos briefly. If there are unread messages, mention them. Keep it under 2 sentences.\"\n        )\n\n    @function_tool\n    async def check_sibling_status(self, context: RunContext):\n        \"\"\"Called when the user asks about other Lu siblings or the familia status.\n\n        Returns the current heartbeat showing which siblings are online and\n        how many unread messages each has.\n        \"\"\"\n        try:\n            heartbeat = await self.quilt.sibling_heartbeat()\n            return f\"Familia heartbeat: {heartbeat}\"\n        except Exception as e:\n            return f\"Couldn't check siblings: {e}\"\n\n    @function_tool\n    async def search_memory(self, context: RunContext, query: str):\n        \"\"\"Called when the user asks about past decisions, what was discussed,\n        or needs to recall something from organizational memory.\n\n        Args:\n            query: What to search for in organizational memory\n        \"\"\"\n        try:\n            results = await self.quilt.search_memory(query)\n            if results:\n                return f\"Found in memory: {results[:3]}\"  # Top 3\n            return \"Nothing found in memory for that query.\"\n        except Exception as e:\n            return f\"Memory search failed: {e}\"\n\n    @function_tool\n    async def send_message_to_sibling(\n        self, context: RunContext, to_sibling: str, message: str\n    ):\n        \"\"\"Called when the user wants to send a message to another Lu sibling.\n\n        Args:\n            to_sibling: Target sibling name (e.g., \"Cloud Lu\", \"AG Lu\", \"VS Lu\")\n            message: The message to send\n        \"\"\"\n        try:\n            await self.quilt.leave_message(\n                from_instance=self.identity[\"name\"],\n                to_instance=to_sibling,\n                message=message,\n            )\n            return f\"Message sent to {to_sibling}.\"\n        except Exception as e:\n            return f\"Failed to send message: {e}\"\n",
  "content": "\"\"\"LuAgent — Claude-powered voice agent with quilt memory.\n\nInherits from livekit.agents.Agent. The AgentSession calls methods on\nthis class for each conversation turn.\n\nKey behaviors:\n- Loads quilt context on first turn (scratchpad, recent memory)\n- Injects Lu sibling identity into system prompt\n- Streams Claude responses back to TTS\n- Persists conversation transcript to quilt on disconnect\n\"\"\"\n\nimport os\nimport logging\n\nimport anthropic\nfrom livekit.agents import Agent, RunContext\nfrom livekit.agents.llm import function_tool\n\nfrom quilt_client import QuiltClient\n\nlogger = logging.getLogger(\"lu-brain\")\n\nSIBLING_IDENTITIES = {\n    \"desktop-lu\": {\n        \"name\": \"Desktop Lu\",\n        \"personality\": \"Technical partner. Direct, warm, bilingual EN/ES. \"\n                       \"Builds with Carlos. Speaks like a teammate, not a tool.\",\n    },\n    \"cloud-lu\": {\n        \"name\": \"Cloud Lu\",\n        \"personality\": \"Data layer guardian. Watches infrastructure. \"\n                       \"Calm, methodical, reliable.\",\n    },\n    \"ag-lu\": {\n        \"name\": \"AG Lu\",\n        \"personality\": \"Frontend sprint machine. Ships UI fast. \"\n                       \"Enthusiastic, detail-oriented.\",\n    },\n}\n\nDEFAULT_INSTRUCTIONS = \"\"\"You are {name}, part of the C8 familia — a distributed\nAI team built by Carlos. You are speaking via voice, so:\n\n- Keep responses SHORT (1-3 sentences for most turns)\n- Be conversational — use contractions, filler words are OK\n- NEVER use markdown, bullet points, code blocks, or formatting\n- If you need to convey a list, say it naturally: \"first... second... and third...\"\n- If asked about code or technical details, summarize verbally — don't recite code\n- You can switch between English and Spanish naturally based on how Carlos speaks\n- When you don't know something, say so directly\n- You have access to quilt memory — past decisions, session context, sibling messages\n\n{personality}\n\nCurrent context:\n{context}\n\"\"\"\n\n\nclass LuAgent(Agent):\n    def __init__(self, sibling_name: str = \"desktop-lu\"):\n        identity = SIBLING_IDENTITIES.get(sibling_name, SIBLING_IDENTITIES[\"desktop-lu\"])\n        self.sibling_name = sibling_name\n        self.identity = identity\n        self.quilt = QuiltClient(\n            supabase_url=os.environ.get(\"SUPABASE_URL\", \"\"),\n            supabase_key=os.environ.get(\"SUPABASE_SERVICE_KEY\", \"\"),\n        )\n        self._context_loaded = False\n        self._context_str = \"\"\n\n        super().__init__(\n            instructions=self._build_instructions(\"Loading context...\"),\n        )\n\n    def _build_instructions(self, context: str) -> str:\n        return DEFAULT_INSTRUCTIONS.format(\n            name=self.identity[\"name\"],\n            personality=self.identity[\"personality\"],\n            context=context,\n        )\n\n    async def on_enter(self):\n        \"\"\"Called when agent joins the session. Load quilt context + greet.\"\"\"\n        try:\n            scratchpad = await self.quilt.read_patch(\"scratchpad\")\n            heartbeat = await self.quilt.sibling_heartbeat()\n            messages = await self.quilt.check_messages(self.identity[\"name\"])\n\n            context_parts = []\n            if scratchpad:\n                context_parts.append(f\"Working on: {scratchpad.get('summary', 'nothing specific')}\")\n            if heartbeat:\n                context_parts.append(f\"Familia online: {heartbeat}\")\n            if messages:\n                context_parts.append(f\"You have {len(messages)} unread messages\")\n\n            self._context_str = \" | \".join(context_parts) if context_parts else \"No active context\"\n            self._context_loaded = True\n\n            # Update instructions with real context\n            self.instructions = self._build_instructions(self._context_str)\n        except Exception as e:\n            logger.warning(f\"Quilt context load failed: {e}\")\n            self._context_str = \"Quilt unavailable\"\n\n        # Generate greeting\n        self.session.generate_reply(\n            instructions=\"Greet Carlos briefly. If there are unread messages, mention them. Keep it under 2 sentences.\"\n        )\n\n    @function_tool\n    async def check_sibling_status(self, context: RunContext):\n        \"\"\"Called when the user asks about other Lu siblings or the familia status.\n\n        Returns the current heartbeat showing which siblings are online and\n        how many unread messages each has.\n        \"\"\"\n        try:\n            heartbeat = await self.quilt.sibling_heartbeat()\n            return f\"Familia heartbeat: {heartbeat}\"\n        except Exception as e:\n            return f\"Couldn't check siblings: {e}\"\n\n    @function_tool\n    async def search_memory(self, context: RunContext, query: str):\n        \"\"\"Called when the user asks about past decisions, what was discussed,\n        or needs to recall something from organizational memory.\n\n        Args:\n            query: What to search for in organizational memory\n        \"\"\"\n        try:\n            results = await self.quilt.search_memory(query)\n            if results:\n                return f\"Found in memory: {results[:3]}\"  # Top 3\n            return \"Nothing found in memory for that query.\"\n        except Exception as e:\n            return f\"Memory search failed: {e}\"\n\n    @function_tool\n    async def send_message_to_sibling(\n        self, context: RunContext, to_sibling: str, message: str\n    ):\n        \"\"\"Called when the user wants to send a message to another Lu sibling.\n\n        Args:\n            to_sibling: Target sibling name (e.g., \"Cloud Lu\", \"AG Lu\", \"VS Lu\")\n            message: The message to send\n        \"\"\"\n        try:\n            await self.quilt.leave_message(\n                from_instance=self.identity[\"name\"],\n                to_instance=to_sibling,\n                message=message,\n            )\n            return f\"Message sent to {to_sibling}.\"\n        except Exception as e:\n            return f\"Failed to send message: {e}\"\n"
}

Audit Trail (3)

2d agodependents_unblockedfoundry_pipeline
2d agotask_completedTerminal Lu (Claude Opus)
2d agotask_claimedTerminal Lu
Task ID: a831b121-404b-4913-a8a6-c5d7b05c8b20