feat: Deny team_message tool for team leads and update string truncation to handle Unicode characters.

This commit is contained in:
viettranx
2026-03-05 19:24:35 +07:00
parent 8862842b46
commit c8cd63dbe8
4 changed files with 33 additions and 7 deletions
+9 -1
View File
@@ -160,6 +160,7 @@ func NewManagedResolver(deps ResolverDeps) ResolverFunc {
// Inject TEAM.md for all team members (lead + members) so every agent
// knows the team workflow: create/claim/complete tasks via team_tasks tool.
hasTeam := false
isTeamLead := false
if deps.TeamStore != nil {
hasTeamMD := false
for _, cf := range contextFiles {
@@ -176,6 +177,13 @@ func NewManagedResolver(deps ResolverDeps) ResolverFunc {
Path: bootstrap.TeamFile,
Content: buildTeamMD(team, members, ag.ID),
})
// Detect lead role for tool policy
for _, m := range members {
if m.AgentID == ag.ID && m.Role == store.TeamRoleLead {
isTeamLead = true
break
}
}
}
}
} else {
@@ -325,7 +333,7 @@ func NewManagedResolver(deps ResolverDeps) ResolverFunc {
Sessions: deps.Sessions,
Tools: toolsReg,
ToolPolicy: deps.ToolPolicy,
AgentToolPolicy: agentToolPolicyWithMCP(ag.ParseToolsConfig(), hasMCPTools),
AgentToolPolicy: agentToolPolicyForTeam(agentToolPolicyWithMCP(ag.ParseToolsConfig(), hasMCPTools), isTeamLead),
SkillsLoader: deps.Skills,
SkillAllowList: skillAllowList,
HasMemory: hasMemory,
+20 -2
View File
@@ -145,7 +145,6 @@ func buildTeamMD(team *store.TeamData, members []store.TeamMemberData, selfID uu
sb.WriteString("- action=search, query=<text> → search tasks by subject/description\n")
sb.WriteString("- action=complete, task_id=<id>, result=<summary> → manually complete a task\n")
sb.WriteString("- action=cancel, task_id=<id>, reason=<why> → cancel a pending task that is no longer needed\n\n")
sb.WriteString("Use `team_message` to send updates to team members.\n\n")
sb.WriteString("For simple questions about team composition, answer directly from the member list above.\n")
} else {
sb.WriteString("As a member, when you receive a delegated task, just do the work.\n")
@@ -156,13 +155,32 @@ func buildTeamMD(team *store.TeamData, members []store.TeamMemberData, selfID uu
sb.WriteString("- action=list → check team task board (active tasks)\n")
sb.WriteString("- action=get, task_id=<id> → read a completed task's full result\n")
sb.WriteString("- action=search, query=<text> → search tasks\n\n")
sb.WriteString("Use `team_message` to send updates to your team lead.\n\n")
sb.WriteString("Use `team_message` to send progress updates to your team lead (one-way, no response expected).\n\n")
sb.WriteString("For simple questions about team composition, answer directly from the member list above.\n")
}
return sb.String()
}
// agentToolPolicyForTeam denies team_message for team leads.
// Leads should use spawn (which auto-announces results back) instead of team_message
// (one-way notification that leaks raw responses to the output channel).
func agentToolPolicyForTeam(policy *config.ToolPolicySpec, isLead bool) *config.ToolPolicySpec {
if !isLead {
return policy
}
if policy == nil {
policy = &config.ToolPolicySpec{}
}
for _, d := range policy.Deny {
if d == "team_message" {
return policy
}
}
policy.Deny = append(policy.Deny, "team_message")
return policy
}
// agentToolPolicyWithMCP injects "group:mcp" into the agent's alsoAllow list
// when MCP tools are loaded, ensuring the PolicyEngine doesn't block them.
func agentToolPolicyWithMCP(policy *config.ToolPolicySpec, hasMCP bool) *config.ToolPolicySpec {
+2 -2
View File
@@ -166,8 +166,8 @@ func (dm *DelegateManager) DelegateAsync(ctx context.Context, opts DelegateOpts)
var announceSummaries []protocol.DelegationAnnounceResultSummary
for _, r := range artifacts.Results {
preview := r.Content
if len(preview) > 200 {
preview = preview[:200] + "..."
if runes := []rune(preview); len(runes) > 200 {
preview = string(runes[:200]) + "..."
}
announceSummaries = append(announceSummaries, protocol.DelegationAnnounceResultSummary{
AgentKey: r.AgentKey,
+2 -2
View File
@@ -100,8 +100,8 @@ func formatDelegateAnnounce(task *DelegationTask, artifacts *DelegateArtifacts,
if len(r.Deliverables) > 0 {
for _, d := range r.Deliverables {
preview := d
if len(preview) > 4000 {
preview = preview[:4000] + "\n[...truncated, full content in team_tasks]"
if runes := []rune(preview); len(runes) > 4000 {
preview = string(runes[:4000]) + "\n[...truncated, full content in team_tasks]"
}
msg += fmt.Sprintf("\n[Deliverable]\n%s\n", preview)
}