diff --git a/internal/agent/loop.go b/internal/agent/loop.go index 40a0383c..be45a14e 100644 --- a/internal/agent/loop.go +++ b/internal/agent/loop.go @@ -542,7 +542,7 @@ func (l *Loop) runLoop(ctx context.Context, req RunRequest) (*RunResult, error) // so only truly un-spawned tasks remain pending. if l.teamStore != nil && l.agentUUID != uuid.Nil { if team, _ := l.teamStore.GetTeamForAgent(ctx, l.agentUUID); team != nil && team.LeadAgentID == l.agentUUID { - if tasks, err := l.teamStore.ListTasks(ctx, team.ID, "newest", "", ""); err == nil { + if tasks, err := l.teamStore.ListTasks(ctx, team.ID, "newest", "", req.UserID); err == nil { var stale []string for _, t := range tasks { if t.Status == store.TeamTaskStatusPending { diff --git a/internal/channels/telegram/commands.go b/internal/channels/telegram/commands.go index 86b4b7f7..4cbb3aa2 100644 --- a/internal/channels/telegram/commands.go +++ b/internal/channels/telegram/commands.go @@ -190,11 +190,11 @@ func (c *Channel) handleBotCommand(ctx context.Context, message *telego.Message, return true case "/tasks": - c.handleTasksList(ctx, chatID, setThread) + c.handleTasksList(ctx, chatID, isGroup, setThread) return true case "/task_detail": - c.handleTaskDetail(ctx, chatID, text, setThread) + c.handleTaskDetail(ctx, chatID, text, isGroup, setThread) return true case "/addwriter": diff --git a/internal/channels/telegram/commands_tasks.go b/internal/channels/telegram/commands_tasks.go index f4254cbb..5bd33692 100644 --- a/internal/channels/telegram/commands_tasks.go +++ b/internal/channels/telegram/commands_tasks.go @@ -39,8 +39,17 @@ func truncateStr(s string, maxLen int) string { return string(runes[:maxLen]) + "…" } +// taskUserID composes the scoped user ID for task filtering. +// Groups use "group:{channel}:{chatID}", DMs use the chat ID directly. +func taskUserID(channelName string, chatID int64, isGroup bool) string { + if isGroup { + return fmt.Sprintf("group:%s:%d", channelName, chatID) + } + return fmt.Sprintf("%d", chatID) +} + // handleTasksList handles the /tasks command — lists team tasks. -func (c *Channel) handleTasksList(ctx context.Context, chatID int64, setThread func(*telego.SendMessageParams)) { +func (c *Channel) handleTasksList(ctx context.Context, chatID int64, isGroup bool, setThread func(*telego.SendMessageParams)) { chatIDObj := tu.ID(chatID) send := func(text string) { @@ -72,7 +81,7 @@ func (c *Channel) handleTasksList(ctx context.Context, chatID int64, setThread f return } - tasks, err := c.teamStore.ListTasks(ctx, team.ID, "newest", store.TeamTaskFilterAll, "") + tasks, err := c.teamStore.ListTasks(ctx, team.ID, "newest", store.TeamTaskFilterAll, taskUserID(c.Name(), chatID, isGroup)) if err != nil { slog.Warn("tasks command: ListTasks failed", "error", err) send("Failed to list tasks. Please try again.") @@ -122,7 +131,7 @@ func (c *Channel) handleTasksList(ctx context.Context, chatID int64, setThread f } // handleTaskDetail handles the /task_detail command — shows detail for a task. -func (c *Channel) handleTaskDetail(ctx context.Context, chatID int64, text string, setThread func(*telego.SendMessageParams)) { +func (c *Channel) handleTaskDetail(ctx context.Context, chatID int64, text string, isGroup bool, setThread func(*telego.SendMessageParams)) { chatIDObj := tu.ID(chatID) send := func(t string) { @@ -164,7 +173,7 @@ func (c *Channel) handleTaskDetail(ctx context.Context, chatID int64, text strin return } - tasks, err := c.teamStore.ListTasks(ctx, team.ID, "newest", store.TeamTaskFilterAll, "") + tasks, err := c.teamStore.ListTasks(ctx, team.ID, "newest", store.TeamTaskFilterAll, taskUserID(c.Name(), chatID, isGroup)) if err != nil { slog.Warn("task_detail command: ListTasks failed", "error", err) send("Failed to list tasks. Please try again.") @@ -197,9 +206,11 @@ func (c *Channel) handleCallbackQuery(ctx context.Context, query *telego.Callbac taskIDStr := strings.TrimPrefix(query.Data, "td:") - // Resolve chat ID from the callback's message. - chatID := query.Message.GetChat().ID + // Resolve chat ID and group status from the callback's message. + chat := query.Message.GetChat() + chatID := chat.ID chatIDObj := tu.ID(chatID) + isGroup := chat.Type == "group" || chat.Type == "supergroup" send := func(text string) { for _, chunk := range chunkPlainText(text, telegramMaxMessageLen) { @@ -225,7 +236,7 @@ func (c *Channel) handleCallbackQuery(ctx context.Context, query *telego.Callbac return } - tasks, err := c.teamStore.ListTasks(ctx, team.ID, "newest", store.TeamTaskFilterAll, "") + tasks, err := c.teamStore.ListTasks(ctx, team.ID, "newest", store.TeamTaskFilterAll, taskUserID(c.Name(), chatID, isGroup)) if err != nil { send("Failed to list tasks.") return diff --git a/internal/tools/delegate.go b/internal/tools/delegate.go index 3347b3eb..aa230c9a 100644 --- a/internal/tools/delegate.go +++ b/internal/tools/delegate.go @@ -503,7 +503,7 @@ func buildDelegateMessage(opts DelegateOpts) string { // pendingTasksHint returns a formatted hint listing pending team tasks, // so the LLM can self-correct a hallucinated team_task_id. func (dm *DelegateManager) pendingTasksHint(ctx context.Context, teamID uuid.UUID) string { - tasks, err := dm.teamStore.ListTasks(ctx, teamID, "newest", "", "") + tasks, err := dm.teamStore.ListTasks(ctx, teamID, "newest", "", store.UserIDFromContext(ctx)) if err != nil { return "" }