Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion components/ambient-mcp/server.go
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ func registerSessionTools(s *server.MCPServer, c *client.Client, transport strin
mcp.WithString("agent_id", mcp.Description("Agent ID to execute the session.")),
mcp.WithString("model", mcp.Description("LLM model override (e.g. 'claude-sonnet-4-6').")),
mcp.WithString("parent_session_id", mcp.Description("Calling session ID for agent-to-agent delegation.")),
mcp.WithString("name", mcp.Description("Human-readable session name.")),
mcp.WithString("name", mcp.Description("Human-readable session name (max 50 chars). Provide a concise, descriptive title, e.g. 'Debug auth middleware'. Auto-generated from prompt if omitted.")),
),
tools.CreateSession(c),
)
Expand Down
57 changes: 43 additions & 14 deletions components/backend/handlers/display_name.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,29 @@ func sanitizeDisplayName(name string) string {
return name
}

// generateFallbackDisplayName creates a display name from the user's message
// when AI generation is unavailable. Extracts the first sentence or truncates.
func generateFallbackDisplayName(userMessage string) string {
msg := strings.TrimSpace(userMessage)
if msg == "" {
return ""
}

// Find first sentence boundary
for _, sep := range []string{". ", "! ", "? ", "\n"} {
if idx := strings.Index(msg, sep); idx > 0 && idx <= maxDisplayNameLength {
msg = msg[:idx]
break
}
}

runes := []rune(msg)
if len(runes) > maxDisplayNameLength {
msg = string(runes[:maxDisplayNameLength-3]) + "..."
}
return sanitizeDisplayName(msg)
}

// ValidateDisplayName validates a display name for the HTTP handler
// Returns an error message if invalid, empty string if valid
func ValidateDisplayName(name string) string {
Expand Down Expand Up @@ -99,26 +122,32 @@ func generateAndUpdateDisplayName(projectName, sessionName, userMessage string,
defer cancel()

// Get Anthropic client (Vertex or API key)
var displayName string
client, isVertex, err := getAnthropicClient(ctx, projectName)
if err != nil {
return fmt.Errorf("failed to get Anthropic client: %w", err)
}

// Build prompt with context
prompt := buildDisplayNamePrompt(userMessage, sessionCtx)

// Call Claude Haiku with appropriate model name
modelName := haiku3Model
if isVertex {
modelName = haiku3ModelVertex
}
displayName, err := callClaudeForDisplayName(ctx, client, prompt, modelName)
if err != nil {
return fmt.Errorf("failed to call Claude: %w", err)
log.Printf("DisplayNameGen: Client init failed for %s/%s, using fallback: %v", projectName, sessionName, err)
displayName = generateFallbackDisplayName(userMessage)
} else {
// Build prompt with context
prompt := buildDisplayNamePrompt(userMessage, sessionCtx)

// Call Claude Haiku with appropriate model name
modelName := haiku3Model
if isVertex {
modelName = haiku3ModelVertex
}
displayName, err = callClaudeForDisplayName(ctx, client, prompt, modelName)
if err != nil {
log.Printf("DisplayNameGen: AI generation failed for %s/%s, using fallback: %v", projectName, sessionName, err)
displayName = generateFallbackDisplayName(userMessage)
}
}

// Sanitize and validate display name
displayName = sanitizeDisplayName(displayName)
if displayName == "" {
return fmt.Errorf("both AI and fallback generation produced empty display name")
}

// Truncate if too long (using runes for proper Unicode handling)
if utf8.RuneCountInString(displayName) > maxDisplayNameLength {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -496,6 +496,10 @@ export function SessionsSidebar({
<span>{formatDistanceToNow(new Date(activityTime), { addSuffix: true })}</span>
</div>
)}
<div className="flex items-center gap-1.5 text-xs text-muted-foreground">
<User className="h-3 w-3" />
<span>{session.spec.userContext?.displayName || session.spec.userContext?.userId || '—'}</span>
</div>
{session.spec.initialPrompt && (
<div className="flex items-start gap-1.5 text-xs text-muted-foreground pt-1">
<MessageSquare className="h-3 w-3 mt-0.5 shrink-0" />
Expand Down
2 changes: 1 addition & 1 deletion components/runners/ambient-runner/ambient_runner/bridges/claude/backend_tools.py
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ async def acp_get_session(args: dict) -> dict:
},
"display_name": {
"type": "string",
"description": "Human-readable display name",
"description": "Human-readable display name for the session (max 50 chars). Provide a concise, descriptive title based on the task, e.g. 'Debug auth middleware', 'Add user dashboard'. Auto-generated from initial_prompt if omitted.",
},
"repos": {
"type": "string",
Expand Down