From 1b27fa7a9b7a1ab2b9b34d172ef4af19c8e8e63e Mon Sep 17 00:00:00 2001 From: viettranx Date: Wed, 18 Mar 2026 16:38:03 +0700 Subject: [PATCH] feat(agents): inject team members into system prompt Team agents now see a ## Team Members section listing all teammates with agent_key, display_name, role, and frontmatter excerpt. This allows the agent to correctly assign tasks via team_tasks instead of guessing keys. --- internal/agent/loop_history.go | 9 ++++++++ internal/agent/systemprompt.go | 7 +++++++ internal/agent/systemprompt_sections.go | 28 +++++++++++++++++++++++++ 3 files changed, 44 insertions(+) diff --git a/internal/agent/loop_history.go b/internal/agent/loop_history.go index 10406c70..82fd963d 100644 --- a/internal/agent/loop_history.go +++ b/internal/agent/loop_history.go @@ -159,6 +159,14 @@ func (l *Loop) buildMessages(ctx context.Context, history []providers.Message, s mcpToolDescs = nil } + // Resolve team members so agent knows who to assign tasks to. + var teamMembers []store.TeamMemberData + if hasTeamTools && l.teamStore != nil && l.agentUUID != uuid.Nil { + if team, _ := l.teamStore.GetTeamForAgent(ctx, l.agentUUID); team != nil { + teamMembers, _ = l.teamStore.ListMembers(ctx, team.ID) + } + } + systemPrompt := BuildSystemPrompt(SystemPromptConfig{ AgentID: l.id, Model: l.model, @@ -174,6 +182,7 @@ func (l *Loop) buildMessages(ctx context.Context, history []providers.Message, s HasSpawn: l.tools != nil && hasSpawn, HasTeam: hasTeamTools, TeamWorkspace: tools.ToolTeamWorkspaceFromCtx(ctx), + TeamMembers: teamMembers, HasSkillSearch: hasSkillSearch, HasSkillManage: l.skillEvolve && hasSkillManage, HasMCPToolSearch: hasMCPToolSearch, diff --git a/internal/agent/systemprompt.go b/internal/agent/systemprompt.go index b12879f6..52f431f7 100644 --- a/internal/agent/systemprompt.go +++ b/internal/agent/systemprompt.go @@ -6,6 +6,7 @@ import ( "strings" "github.com/nextlevelbuilder/goclaw/internal/bootstrap" + "github.com/nextlevelbuilder/goclaw/internal/store" "github.com/nextlevelbuilder/goclaw/internal/tools" ) @@ -35,6 +36,7 @@ type SystemPromptConfig struct { HasSpawn bool // spawn tool available? HasTeam bool // agent belongs to a team? (skips generic spawn section) TeamWorkspace string // absolute path to team shared workspace (empty if not in team) + TeamMembers []store.TeamMemberData // team member roster for task assignment ContextFiles []bootstrap.ContextFile // bootstrap files for # Project Context ExtraPrompt string // extra system prompt (subagent context, etc.) AgentType string // "open" or "predefined" — affects context file framing @@ -225,6 +227,11 @@ func BuildSystemPrompt(cfg SystemPromptConfig) string { lines = append(lines, buildTeamWorkspaceSection(cfg.TeamWorkspace)...) } + // 6.4. ## Team Members — inject roster so agent knows who to assign tasks to + if !cfg.IsBootstrap && len(cfg.TeamMembers) > 0 { + lines = append(lines, buildTeamMembersSection(cfg.TeamMembers)...) + } + // 6.5 ## Sandbox (matching TS sandboxInfo section) — skip during bootstrap if !cfg.IsBootstrap && cfg.SandboxEnabled { lines = append(lines, buildSandboxSection(cfg)...) diff --git a/internal/agent/systemprompt_sections.go b/internal/agent/systemprompt_sections.go index fa8295b9..e7858554 100644 --- a/internal/agent/systemprompt_sections.go +++ b/internal/agent/systemprompt_sections.go @@ -8,6 +8,7 @@ import ( "time" "github.com/nextlevelbuilder/goclaw/internal/bootstrap" + "github.com/nextlevelbuilder/goclaw/internal/store" ) // mcpToolDescMaxLen is the max character length for MCP tool descriptions @@ -377,3 +378,30 @@ func buildTeamWorkspaceSection(teamWsPath string) []string { "", } } + +// buildTeamMembersSection lists team members so the agent knows who to assign tasks to. +func buildTeamMembersSection(members []store.TeamMemberData) []string { + lines := []string{ + "## Team Members", + "", + "Your team (use agent_key as assignee in team_tasks):", + } + for _, m := range members { + entry := fmt.Sprintf("- %s (%s) [%s]", m.AgentKey, m.DisplayName, m.Role) + if m.Frontmatter != "" { + fm := m.Frontmatter + if len([]rune(fm)) > 80 { + fm = string([]rune(fm)[:80]) + "…" + } + entry += " — " + fm + } + lines = append(lines, entry) + } + lines = append(lines, + "", + "When creating tasks with team_tasks, set assignee to the agent_key of the best-suited member.", + "Do NOT invent agent keys — only use the keys listed above.", + "", + ) + return lines +}