Files
goclaw/pkg/protocol/team_events.go
T
Viet TranandGitHub 84650c5c14 feat(teams): attachments refactor, semantic search, improved prompting (#310)
* feat(teams): refactor attachments, remove team_message, add task comments UI

Major team system refactoring:

- Drop team_workspace_files, team_workspace_file_versions, team_workspace_comments,
  team_messages tables; replace team_task_attachments with path-based schema
- Add denormalized comment_count/attachment_count on team_tasks for dashboard perf
- Auto-track file writes as task attachments via WorkspaceInterceptor
- Remove team_message tool entirely (tool, store, i18n, builtin_tools, MCP bridge)
- Members communicate via task comments; approve/reject use comments for audit trail
- Add commented/new_task notification types to TeamNotifyConfig
- Enrich task completion announce with member comments
- User-created tasks stay pending (backlog) — no auto-assign to leader
- Configurable member request tasks (member_requests.enabled in team settings)
- Structured task description template in TEAM.md for v2 leads
- HTTP attachment download endpoint with IDOR + path traversal protection
- Web UI: count badges on task list, comments section with input, download button
- Team settings UI: completed/commented/new_task toggles, member requests section

* feat(teams): priority dispatch, compact prompting, realtime comments

- Priority dispatch: DispatchUnblockedTasks dispatches only 1 task per
  owner per round (highest priority first). Fixes cancel bug where
  CancelSession killed innocent queued tasks.
- Prompt rework: Replace verbose Task Decomposition (25 lines) with
  compact Task Planning (8 lines). Add explicit UUID warning and
  sequencing guidance for weak models (Qwen, MiniMax).
- Recent comments in dispatch: buildRecentCommentsSummary appends 3
  most recent comments to re-dispatched tasks (reject, retry, stale).
- Enrich comment event payload with TaskNumber, Subject, CommentText
  (truncated 500 runes, UTF-8 safe).
- UI: Board subscribes to TEAM_TASK_COMMENTED for realtime comment_count
  badge updates. Task detail dialog auto-refreshes comments on event.
- Tool description hint: guide models to write self-contained task
  descriptions with clear objectives and context.

* perf(teams): add ListRecentTaskComments with SQL LIMIT

Dispatch only needs 3 most recent comments — avoid fetching all.
New ListRecentTaskComments(ctx, taskID, limit) uses ORDER BY DESC
LIMIT N then reverses to chronological order.

* feat(teams): add subject embedding for semantic task search + improve prompting

- Add vector(1536) embedding column to team_tasks with HNSW index
- Implement hybrid search: FTS (0.3) + cosine similarity (0.7) with graceful fallback
- Auto-generate embeddings on task create/update, backfill existing tasks on startup
- Wire embedding provider into PGTeamStore via gateway_setup
- Change FTS from OR to AND with prefix matching for precise keyword search
- Reduce search page size from 30 to 5 to save tokens
- Rename migration 000023 → 000024, bump RequiredSchemaVersion to 24
- Update TEAM.md hints: prefer search over list, batch task creation with blocked_by
- Add anti-pattern examples to prevent sequential task creation
2026-03-20 17:51:40 +07:00

205 lines
8.8 KiB
Go

package protocol
// DelegationEventPayload is the typed payload for delegation lifecycle events
// (started, completed, failed, cancelled).
type DelegationEventPayload struct {
DelegationID string `json:"delegation_id"`
SourceAgentID string `json:"source_agent_id"`
SourceAgentKey string `json:"source_agent_key"`
SourceDisplayName string `json:"source_display_name,omitempty"`
TargetAgentID string `json:"target_agent_id"`
TargetAgentKey string `json:"target_agent_key"`
TargetDisplayName string `json:"target_display_name,omitempty"`
UserID string `json:"user_id"`
Channel string `json:"channel"`
ChatID string `json:"chat_id"`
Mode string `json:"mode"`
Task string `json:"task,omitempty"`
TeamID string `json:"team_id,omitempty"`
TeamTaskID string `json:"team_task_id,omitempty"`
Status string `json:"status,omitempty"`
ElapsedMS int `json:"elapsed_ms,omitempty"`
Error string `json:"error,omitempty"`
CreatedAt string `json:"created_at"`
}
// DelegationProgressItem describes one active delegation in a progress report.
type DelegationProgressItem struct {
DelegationID string `json:"delegation_id"`
TargetAgentKey string `json:"target_agent_key"`
TargetDisplayName string `json:"target_display_name,omitempty"`
ElapsedMS int `json:"elapsed_ms"`
TeamTaskID string `json:"team_task_id,omitempty"`
Activity string `json:"activity,omitempty"` // "thinking", "tool_exec", "compacting"
Tool string `json:"tool,omitempty"` // current tool name (when Activity == "tool_exec")
}
// DelegationProgressPayload is emitted periodically for async delegations.
type DelegationProgressPayload struct {
SourceAgentID string `json:"source_agent_id"`
SourceAgentKey string `json:"source_agent_key"`
UserID string `json:"user_id"`
Channel string `json:"channel"`
ChatID string `json:"chat_id"`
TeamID string `json:"team_id,omitempty"`
Active []DelegationProgressItem `json:"active_delegations"`
}
// DelegationAccumulatedPayload is emitted when an async delegation completes
// but siblings are still running (result accumulated, not yet announced).
type DelegationAccumulatedPayload struct {
DelegationID string `json:"delegation_id"`
SourceAgentID string `json:"source_agent_id"`
SourceAgentKey string `json:"source_agent_key"`
TargetAgentKey string `json:"target_agent_key"`
TargetDisplayName string `json:"target_display_name,omitempty"`
UserID string `json:"user_id"`
Channel string `json:"channel"`
ChatID string `json:"chat_id"`
TeamID string `json:"team_id,omitempty"`
TeamTaskID string `json:"team_task_id,omitempty"`
SiblingsRemaining int `json:"siblings_remaining"`
ElapsedMS int `json:"elapsed_ms,omitempty"`
}
// DelegationAnnounceResultSummary describes one delegation result in the announce payload.
type DelegationAnnounceResultSummary struct {
AgentKey string `json:"agent_key"`
DisplayName string `json:"display_name,omitempty"`
HasMedia bool `json:"has_media"`
ContentPreview string `json:"content_preview,omitempty"`
}
// DelegationAnnouncePayload is emitted when the last sibling delegation completes
// and all accumulated results are announced back to the lead agent.
type DelegationAnnouncePayload struct {
SourceAgentID string `json:"source_agent_id"`
SourceAgentKey string `json:"source_agent_key"`
SourceDisplayName string `json:"source_display_name,omitempty"`
UserID string `json:"user_id"`
Channel string `json:"channel"`
ChatID string `json:"chat_id"`
TeamID string `json:"team_id,omitempty"`
Results []DelegationAnnounceResultSummary `json:"results"`
CompletedTaskIDs []string `json:"completed_task_ids,omitempty"`
TotalElapsedMS int `json:"total_elapsed_ms"`
HasMedia bool `json:"has_media"`
}
// TeamTaskEventPayload is the typed payload for team task lifecycle events
// (created, claimed, completed, cancelled, approved, rejected).
type TeamTaskEventPayload struct {
TeamID string `json:"team_id"`
TaskID string `json:"task_id"`
TaskNumber int `json:"task_number,omitempty"`
Subject string `json:"subject,omitempty"`
Status string `json:"status"`
OwnerAgentKey string `json:"owner_agent_key,omitempty"`
OwnerDisplayName string `json:"owner_display_name,omitempty"`
Reason string `json:"reason,omitempty"`
UserID string `json:"user_id"`
Channel string `json:"channel"`
ChatID string `json:"chat_id"`
Timestamp string `json:"timestamp"`
// Comment text preview (for team.task.commented events, truncated).
CommentText string `json:"comment_text,omitempty"`
// Progress (for team.task.progress events).
ProgressPercent int `json:"progress_percent,omitempty"`
ProgressStep string `json:"progress_step,omitempty"`
// Actor info for audit trail (recorded to team_task_events by subscriber).
ActorType string `json:"actor_type,omitempty"` // "agent", "human", "system"
ActorID string `json:"actor_id,omitempty"` // agent key, user ID, or system identifier
}
// TeamMessageEventPayload is the typed payload for team.message.sent events.
type TeamMessageEventPayload struct {
TeamID string `json:"team_id"`
FromAgentKey string `json:"from_agent_key"`
FromDisplayName string `json:"from_display_name,omitempty"`
ToAgentKey string `json:"to_agent_key"`
ToDisplayName string `json:"to_display_name,omitempty"`
MessageType string `json:"message_type"`
Preview string `json:"preview"`
TaskID string `json:"task_id,omitempty"`
UserID string `json:"user_id"`
Channel string `json:"channel"`
ChatID string `json:"chat_id"`
}
// --- Team CRUD event payloads ---
// TeamCreatedPayload is emitted when a new team is created via RPC.
type TeamCreatedPayload struct {
TeamID string `json:"team_id"`
TeamName string `json:"team_name"`
LeadAgentKey string `json:"lead_agent_key"`
LeadDisplayName string `json:"lead_display_name,omitempty"`
MemberCount int `json:"member_count"`
}
// TeamUpdatedPayload is emitted when team settings are updated via RPC.
type TeamUpdatedPayload struct {
TeamID string `json:"team_id"`
TeamName string `json:"team_name"`
Changes []string `json:"changes"`
}
// TeamDeletedPayload is emitted when a team is deleted via RPC.
type TeamDeletedPayload struct {
TeamID string `json:"team_id"`
TeamName string `json:"team_name"`
}
// TeamMemberAddedPayload is emitted when a member is added to a team.
type TeamMemberAddedPayload struct {
TeamID string `json:"team_id"`
TeamName string `json:"team_name"`
AgentID string `json:"agent_id"`
AgentKey string `json:"agent_key"`
DisplayName string `json:"display_name,omitempty"`
Role string `json:"role"`
}
// TeamMemberRemovedPayload is emitted when a member is removed from a team.
type TeamMemberRemovedPayload struct {
TeamID string `json:"team_id"`
TeamName string `json:"team_name"`
AgentID string `json:"agent_id"`
AgentKey string `json:"agent_key"`
DisplayName string `json:"display_name,omitempty"`
}
// --- Agent link event payloads ---
// AgentLinkCreatedPayload is emitted when a new agent link is created.
type AgentLinkCreatedPayload struct {
LinkID string `json:"link_id"`
SourceAgentID string `json:"source_agent_id"`
SourceAgentKey string `json:"source_agent_key"`
TargetAgentID string `json:"target_agent_id"`
TargetAgentKey string `json:"target_agent_key"`
Direction string `json:"direction"`
TeamID string `json:"team_id,omitempty"`
Status string `json:"status"`
}
// AgentLinkUpdatedPayload is emitted when an agent link is updated.
type AgentLinkUpdatedPayload struct {
LinkID string `json:"link_id"`
SourceAgentKey string `json:"source_agent_key"`
TargetAgentKey string `json:"target_agent_key"`
Direction string `json:"direction,omitempty"`
Status string `json:"status,omitempty"`
Changes []string `json:"changes"`
}
// AgentLinkDeletedPayload is emitted when an agent link is deleted.
type AgentLinkDeletedPayload struct {
LinkID string `json:"link_id"`
SourceAgentKey string `json:"source_agent_key"`
TargetAgentKey string `json:"target_agent_key"`
}