From 1e5ce2482f48dbe6a2f07639f236823c0d1c6af7 Mon Sep 17 00:00:00 2001 From: DBT Date: Mon, 23 Feb 2026 13:49:50 +0000 Subject: [PATCH] add subagent all/# controls and session window filters --- cmd/clawgo/cmd_status.go | 40 +++++++++++++++++++++++ pkg/tools/sessions_tool.go | 64 ++++++++++++++++++++++++++++++++++++- pkg/tools/subagents_tool.go | 15 ++++++++- 3 files changed, 117 insertions(+), 2 deletions(-) diff --git a/cmd/clawgo/cmd_status.go b/cmd/clawgo/cmd_status.go index 0a32a13..2601d8e 100644 --- a/cmd/clawgo/cmd_status.go +++ b/cmd/clawgo/cmd_status.go @@ -93,6 +93,12 @@ func statusCmd() { if data, err := os.ReadFile(triggerStats); err == nil { fmt.Printf("Trigger Stats: %s\n", strings.TrimSpace(string(data))) } + if errs, err := collectRecentTriggerErrors(filepath.Join(workspace, "memory", "trigger-audit.jsonl"), 5); err == nil && len(errs) > 0 { + fmt.Println("Recent Trigger Errors:") + for _, e := range errs { + fmt.Printf(" - %s\n", e) + } + } sessionsDir := filepath.Join(filepath.Dir(configPath), "sessions") if kinds, err := collectSessionKindCounts(sessionsDir); err == nil && len(kinds) > 0 { @@ -142,6 +148,40 @@ func collectSessionKindCounts(sessionsDir string) (map[string]int, error) { return counts, nil } +func collectRecentTriggerErrors(path string, limit int) ([]string, error) { + data, err := os.ReadFile(path) + if err != nil { + return nil, err + } + lines := strings.Split(strings.TrimSpace(string(data)), "\n") + if len(lines) == 1 && strings.TrimSpace(lines[0]) == "" { + return nil, nil + } + out := make([]string, 0, limit) + for i := len(lines) - 1; i >= 0; i-- { + line := strings.TrimSpace(lines[i]) + if line == "" { + continue + } + var row struct { + Time string `json:"time"` + Trigger string `json:"trigger"` + Error string `json:"error"` + } + if err := json.Unmarshal([]byte(line), &row); err != nil { + continue + } + if strings.TrimSpace(row.Error) == "" { + continue + } + out = append(out, fmt.Sprintf("[%s/%s] %s", row.Time, row.Trigger, row.Error)) + if len(out) >= limit { + break + } + } + return out, nil +} + func collectRecentSubagentSessions(sessionsDir string, limit int) ([]string, error) { entries, err := os.ReadDir(sessionsDir) if err != nil { diff --git a/pkg/tools/sessions_tool.go b/pkg/tools/sessions_tool.go index 6187c27..d7e8246 100644 --- a/pkg/tools/sessions_tool.go +++ b/pkg/tools/sessions_tool.go @@ -42,6 +42,9 @@ func (t *SessionsTool) Parameters() map[string]interface{} { "active_minutes": map[string]interface{}{"type": "integer", "description": "only sessions updated in recent N minutes (list action)"}, "kinds": map[string]interface{}{"type": "array", "items": map[string]interface{}{"type": "string"}, "description": "optional session kinds filter for list"}, "include_tools": map[string]interface{}{"type": "boolean", "description": "include tool role messages in history", "default": false}, + "around": map[string]interface{}{"type": "integer", "description": "1-indexed message index center for history window"}, + "before": map[string]interface{}{"type": "integer", "description": "1-indexed message index upper bound (exclusive)"}, + "after": map[string]interface{}{"type": "integer", "description": "1-indexed message index lower bound (exclusive)"}, }, "required": []string{"action"}, } @@ -59,6 +62,18 @@ func (t *SessionsTool) Execute(ctx context.Context, args map[string]interface{}) if v, ok := args["include_tools"].(bool); ok { includeTools = v } + around := 0 + if v, ok := args["around"].(float64); ok && int(v) > 0 { + around = int(v) + } + before := 0 + if v, ok := args["before"].(float64); ok && int(v) > 0 { + before = int(v) + } + after := 0 + if v, ok := args["after"].(float64); ok && int(v) > 0 { + after = int(v) + } activeMinutes := 0 if v, ok := args["active_minutes"].(float64); ok && int(v) > 0 { activeMinutes = int(v) @@ -126,10 +141,57 @@ func (t *SessionsTool) Execute(ctx context.Context, args map[string]interface{}) if key == "" { return "key is required for history", nil } - h := t.historyFn(key, limit*3) + h := t.historyFn(key, 0) if len(h) == 0 { return "No history.", nil } + + // Window selectors are 1-indexed (human-friendly) + if around > 0 { + center := around - 1 + if center < 0 { + center = 0 + } + if center >= len(h) { + center = len(h) - 1 + } + half := limit / 2 + if half < 1 { + half = 1 + } + start := center - half + if start < 0 { + start = 0 + } + end := center + half + 1 + if end > len(h) { + end = len(h) + } + h = h[start:end] + } else { + start := 0 + end := len(h) + if after > 0 { + start = after + if start > len(h) { + start = len(h) + } + } + if before > 0 { + end = before - 1 + if end < 0 { + end = 0 + } + if end > len(h) { + end = len(h) + } + } + if start > end { + start = end + } + h = h[start:end] + } + if !includeTools { filtered := make([]providers.Message, 0, len(h)) for _, m := range h { diff --git a/pkg/tools/subagents_tool.go b/pkg/tools/subagents_tool.go index 7f9764a..0a1bcf7 100644 --- a/pkg/tools/subagents_tool.go +++ b/pkg/tools/subagents_tool.go @@ -27,7 +27,7 @@ func (t *SubagentsTool) Parameters() map[string]interface{} { "type": "object", "properties": map[string]interface{}{ "action": map[string]interface{}{"type": "string", "description": "list|info|kill|steer|send|log"}, - "id": map[string]interface{}{"type": "string", "description": "subagent id for info/kill/steer/send/log"}, + "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"}, }, "required": []string{"action"}, @@ -70,6 +70,19 @@ func (t *SubagentsTool) Execute(ctx context.Context, args map[string]interface{} } return fmt.Sprintf("ID: %s\nStatus: %s\nLabel: %s\nCreated: %d\nUpdated: %d\nSteering Count: %d\nTask: %s\nResult:\n%s", task.ID, task.Status, task.Label, task.Created, task.Updated, len(task.Steering), task.Task, task.Result), nil case "kill": + if strings.EqualFold(strings.TrimSpace(id), "all") { + tasks := t.manager.ListTasks() + if len(tasks) == 0 { + return "No subagents.", nil + } + killed := 0 + for _, task := range tasks { + if t.manager.KillTask(task.ID) { + killed++ + } + } + return fmt.Sprintf("subagent kill requested for %d tasks", killed), nil + } resolvedID, err := t.resolveTaskID(id) if err != nil { return err.Error(), nil