add sessions tool and subagents steer control

This commit is contained in:
DBT
2026-02-23 13:13:06 +00:00
parent 89847f5672
commit 5f8678f091
5 changed files with 185 additions and 5 deletions

101
pkg/tools/sessions_tool.go Normal file
View File

@@ -0,0 +1,101 @@
package tools
import (
"context"
"fmt"
"sort"
"strings"
"time"
"clawgo/pkg/providers"
)
type SessionInfo struct {
Key string
Kind string
Summary string
UpdatedAt time.Time
}
type SessionsTool struct {
listFn func(limit int) []SessionInfo
historyFn func(key string, limit int) []providers.Message
}
func NewSessionsTool(listFn func(limit int) []SessionInfo, historyFn func(key string, limit int) []providers.Message) *SessionsTool {
return &SessionsTool{listFn: listFn, historyFn: historyFn}
}
func (t *SessionsTool) Name() string { return "sessions" }
func (t *SessionsTool) Description() string {
return "Inspect sessions in current runtime: list or history"
}
func (t *SessionsTool) Parameters() map[string]interface{} {
return map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"action": map[string]interface{}{"type": "string", "description": "list|history"},
"key": map[string]interface{}{"type": "string", "description": "session key for history"},
"limit": map[string]interface{}{"type": "integer", "description": "max items", "default": 20},
},
"required": []string{"action"},
}
}
func (t *SessionsTool) Execute(ctx context.Context, args map[string]interface{}) (string, error) {
_ = ctx
action, _ := args["action"].(string)
action = strings.ToLower(strings.TrimSpace(action))
limit := 20
if v, ok := args["limit"].(float64); ok && int(v) > 0 {
limit = int(v)
}
switch action {
case "list":
if t.listFn == nil {
return "sessions list unavailable", nil
}
items := t.listFn(limit)
if len(items) == 0 {
return "No sessions.", nil
}
sort.Slice(items, func(i, j int) bool { return items[i].UpdatedAt.After(items[j].UpdatedAt) })
var sb strings.Builder
sb.WriteString("Sessions:\n")
for _, s := range items {
sb.WriteString(fmt.Sprintf("- %s kind=%s updated=%s\n", s.Key, s.Kind, s.UpdatedAt.Format(time.RFC3339)))
}
return strings.TrimSpace(sb.String()), nil
case "history":
if t.historyFn == nil {
return "sessions history unavailable", nil
}
key, _ := args["key"].(string)
key = strings.TrimSpace(key)
if key == "" {
return "key is required for history", nil
}
h := t.historyFn(key, limit)
if len(h) == 0 {
return "No history.", nil
}
if len(h) > limit {
h = h[len(h)-limit:]
}
var sb strings.Builder
sb.WriteString(fmt.Sprintf("History for %s:\n", key))
for _, m := range h {
content := strings.TrimSpace(m.Content)
if len(content) > 180 {
content = content[:180] + "..."
}
sb.WriteString(fmt.Sprintf("- [%s] %s\n", m.Role, content))
}
return strings.TrimSpace(sb.String()), nil
default:
return "unsupported action", nil
}
}

View File

@@ -3,6 +3,7 @@ package tools
import (
"context"
"fmt"
"strings"
"sync"
"time"
@@ -22,6 +23,7 @@ type SubagentTask struct {
OriginChatID string
Status string
Result string
Steering []string
Created int64
Updated int64
}
@@ -230,3 +232,19 @@ func (sm *SubagentManager) KillTask(taskID string) bool {
}
return true
}
func (sm *SubagentManager) SteerTask(taskID, message string) bool {
sm.mu.Lock()
defer sm.mu.Unlock()
t, ok := sm.tasks[taskID]
if !ok {
return false
}
message = strings.TrimSpace(message)
if message == "" {
return false
}
t.Steering = append(t.Steering, message)
t.Updated = time.Now().UnixMilli()
return true
}

View File

@@ -17,15 +17,16 @@ func NewSubagentsTool(m *SubagentManager) *SubagentsTool {
func (t *SubagentsTool) Name() string { return "subagents" }
func (t *SubagentsTool) Description() string {
return "Manage subagent runs in current process: list, info, kill"
return "Manage subagent runs in current process: list, info, kill, steer"
}
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"},
"id": map[string]interface{}{"type": "string", "description": "subagent id for info/kill"},
"action": map[string]interface{}{"type": "string", "description": "list|info|kill|steer"},
"id": map[string]interface{}{"type": "string", "description": "subagent id for info/kill/steer"},
"message": map[string]interface{}{"type": "string", "description": "steering message for steer action"},
},
"required": []string{"action"},
}
@@ -40,6 +41,8 @@ func (t *SubagentsTool) Execute(ctx context.Context, args map[string]interface{}
action = strings.ToLower(strings.TrimSpace(action))
id, _ := args["id"].(string)
id = strings.TrimSpace(id)
message, _ := args["message"].(string)
message = strings.TrimSpace(message)
switch action {
case "list":
@@ -70,6 +73,14 @@ func (t *SubagentsTool) Execute(ctx context.Context, args map[string]interface{}
return "subagent not found", nil
}
return "subagent kill requested", nil
case "steer":
if id == "" || message == "" {
return "id and message are required for steer", nil
}
if !t.manager.SteerTask(id, message) {
return "subagent not found", nil
}
return "steering message accepted", nil
default:
return "unsupported action", nil
}