Skip to content
Open
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
40 changes: 10 additions & 30 deletions apps/desktop/src/main/acp/acp-smart-router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,48 +17,28 @@ type ACPAgentForDelegationPrompt = {
*/
export class ACPSmartRouter {
/**
* Generate system prompt text describing available agents.
* This text can be injected into the main AI's system prompt to inform it
* about delegation options.
* Format available ACP agents into compact delegation lines for a unified prompt.
*
* @param availableAgents - List of agents to include in the prompt
* @returns Formatted string for system prompt injection
* @returns Array of formatted lines for system prompt injection
*
* @example
* ```typescript
* const agents = acpRegistry.getReadyAgents()
* const promptAddition = acpSmartRouter.generateDelegationPromptAddition(agents)
* // Returns: "You have access to the following specialized agents..."
* const lines = acpSmartRouter.formatDelegationAgentLines(agents)
* // Returns: ["- **research-agent** (external): Web research and fact-finding", ...]
* ```
*/
generateDelegationPromptAddition(availableAgents: ReadonlyArray<ACPAgentForDelegationPrompt>): string {
formatDelegationAgentLines(availableAgents: ReadonlyArray<ACPAgentForDelegationPrompt>): string[] {
if (availableAgents.length === 0) {
return ''
return []
}

const agentDescriptions = availableAgents.map(agent => {
return availableAgents.map(agent => {
const def = agent.definition
return `- **${def.displayName || def.name}**: ${def.description || 'No description available'}`
}).join('\n')

return `
## Available Specialized Agents

You have access to the following specialized agents that can help with specific tasks.
Consider delegating work to these agents when appropriate:

${agentDescriptions}

### When to Delegate
- Use the **research** agent for information gathering, web searches, and fact-finding
- Use the **coding** agent for complex programming tasks, debugging, or code generation
- Use the **analysis** agent for data analysis, comparisons, and evaluations
- Use the **writing** agent for document creation, summarization, and content drafting

### How to Delegate
Use the delegate_to_agent tool with the agent name and a clear task description.
Monitor the agent's progress and incorporate its results into your response.
`.trim()
const summary = (def.description || def.displayName || 'External agent').trim()
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(def.description || def.displayName || 'External agent').trim() can become an empty string if the configured description/displayName is whitespace, resulting in a blank delegation entry. Consider applying the fallback after trimming so the prompt line always has a non-empty summary.

Severity: low

Other Locations
  • apps/desktop/src/main/system-prompts.ts:156

Fix This in Augment

🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage.

return `- **${def.name}** (external): ${summary}`
})
}
}

Expand Down
2 changes: 1 addition & 1 deletion apps/desktop/src/main/system-prompts.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { describe, it, expect, vi, beforeEach } from "vitest"
// Avoid pulling in real ACP/services (can have side effects / require Electron runtime)
vi.mock("./acp/acp-smart-router", () => ({
acpSmartRouter: {
generateDelegationPromptAddition: () => "",
formatDelegationAgentLines: () => [],
},
}))

Expand Down
103 changes: 35 additions & 68 deletions apps/desktop/src/main/system-prompts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,74 +128,50 @@ function formatLightweightToolInfo(
}

/**
* Generate ACP routing prompt addition based on available agents.
* Returns an empty string if no agents are ready.
* Generate a unified prompt addition for ACP agents, personas, and the internal agent.
* Returns an empty string only if there are no delegation entries at all.
*/
export function getACPRoutingPromptAddition(): string {
// Get agents from acpService which has runtime status
const agentStatuses = acpService.getAgents()
export function getAgentDelegationPromptAddition(): string {
const lines: string[] = []

// Filter to only ready agents
// ACP agents (external)
const agentStatuses = acpService.getAgents()
const readyAgents = agentStatuses.filter(a => a.status === 'ready')

if (readyAgents.length === 0) {
return ''
if (readyAgents.length > 0) {
const formattedAgents = readyAgents.map(a => ({
definition: {
name: a.config.name,
displayName: a.config.displayName,
description: a.config.description || '',
},
}))
lines.push(...acpSmartRouter.formatDelegationAgentLines(formattedAgents))
}

// Format agents for the smart router
const formattedAgents = readyAgents.map(a => ({
definition: {
name: a.config.name,
displayName: a.config.displayName,
description: a.config.description || '',
},
status: 'ready' as const,
activeRuns: 0,
}))

return acpSmartRouter.generateDelegationPromptAddition(formattedAgents)
}

/**
* Generate prompt addition for the internal agent.
* This instructs the agent on when and how to use the internal agent for parallel work.
*/
export function getSubSessionPromptAddition(): string {
const info = getInternalAgentInfo()

return `
INTERNAL AGENT: Use \`delegate_to_agent\` with \`agentName: "internal"\` to spawn parallel sub-agents. Batch multiple calls for efficiency.
- USE FOR: Independent parallel tasks (analyzing multiple files, researching different topics, divide-and-conquer)
- AVOID FOR: Sequential dependencies, shared state/file conflicts, simple tasks
- LIMITS: Max depth ${info.maxRecursionDepth}, max ${info.maxConcurrent} concurrent per parent
`.trim()
}

/**
* Generate prompt addition for available agent personas (delegation-targets).
* These are internal personas that can be delegated to via delegate_to_agent.
* Similar format to tools/skills for easy discoverability.
*/
export function getAgentPersonasPromptAddition(): string {
// Get enabled delegation-target profiles
// Personas (delegation-targets)
const delegationTargets = agentProfileService.getByRole('delegation-target')
.filter(p => p.enabled)
if (delegationTargets.length > 0) {
const personaLines = delegationTargets.map(p => {
const summary = (p.description || p.displayName || 'General tasks').trim()
return `- **${p.name}** (persona): ${summary}`
})
lines.push(...personaLines)
}

if (delegationTargets.length === 0) {
// Internal agent (always available in agent mode)
const info = getInternalAgentInfo()
lines.push(`- **internal**: Spawn parallel sub-agents (max depth ${info.maxRecursionDepth}, max ${info.maxConcurrent} concurrent)`)

if (lines.length === 0) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because the internal agent line is always pushed (line 164), the if (lines.length === 0) branch is unreachable and the docstring about returning an empty string is currently misleading. If the intent was to sometimes omit the entire block, this logic will never do that in agent mode.

Severity: low

Fix This in Augment

🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage.

return ''
}

// Format personas in a compact, discoverable format similar to tools/skills
const personasList = delegationTargets.map(p => {
return `- **${p.name}**: ${p.description || p.displayName || 'No description'}`
}).join('\n')

return `
AVAILABLE AGENT PERSONAS (${delegationTargets.length}):
${personasList}
AVAILABLE AGENTS (delegate via delegate_to_agent):
${lines.join('\n')}

To delegate: \`delegate_to_agent(agentName: "persona_name", task: "...")\`
When user mentions a persona by name (e.g., "ask joker...", "have coder..."), delegate to that persona.
Delegate when user mentions an agent by name or task matches agent specialty.
`.trim()
}

Expand All @@ -222,20 +198,11 @@ export function constructSystemPrompt(
if (isAgentMode) {
prompt += AGENT_MODE_ADDITIONS

// Add ACP agent delegation information if agents are available
const acpPromptAddition = getACPRoutingPromptAddition()
if (acpPromptAddition) {
prompt += '\n\n' + acpPromptAddition
// Add unified agent delegation block (ACP agents, personas, internal agent)
const delegationAddition = getAgentDelegationPromptAddition()
if (delegationAddition) {
prompt += '\n\n' + delegationAddition
}

// Add agent personas (delegation-targets) in a discoverable format
const personasAddition = getAgentPersonasPromptAddition()
if (personasAddition) {
prompt += '\n\n' + personasAddition
}

// Add internal sub-session instructions (always available in agent mode)
prompt += '\n\n' + getSubSessionPromptAddition()
}

// Add agent skills instructions if provided
Expand Down