diff --git a/pkg/tools/sessions_tool.go b/pkg/tools/sessions_tool.go index 28ef419..4103776 100644 --- a/pkg/tools/sessions_tool.go +++ b/pkg/tools/sessions_tool.go @@ -164,10 +164,18 @@ func (t *SessionsTool) Execute(ctx context.Context, args map[string]interface{}) if key == "" { return "key is required for history", nil } - h := t.historyFn(key, 0) - if len(h) == 0 { + raw := t.historyFn(key, 0) + if len(raw) == 0 { return "No history.", nil } + type indexedMsg struct { + idx int + msg providers.Message + } + window := make([]indexedMsg, 0, len(raw)) + for i, m := range raw { + window = append(window, indexedMsg{idx: i + 1, msg: m}) + } // Window selectors are 1-indexed (human-friendly) if around > 0 { @@ -175,8 +183,8 @@ func (t *SessionsTool) Execute(ctx context.Context, args map[string]interface{}) if center < 0 { center = 0 } - if center >= len(h) { - center = len(h) - 1 + if center >= len(window) { + center = len(window) - 1 } half := limit / 2 if half < 1 { @@ -187,17 +195,17 @@ func (t *SessionsTool) Execute(ctx context.Context, args map[string]interface{}) start = 0 } end := center + half + 1 - if end > len(h) { - end = len(h) + if end > len(window) { + end = len(window) } - h = h[start:end] + window = window[start:end] } else { start := 0 - end := len(h) + end := len(window) if after > 0 { start = after - if start > len(h) { - start = len(h) + if start > len(window) { + start = len(window) } } if before > 0 { @@ -205,72 +213,72 @@ func (t *SessionsTool) Execute(ctx context.Context, args map[string]interface{}) if end < 0 { end = 0 } - if end > len(h) { - end = len(h) + if end > len(window) { + end = len(window) } } if start > end { start = end } - h = h[start:end] + window = window[start:end] } if !includeTools { - filtered := make([]providers.Message, 0, len(h)) - for _, m := range h { - if strings.TrimSpace(strings.ToLower(m.Role)) == "tool" { + filtered := make([]indexedMsg, 0, len(window)) + for _, m := range window { + if strings.TrimSpace(strings.ToLower(m.msg.Role)) == "tool" { continue } filtered = append(filtered, m) } - h = filtered + window = filtered } if roleFilter != "" { - filtered := make([]providers.Message, 0, len(h)) - for _, m := range h { - if strings.ToLower(strings.TrimSpace(m.Role)) == roleFilter { + filtered := make([]indexedMsg, 0, len(window)) + for _, m := range window { + if strings.ToLower(strings.TrimSpace(m.msg.Role)) == roleFilter { filtered = append(filtered, m) } } - h = filtered + window = filtered } if fromMeSet { targetRole := "user" if fromMe { targetRole = "assistant" } - filtered := make([]providers.Message, 0, len(h)) - for _, m := range h { - if strings.ToLower(strings.TrimSpace(m.Role)) == targetRole { + filtered := make([]indexedMsg, 0, len(window)) + for _, m := range window { + if strings.ToLower(strings.TrimSpace(m.msg.Role)) == targetRole { filtered = append(filtered, m) } } - h = filtered + window = filtered } if query != "" { - filtered := make([]providers.Message, 0, len(h)) - for _, m := range h { - blob := strings.ToLower(strings.TrimSpace(m.Role + "\n" + m.Content)) + filtered := make([]indexedMsg, 0, len(window)) + for _, m := range window { + blob := strings.ToLower(strings.TrimSpace(m.msg.Role + "\n" + m.msg.Content)) if strings.Contains(blob, query) { filtered = append(filtered, m) } } - h = filtered + window = filtered } - if len(h) == 0 { + if len(window) == 0 { return "No history (after filters).", nil } - if len(h) > limit { - h = h[len(h)-limit:] + if len(window) > limit { + window = window[len(window)-limit:] } var sb strings.Builder sb.WriteString(fmt.Sprintf("History for %s:\n", key)) - for _, m := range h { - content := strings.TrimSpace(m.Content) + for _, item := range window { + content := strings.TrimSpace(item.msg.Content) if len(content) > 180 { content = content[:180] + "..." } - sb.WriteString(fmt.Sprintf("- [%s] %s\n", m.Role, content)) + sb.WriteString(fmt.Sprintf("- [#%d][%s] %s\n", item.idx, item.msg.Role, content)) } return strings.TrimSpace(sb.String()), nil default: diff --git a/pkg/tools/subagent.go b/pkg/tools/subagent.go index bc401e4..6654415 100644 --- a/pkg/tools/subagent.go +++ b/pkg/tools/subagent.go @@ -253,6 +253,29 @@ func (sm *SubagentManager) SteerTask(taskID, message string) bool { return true } +func (sm *SubagentManager) ResumeTask(ctx context.Context, taskID string) (string, bool) { + sm.mu.RLock() + t, ok := sm.tasks[taskID] + sm.mu.RUnlock() + if !ok { + return "", false + } + if strings.TrimSpace(t.Task) == "" { + return "", false + } + label := strings.TrimSpace(t.Label) + if label == "" { + label = "resumed" + } else { + label = label + "-resumed" + } + _, err := sm.Spawn(ctx, t.Task, label, t.OriginChannel, t.OriginChatID, t.PipelineID, t.PipelineTask) + if err != nil { + return "", false + } + return label, true +} + func (sm *SubagentManager) pruneArchivedLocked() { if sm.archiveAfterMinute <= 0 { return diff --git a/pkg/tools/subagents_tool.go b/pkg/tools/subagents_tool.go index ae63693..8d5fb2c 100644 --- a/pkg/tools/subagents_tool.go +++ b/pkg/tools/subagents_tool.go @@ -27,7 +27,7 @@ func (t *SubagentsTool) Parameters() map[string]interface{} { return map[string]interface{}{ "type": "object", "properties": map[string]interface{}{ - "action": map[string]interface{}{"type": "string", "description": "list|info|kill|steer|send|log"}, + "action": map[string]interface{}{"type": "string", "description": "list|info|kill|steer|send|log|resume"}, "id": map[string]interface{}{"type": "string", "description": "subagent id/#index/all for info/kill/steer/send/log"}, "message": map[string]interface{}{"type": "string", "description": "steering message for steer/send action"}, "recent_minutes": map[string]interface{}{"type": "integer", "description": "optional list/info all filter by recent updated minutes"}, @@ -148,6 +148,16 @@ func (t *SubagentsTool) Execute(ctx context.Context, args map[string]interface{} sb.WriteString("Result Preview:\n" + result) } return strings.TrimSpace(sb.String()), nil + case "resume": + resolvedID, err := t.resolveTaskID(id) + if err != nil { + return err.Error(), nil + } + label, ok := t.manager.ResumeTask(ctx, resolvedID) + if !ok { + return "subagent resume failed", nil + } + return fmt.Sprintf("subagent resumed as %s", label), nil default: return "unsupported action", nil }