diff --git a/pkg/agent/core.go b/pkg/agent/core.go index baaf280..914476b 100644 --- a/pkg/agent/core.go +++ b/pkg/agent/core.go @@ -607,12 +607,41 @@ func (s *agent) InjectSystemEvent(content string) { s.Queue("\n" + content + "\n") } +// classifyReaction returns a category and description for a reaction emoji. +func classifyReaction(emoji string) (category, description string, positive bool) { + switch emoji { + case "👍", "✅": + return "positive", "The response was good. Keep doing what you're doing.", true + case "🚀", "🎉": + return "excellent", "The response was exactly what was wanted.", true + case "👎", "❌": + return "negative", "The response was wrong or unhelpful.", false + case "💩", "🤬": + return "terrible", "The response was fundamentally wrong. Stop this approach entirely and reassess from scratch.", false + case "🤔": + return "confused", "The response was unclear or confusing.", false + default: + return "unknown", "", false + } +} + func (s *agent) React(emoji string) { if s.session == nil { return } - msg := "[reacted " + emoji + " to your previous response]" + category, desc, positive := classifyReaction(emoji) + var msg string + if desc != "" { + msg = "[reaction: " + category + " (" + emoji + ")] " + desc + } else { + msg = "[reacted " + emoji + " to your previous response]" + } s.session.appendUserMessage(msg) + if positive { + s.session.PositiveReactions++ + } else if category == "negative" || category == "terrible" { + s.session.NegativeReactions++ + } s.emit(Event{Role: "user", Content: msg}) } @@ -773,7 +802,6 @@ func (s *agent) SetSessionID(newID string) error { const defaultContextLength = 128000 const defaultToolResultMaxBytes = 131072 - // autoCompactLimit returns the token threshold for auto-compaction (75%). func (s *agent) autoCompactLimit(ctx context.Context) int { ctxLen := s.runtime.Backend.ContextLength(ctx) @@ -1204,16 +1232,16 @@ func (s *agent) executeTurn(ctx context.Context, input string) string { // Build per-turn agentConfig from the current runtime. s.cfg = agentConfig{ - Backend: s.runtime.Backend, - preamble: s.runtime.Preamble, - Tools: s.runtime.Tools, - Exec: s.runtime.Exec, - ClassifyTool: s.runtime.ClassifyTool, - ClassifyTier: s.runtime.ClassifyTier, - GenerationParams: s.runtime.GenParams, - MaxSteps: s.runtime.MaxSteps, - ReadPlanStep: s.readPlanStep, - TurnError: s.turnError, + Backend: s.runtime.Backend, + preamble: s.runtime.Preamble, + Tools: s.runtime.Tools, + Exec: s.runtime.Exec, + ClassifyTool: s.runtime.ClassifyTool, + ClassifyTier: s.runtime.ClassifyTier, + GenerationParams: s.runtime.GenParams, + MaxSteps: s.runtime.MaxSteps, + ReadPlanStep: s.readPlanStep, + TurnError: s.turnError, } var replyBuf strings.Builder diff --git a/pkg/agent/session.go b/pkg/agent/session.go index 68ab243..eee8eb1 100644 --- a/pkg/agent/session.go +++ b/pkg/agent/session.go @@ -52,6 +52,9 @@ type PersistedSession struct { Estimated bool `json:"estimated,omitempty"` LastTurnCostUSD float64 `json:"lastTurnCostUSD,omitempty"` SessionCostUSD float64 `json:"sessionCostUSD,omitempty"` + // Reaction tracking. + PositiveReactions int `json:"positiveReactions,omitempty"` + NegativeReactions int `json:"negativeReactions,omitempty"` } // TaskState is a compact structured overlay that summarizes the agent's @@ -117,6 +120,8 @@ func (s *Session) saveToFull(path, id, agentName, backendName, modelName, cwd, r Estimated: s.Estimated, LastTurnCostUSD: s.LastTurnCostUSD, SessionCostUSD: s.SessionCostUSD, + PositiveReactions: s.PositiveReactions, + NegativeReactions: s.NegativeReactions, } data, err := json.Marshal(ps) if err != nil { @@ -151,6 +156,8 @@ func RestoreSession(ps *PersistedSession) *Session { Estimated: ps.Estimated, LastTurnCostUSD: ps.LastTurnCostUSD, SessionCostUSD: ps.SessionCostUSD, + PositiveReactions: ps.PositiveReactions, + NegativeReactions: ps.NegativeReactions, } for _, m := range ps.Messages { if m.Role == "user" { @@ -181,6 +188,9 @@ type Session struct { turnCreationTokens int turnOutputTokens int turnCostUSD float64 // >0 when backend reported cost directly (e.g. OpenRouter) + // Reaction tracking. + PositiveReactions int + NegativeReactions int } // newSession creates a new empty Session. The caller is responsible for