@@ -858,138 +858,3 @@ async def capture_and_persist_messages(
|
||||
"messages_created": len(response_messages),
|
||||
"run_ids": run_ids,
|
||||
}
|
||||
|
||||
|
||||
async def _backfill_agent_project_id(server, agent, actor, project_id: str):
|
||||
"""
|
||||
Temporary helper to backfill project_id for legacy agents.
|
||||
|
||||
TODO(@caren): Remove this function after all existing Claude Code agents have been backfilled.
|
||||
|
||||
Args:
|
||||
server: SyncServer instance
|
||||
agent: Agent to update
|
||||
actor: Actor performing the operation
|
||||
project_id: Project ID to set
|
||||
|
||||
Returns:
|
||||
Updated agent or original agent if update fails
|
||||
"""
|
||||
from letta.schemas.agent import UpdateAgent
|
||||
|
||||
try:
|
||||
updated_agent = await server.update_agent_async(
|
||||
agent_id=agent.id,
|
||||
request=UpdateAgent(project_id=project_id),
|
||||
actor=actor,
|
||||
)
|
||||
logger.info(f"[Backfill] Successfully updated agent {agent.id} with project_id {project_id}")
|
||||
return updated_agent
|
||||
except Exception as e:
|
||||
logger.warning(f"[Backfill] Failed to update agent project_id: {e}. Continuing with in-memory update.")
|
||||
# Fallback: continue with in-memory update
|
||||
agent.project_id = project_id
|
||||
return agent
|
||||
|
||||
|
||||
async def get_or_create_claude_code_agent(
|
||||
server,
|
||||
actor,
|
||||
project_id: Optional[str] = None,
|
||||
):
|
||||
"""
|
||||
Get or create a special agent for Claude Code sessions.
|
||||
|
||||
Args:
|
||||
server: SyncServer instance
|
||||
actor: Actor performing the operation (user ID)
|
||||
project_id: Optional project ID to associate the agent with
|
||||
|
||||
Returns:
|
||||
Agent ID
|
||||
"""
|
||||
from letta.schemas.agent import CreateAgent
|
||||
|
||||
# Create short user identifier from UUID (first 8 chars)
|
||||
if actor:
|
||||
user_short_id = str(actor.id)[:8] if hasattr(actor, "id") else str(actor)[:8]
|
||||
else:
|
||||
user_short_id = "default"
|
||||
|
||||
agent_name = f"claude-code-{user_short_id}"
|
||||
|
||||
try:
|
||||
# Try to find existing agent by name (most reliable)
|
||||
# Note: Search by name only, not tags, since name is unique and more reliable
|
||||
logger.debug(f"Searching for agent with name: {agent_name}")
|
||||
agents = await server.agent_manager.list_agents_async(
|
||||
actor=actor,
|
||||
limit=10, # Get a few in case of duplicates
|
||||
name=agent_name,
|
||||
include=["agent.blocks", "agent.managed_group", "agent.tags"],
|
||||
)
|
||||
|
||||
# list_agents_async returns a list directly, not an object with .agents
|
||||
logger.debug(f"Agent search returned {len(agents) if agents else 0} results")
|
||||
if agents and len(agents) > 0:
|
||||
# Return the first matching agent
|
||||
logger.info(f"Found existing Claude Code agent: {agents[0].id} (name: {agent_name})")
|
||||
agent = agents[0]
|
||||
|
||||
# Temporary patch: Fix project_id if it's missing (legacy bug)
|
||||
# TODO(@caren): Remove this after all existing Claude Code agents have been backfilled
|
||||
if not agent.project_id and project_id:
|
||||
logger.info(f"[Backfill] Agent {agent.id} missing project_id, backfilling with {project_id}")
|
||||
agent = await _backfill_agent_project_id(server, agent, actor, project_id)
|
||||
|
||||
return agent
|
||||
else:
|
||||
logger.debug(f"No existing agent found with name: {agent_name}")
|
||||
|
||||
except Exception as e:
|
||||
logger.warning(f"Could not find existing agent: {e}", exc_info=True)
|
||||
|
||||
# Create new agent
|
||||
try:
|
||||
logger.info(f"Creating new Claude Code agent: {agent_name} with project_id: {project_id}")
|
||||
|
||||
# Create minimal agent config
|
||||
agent_config = CreateAgent(
|
||||
name=agent_name,
|
||||
description="Agent for capturing Claude Code conversations",
|
||||
memory_blocks=[
|
||||
{
|
||||
"label": "human",
|
||||
"value": "This is my section of core memory devoted to information about the human.\nI don't yet know anything about them.\nWhat's their name? Where are they from? What do they do? Who are they?\nI should update this memory over time as I interact with the human and learn more about them.",
|
||||
"description": "A memory block for keeping track of the human (user) the agent is interacting with.",
|
||||
},
|
||||
{
|
||||
"label": "persona",
|
||||
"value": "This is my section of core memory devoted to information myself.\nThere's nothing here yet.\nI should update this memory over time as I develop my personality.",
|
||||
"description": "A memory block for storing the agent's core personality details and behavior profile.",
|
||||
},
|
||||
{
|
||||
"label": "project",
|
||||
"value": "This is my section of core memory devoted to information about what the agent is working on.\nI don't yet know anything about it.\nI should update this memory over time with high level understanding and learnings.",
|
||||
"description": "A memory block for storing the information about the project the agent is working on.",
|
||||
},
|
||||
],
|
||||
tags=["claude-code"],
|
||||
enable_sleeptime=True,
|
||||
agent_type="letta_v1_agent",
|
||||
model="anthropic/claude-sonnet-4-5-20250929",
|
||||
embedding="openai/text-embedding-ada-002",
|
||||
project_id=project_id,
|
||||
)
|
||||
|
||||
new_agent = await server.create_agent_async(
|
||||
request=agent_config,
|
||||
actor=actor,
|
||||
)
|
||||
|
||||
logger.info(f"Created Claude Code agent {new_agent.name}: {new_agent.id}")
|
||||
return new_agent
|
||||
|
||||
except Exception as e:
|
||||
logger.exception(f"Failed to create Claude Code agent: {e}")
|
||||
raise
|
||||
|
||||
Reference in New Issue
Block a user