agent: rename receiver s → a for clarity

The agent struct receiver was historically 's' (from when it was
called 'session'). Now that session is a separate concept, rename
to 'a' to match the type name.
This commit is contained in:
Levi Neely 2026-07-29 18:33:21 +02:00
parent 6396167361
commit 23de2e8267
2 changed files with 522 additions and 522 deletions

View File

@ -12,7 +12,7 @@ import (
) )
func (s *agent) handleCommand(ctx context.Context, input string) bool { func (a *agent) handleCommand(ctx context.Context, input string) bool {
if !strings.HasPrefix(input, "/") { if !strings.HasPrefix(input, "/") {
return false return false
} }
@ -25,9 +25,9 @@ func (s *agent) handleCommand(ctx context.Context, input string) bool {
args := parts[1:] args := parts[1:]
listFromHandler := func(name string) { listFromHandler := func(name string) {
if h := s.listHandlers[name]; h != nil { if h := a.listHandlers[name]; h != nil {
for _, item := range h() { for _, item := range h() {
s.emit(infoEvent(" " + item)) a.emit(infoEvent(" " + item))
} }
} }
} }
@ -37,88 +37,88 @@ func (s *agent) handleCommand(ctx context.Context, input string) bool {
"/i": func(args []string) { "/i": func(args []string) {
prompt := strings.Join(args, " ") prompt := strings.Join(args, " ")
if prompt == "" { if prompt == "" {
s.emit(infoEvent("error: /i requires a prompt")) a.emit(infoEvent("error: /i requires a prompt"))
return return
} }
if s.IsRunning() { if a.IsRunning() {
s.Inject(prompt) a.Inject(prompt)
} else { } else {
go s.Submit(context.Background(), prompt) go a.Submit(context.Background(), prompt)
} }
}, },
"/irw": func(args []string) { "/irw": func(args []string) {
prompt := strings.Join(args, " ") prompt := strings.Join(args, " ")
if prompt == "" { if prompt == "" {
s.emit(infoEvent("error: /irw requires a prompt")) a.emit(infoEvent("error: /irw requires a prompt"))
return return
} }
s.injectRewrite(prompt) a.injectRewrite(prompt)
}, },
"/backend": func(args []string) { "/backend": func(args []string) {
if len(args) == 0 { if len(args) == 0 {
s.emit(infoEvent(s.runtime.Backend.Name())) a.emit(infoEvent(a.runtime.Backend.Name()))
return return
} }
if s.IsRunning() { if a.IsRunning() {
s.emit(infoEvent("error: cannot switch backend while agent is running")) a.emit(infoEvent("error: cannot switch backend while agent is running"))
return return
} }
be, err := s.newBackend(args[0]) be, err := a.newBackend(args[0])
if err != nil { if err != nil {
s.emit(infoEvent(fmt.Sprintf("error: failed to switch backend: %v", err))) a.emit(infoEvent(fmt.Sprintf("error: failed to switch backend: %v", err)))
return return
} }
s.runtime.Backend = be a.runtime.Backend = be
s.emit(infoEvent(fmt.Sprintf("switched backend to: %s (model: %s)", be.Name(), be.Model()))) a.emit(infoEvent(fmt.Sprintf("switched backend to: %s (model: %s)", be.Name(), be.Model())))
}, },
"/models": func(args []string) { "/models": func(args []string) {
models := s.runtime.Backend.Models(ctx) models := a.runtime.Backend.Models(ctx)
if len(models) == 0 { if len(models) == 0 {
s.emit(infoEvent("no models available")) a.emit(infoEvent("no models available"))
return return
} }
slices.Sort(models) slices.Sort(models)
current := s.runtime.Backend.Model() current := a.runtime.Backend.Model()
for _, m := range models { for _, m := range models {
marker := " " marker := " "
if m == current { if m == current {
marker = "* " marker = "* "
} }
s.emit(infoEvent(marker + m)) a.emit(infoEvent(marker + m))
} }
}, },
"/model": func(args []string) { "/model": func(args []string) {
if len(args) == 0 { if len(args) == 0 {
s.emit(infoEvent(s.runtime.Backend.Model())) a.emit(infoEvent(a.runtime.Backend.Model()))
return return
} }
s.runtime.Backend.SetModel(args[0]) a.runtime.Backend.SetModel(args[0])
s.emit(infoEvent("switched model to: " + args[0])) a.emit(infoEvent("switched model to: " + args[0]))
}, },
"/maxsteps": func(args []string) { "/maxsteps": func(args []string) {
if len(args) == 0 { if len(args) == 0 {
if s.runtime.MaxSteps == 0 { if a.runtime.MaxSteps == 0 {
s.emit(infoEvent("maxsteps: unlimited")) a.emit(infoEvent("maxsteps: unlimited"))
} else { } else {
s.emit(infoEvent(fmt.Sprintf("maxsteps: %d", s.runtime.MaxSteps))) a.emit(infoEvent(fmt.Sprintf("maxsteps: %d", a.runtime.MaxSteps)))
} }
return return
} }
n, err := strconv.Atoi(args[0]) n, err := strconv.Atoi(args[0])
if err != nil || n < 0 { if err != nil || n < 0 {
s.emit(infoEvent("error: maxsteps requires a non-negative integer (0 = unlimited)")) a.emit(infoEvent("error: maxsteps requires a non-negative integer (0 = unlimited)"))
return return
} }
s.runtime.MaxSteps = n a.runtime.MaxSteps = n
if n == 0 { if n == 0 {
s.emit(infoEvent("maxsteps: unlimited")) a.emit(infoEvent("maxsteps: unlimited"))
} else { } else {
s.emit(infoEvent(fmt.Sprintf("maxsteps: %d", n))) a.emit(infoEvent(fmt.Sprintf("maxsteps: %d", n)))
} }
}, },
@ -140,48 +140,48 @@ func (s *agent) handleCommand(ctx context.Context, input string) bool {
} }
seen[name] = true seen[name] = true
marker := " " marker := " "
if name == s.agentName { if name == a.agentName {
marker = "* " marker = "* "
} }
s.emit(infoEvent(marker + name)) a.emit(infoEvent(marker + name))
found = true found = true
} }
} }
if !found { if !found {
s.emit(infoEvent("no agents found")) a.emit(infoEvent("no agents found"))
} }
}, },
"/agent": func(args []string) { "/agent": func(args []string) {
if len(args) == 0 { if len(args) == 0 {
s.emit(infoEvent("active agent: " + s.agentName)) a.emit(infoEvent("active agent: " + a.agentName))
return return
} }
if s.IsRunning() { if a.IsRunning() {
s.emit(infoEvent("error: cannot switch agent while agent is running")) a.emit(infoEvent("error: cannot switch agent while agent is running"))
return return
} }
name := args[0] name := args[0]
cfgPath := AgentConfigPath(s.agentsDir, name) cfgPath := AgentConfigPath(a.agentsDir, name)
f, err := os.Open(cfgPath) f, err := os.Open(cfgPath)
if err != nil { if err != nil {
s.emit(infoEvent(fmt.Sprintf("error: agent %q: %v", name, err))) a.emit(infoEvent(fmt.Sprintf("error: agent %q: %v", name, err)))
return return
} }
cfg, err := Load(f) cfg, err := Load(f)
f.Close() f.Close()
if err != nil { if err != nil {
s.emit(infoEvent(fmt.Sprintf("error: agent %q: %v", name, err))) a.emit(infoEvent(fmt.Sprintf("error: agent %q: %v", name, err)))
return return
} }
d := s.newDispatcher() d := a.newDispatcher()
env := []string{"OLLIE_SESSION_ID=" + s.sess.ID(), "OLLIE_UNAME=" + s.sess.Uname()} env := []string{"OLLIE_SESSION_ID=" + a.sess.ID(), "OLLIE_UNAME=" + a.sess.Uname()}
env = append(env, s.promptEnvExtra...) env = append(env, a.promptEnvExtra...)
rt := BuildRuntime(cfg, d, s.sess.CWD(), env, s.baseLayers...) rt := BuildRuntime(cfg, d, a.sess.CWD(), env, a.baseLayers...)
if rt.CfgBackend != "" { if rt.CfgBackend != "" {
newBe, err := s.newBackend(rt.CfgBackend) newBe, err := a.newBackend(rt.CfgBackend)
if err != nil { if err != nil {
s.emit(infoEvent(fmt.Sprintf("error: backend %q: %v", rt.CfgBackend, err))) a.emit(infoEvent(fmt.Sprintf("error: backend %q: %v", rt.CfgBackend, err)))
return return
} }
if rt.CfgModel != "" { if rt.CfgModel != "" {
@ -189,47 +189,47 @@ func (s *agent) handleCommand(ctx context.Context, input string) bool {
} }
rt.Backend = newBe rt.Backend = newBe
} else { } else {
rt.Backend = s.runtime.Backend rt.Backend = a.runtime.Backend
if rt.CfgModel != "" { if rt.CfgModel != "" {
rt.Backend.SetModel(rt.CfgModel) rt.Backend.SetModel(rt.CfgModel)
} }
} }
s.runtime = rt a.runtime = rt
s.agentName = name a.agentName = name
s.history = nil a.history = nil
s.pushSessionEnv() a.pushSessionEnv()
s.notifyChange() a.notifyChange()
for _, msg := range rt.Messages { for _, msg := range rt.Messages {
s.emit(infoEvent(msg)) a.emit(infoEvent(msg))
} }
s.emit(infoEvent("agent: " + name)) a.emit(infoEvent("agent: " + name))
}, },
"/compact": func(args []string) { "/compact": func(args []string) {
if s.IsRunning() { if a.IsRunning() {
s.emit(infoEvent("error: cannot compact while agent is running")) a.emit(infoEvent("error: cannot compact while agent is running"))
return return
} }
if s.history == nil { if a.history == nil {
s.emit(infoEvent("nothing to compact")) a.emit(infoEvent("nothing to compact"))
return return
} }
snapshot := s.history.PreCompactionSnapshot() snapshot := a.history.PreCompactionSnapshot()
s.setState("compacting") a.setState("compacting")
n, err := s.runCompact(ctx, "manual") n, err := a.runCompact(ctx, "manual")
s.setState("idle") a.setState("idle")
if err != nil { if err != nil {
s.emit(infoEvent("compact error: " + err.Error())) a.emit(infoEvent("compact error: " + err.Error()))
return return
} }
if n == 0 { if n == 0 {
s.emit(infoEvent("nothing to compact")) a.emit(infoEvent("nothing to compact"))
return return
} }
if s.sessionsDir != "" && s.sess.ID() != "" { if a.sessionsDir != "" && a.sess.ID() != "" {
histPath := s.activeSessionPath(s.sess.ID(), ".compaction.jsonl") histPath := a.activeSessionPath(a.sess.ID(), ".compaction.jsonl")
if err := os.MkdirAll(filepath.Dir(histPath), 0700); err != nil { if err := os.MkdirAll(filepath.Dir(histPath), 0700); err != nil {
s.emit(infoEvent("compaction history save: " + err.Error())) a.emit(infoEvent("compaction history save: " + err.Error()))
} else if data, err := json.Marshal(snapshot); err == nil { } else if data, err := json.Marshal(snapshot); err == nil {
f, err := os.OpenFile(histPath, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0600) f, err := os.OpenFile(histPath, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0600)
if err == nil { if err == nil {
@ -238,76 +238,76 @@ func (s *agent) handleCommand(ctx context.Context, input string) bool {
} }
} }
} }
s.emit(infoEvent(fmt.Sprintf("compacted %d messages", n))) a.emit(infoEvent(fmt.Sprintf("compacted %d messages", n)))
s.saveSession() a.saveSession()
}, },
"/context": func(args []string) { "/context": func(args []string) {
if s.history == nil { if a.history == nil {
s.emit(infoEvent("no active session")) a.emit(infoEvent("no active session"))
return return
} }
ctxLen := s.runtime.Backend.ContextLength(ctx) ctxLen := a.runtime.Backend.ContextLength(ctx)
if ctxLen <= 0 { if ctxLen <= 0 {
ctxLen = defaultContextLength ctxLen = defaultContextLength
} }
estimated := s.history.estimateTokens() estimated := a.history.estimateTokens()
pct := estimated * 100 / ctxLen pct := estimated * 100 / ctxLen
s.emit(infoEvent(fmt.Sprintf("~%d / %d tokens (%d%%)", estimated, ctxLen, pct))) a.emit(infoEvent(fmt.Sprintf("~%d / %d tokens (%d%%)", estimated, ctxLen, pct)))
s.emit(infoEvent(strings.TrimRight(s.history.contextDebug(), "\n"))) a.emit(infoEvent(strings.TrimRight(a.history.contextDebug(), "\n")))
}, },
"/cost": func(args []string) { "/cost": func(args []string) {
if s.history == nil { if a.history == nil {
s.emit(infoEvent("no active session")) a.emit(infoEvent("no active session"))
return return
} }
s.emit(infoEvent(fmt.Sprintf("last=$%.4f session=$%.4f", a.emit(infoEvent(fmt.Sprintf("last=$%.4f session=$%.4f",
s.history.LastTurnCostUSD, s.history.SessionCostUSD))) a.history.LastTurnCostUSD, a.history.SessionCostUSD)))
}, },
"/usage": func(args []string) { "/usage": func(args []string) {
if s.history == nil { if a.history == nil {
s.emit(infoEvent("no active session")) a.emit(infoEvent("no active session"))
return return
} }
ctxLen := s.runtime.Backend.ContextLength(ctx) ctxLen := a.runtime.Backend.ContextLength(ctx)
if ctxLen <= 0 { if ctxLen <= 0 {
ctxLen = defaultContextLength ctxLen = defaultContextLength
} }
estimated := s.history.estimateTokens() estimated := a.history.estimateTokens()
pct := estimated * 100 / ctxLen pct := estimated * 100 / ctxLen
usageStr := fmt.Sprintf("~%d / %d tokens (%d%%) | %d in, %d out, %d requests", usageStr := fmt.Sprintf("~%d / %d tokens (%d%%) | %d in, %d out, %d requests",
estimated, ctxLen, pct, estimated, ctxLen, pct,
s.history.TotalInputTokens, s.history.TotalOutputTokens, a.history.TotalInputTokens, a.history.TotalOutputTokens,
s.history.TotalRequests) a.history.TotalRequests)
if s.history.Estimated { if a.history.Estimated {
usageStr += " [estimated]" usageStr += " [estimated]"
} }
s.emit(infoEvent(usageStr)) a.emit(infoEvent(usageStr))
}, },
"/history": func(args []string) { "/history": func(args []string) {
if s.history == nil { if a.history == nil {
s.emit(infoEvent("no active session")) a.emit(infoEvent("no active session"))
return return
} }
for _, msg := range s.history.history() { for _, msg := range a.history.history() {
preview := msg.Content preview := msg.Content
if len(preview) > 200 { if len(preview) > 200 {
preview = preview[:200] + "..." preview = preview[:200] + "..."
} }
s.emit(infoEvent(fmt.Sprintf("[%s] %s", msg.Role, preview))) a.emit(infoEvent(fmt.Sprintf("[%s] %s", msg.Role, preview)))
} }
}, },
"/clear": func(args []string) { "/clear": func(args []string) {
if s.IsRunning() { if a.IsRunning() {
s.emit(infoEvent("error: cannot clear while agent is running")) a.emit(infoEvent("error: cannot clear while agent is running"))
return return
} }
s.history = nil a.history = nil
s.emit(infoEvent("cleared")) a.emit(infoEvent("cleared"))
}, },
"/sessions": func(args []string) { "/sessions": func(args []string) {
@ -333,10 +333,10 @@ func (s *agent) handleCommand(ctx context.Context, input string) bool {
}) })
} }
} }
appendSessionFiles(s.sessionsDir) appendSessionFiles(a.sessionsDir)
appendSessionFiles(filepath.Join(s.sessionsDir, "active")) appendSessionFiles(filepath.Join(a.sessionsDir, "active"))
cwd := s.CWD() cwd := a.CWD()
found := false found := false
for _, file := range files { for _, file := range files {
data, readErr := os.ReadFile(file.path) data, readErr := os.ReadFile(file.path)
@ -351,7 +351,7 @@ func (s *agent) handleCommand(ctx context.Context, input string) bool {
continue continue
} }
marker := " " marker := " "
if file.id == s.sess.ID() { if file.id == a.sess.ID() {
marker = "* " marker = "* "
} }
goal := "" goal := ""
@ -364,75 +364,75 @@ func (s *agent) handleCommand(ctx context.Context, input string) bool {
if len(goal) > 60 { if len(goal) > 60 {
goal = goal[:60] + "..." goal = goal[:60] + "..."
} }
s.emit(infoEvent(marker + fmt.Sprintf("%-24s [%s] %q", file.id, ps.Agent, goal))) a.emit(infoEvent(marker + fmt.Sprintf("%-24s [%s] %q", file.id, ps.Agent, goal)))
found = true found = true
} }
if !found { if !found {
s.emit(infoEvent("no sessions for " + cwd)) a.emit(infoEvent("no sessions for " + cwd))
} }
}, },
"/save": func(args []string) { "/save": func(args []string) {
if s.history == nil { if a.history == nil {
s.emit(infoEvent("error: no active session")) a.emit(infoEvent("error: no active session"))
return return
} }
if len(args) == 0 { if len(args) == 0 {
s.emit(infoEvent("error: /save requires a name")) a.emit(infoEvent("error: /save requires a name"))
return return
} }
name := args[0] name := args[0]
path := s.sessionsDir + "/" + name + ".json" path := a.sessionsDir + "/" + name + ".json"
if err := s.history.saveTo(path, name, s.agentName, s.CWD()); err != nil { if err := a.history.saveTo(path, name, a.agentName, a.CWD()); err != nil {
s.emit(infoEvent("error: " + err.Error())) a.emit(infoEvent("error: " + err.Error()))
return return
} }
s.emit(infoEvent("saved: " + path)) a.emit(infoEvent("saved: " + path))
}, },
"/resume": func(args []string) { "/resume": func(args []string) {
if len(args) == 0 { if len(args) == 0 {
s.emit(infoEvent("error: /resume requires a session id or name")) a.emit(infoEvent("error: /resume requires a session id or name"))
return return
} }
if s.IsRunning() { if a.IsRunning() {
s.emit(infoEvent("error: cannot resume while agent is running")) a.emit(infoEvent("error: cannot resume while agent is running"))
return return
} }
name := args[0] name := args[0]
path := s.sessionsDir + "/" + name + ".json" path := a.sessionsDir + "/" + name + ".json"
data, err := os.ReadFile(path) data, err := os.ReadFile(path)
if err != nil { if err != nil {
s.emit(infoEvent(fmt.Sprintf("error: %v", err))) a.emit(infoEvent(fmt.Sprintf("error: %v", err)))
return return
} }
var ps PersistedSession var ps PersistedSession
if err := json.Unmarshal(data, &ps); err != nil { if err := json.Unmarshal(data, &ps); err != nil {
s.emit(infoEvent(fmt.Sprintf("error: %v", err))) a.emit(infoEvent(fmt.Sprintf("error: %v", err)))
return return
} }
s.history = RestoreHistory(&ps) a.history = RestoreHistory(&ps)
s.emit(infoEvent(fmt.Sprintf("resumed session %s (%d messages)", name, len(ps.Messages)))) a.emit(infoEvent(fmt.Sprintf("resumed session %s (%d messages)", name, len(ps.Messages))))
}, },
"/cwd": func(args []string) { "/cwd": func(args []string) {
if len(args) == 0 { if len(args) == 0 {
s.emit(infoEvent("cwd: " + s.CWD())) a.emit(infoEvent("cwd: " + a.CWD()))
return return
} }
dir := strings.Join(args, " ") dir := strings.Join(args, " ")
if err := s.SetCWD(dir); err != nil { if err := a.SetCWD(dir); err != nil {
s.emit(infoEvent("error: " + err.Error())) a.emit(infoEvent("error: " + err.Error()))
return return
} }
s.emit(infoEvent("cwd: " + dir)) a.emit(infoEvent("cwd: " + dir))
}, },
"/skills": func(args []string) { listFromHandler("skills") }, "/skills": func(args []string) { listFromHandler("skills") },
"/tools": func(args []string) { listFromHandler("tools") }, "/tools": func(args []string) { listFromHandler("tools") },
"/sp": func(args []string) { "/sp": func(args []string) {
s.emit(infoEvent(s.runtime.Preamble)) a.emit(infoEvent(a.runtime.Preamble))
}, },
"/help": func(args []string) { "/help": func(args []string) {
@ -466,7 +466,7 @@ func (s *agent) handleCommand(ctx context.Context, input string) bool {
" !<cmd> - run shell command", " !<cmd> - run shell command",
} }
for _, l := range lines { for _, l := range lines {
s.emit(infoEvent(l)) a.emit(infoEvent(l))
} }
}, },
} }
@ -475,7 +475,7 @@ func (s *agent) handleCommand(ctx context.Context, input string) bool {
if !ok { if !ok {
return false return false
} }
s.emit(infoEvent("")) a.emit(infoEvent(""))
fn(args) fn(args)
return true return true
} }

File diff suppressed because it is too large Load Diff