From 2101a2c0236ce20365c439c5f9777c36dabf4cc8 Mon Sep 17 00:00:00 2001 From: Levi Neely <141506390+lneely@users.noreply.github.com> Date: Sat, 16 May 2026 18:56:39 +0000 Subject: [PATCH] agent: add MaxSteps override in AgentCoreConfig and /maxsteps command - AgentCoreConfig.MaxSteps: when non-zero, overrides the value loaded from the agent JSON (cfg.Env.maxSteps). Zero means "use the agent JSON value, or unlimited if absent". Callers (9p frontend, httpgw, CLI) can now inject a per-session limit at construction time without touching any agent config file. - /maxsteps [n] command: shows the current limit (0 = unlimited) or sets it live during a session. Follows the same pattern as /model. Added to /help listing. --- pkg/agent/commands.go | 25 +++++++++++++++++++++++++ pkg/agent/core.go | 9 +++++++++ 2 files changed, 34 insertions(+) diff --git a/pkg/agent/commands.go b/pkg/agent/commands.go index f8b40a6..45f5558 100644 --- a/pkg/agent/commands.go +++ b/pkg/agent/commands.go @@ -7,6 +7,7 @@ import ( "os" "os/exec" "slices" + "strconv" "strings" "ollie/pkg/config" @@ -130,6 +131,28 @@ func (s *agent) handleCommand(ctx context.Context, input string, handler EventHa handler(infoEvent("switched model to: " + args[0])) }, + "/maxsteps": func(args []string) { + if len(args) == 0 { + if s.cfg.MaxSteps == 0 { + handler(infoEvent("maxsteps: unlimited")) + } else { + handler(infoEvent(fmt.Sprintf("maxsteps: %d", s.cfg.MaxSteps))) + } + return + } + n, err := strconv.Atoi(args[0]) + if err != nil || n < 0 { + handler(infoEvent("error: maxsteps requires a non-negative integer (0 = unlimited)")) + return + } + s.cfg.MaxSteps = n + if n == 0 { + handler(infoEvent("maxsteps: unlimited")) + } else { + handler(infoEvent(fmt.Sprintf("maxsteps: %d", n))) + } + }, + "/agents": func(args []string) { seen := make(map[string]bool) found := false @@ -190,6 +213,7 @@ func (s *agent) handleCommand(ctx context.Context, input string, handler EventHa s.cfg.Tools = env.tools s.cfg.Exec = env.exec s.cfg.GenerationParams = env.genParams + s.cfg.MaxSteps = env.maxSteps s.agentName = name s.session = nil s.sessionID = NewSessionID() @@ -374,6 +398,7 @@ func (s *agent) handleCommand(ctx context.Context, input string, handler EventHa " /backend [type] - show current backend, or switch to ", " /model [name] - show current model, or switch to ", " /models - list available models", + " /maxsteps [n] - show or set max tool-call steps (0 = unlimited)", " /skills - list available skills", " /tools - list available tools", " /cwd [path] - show or change working directory", diff --git a/pkg/agent/core.go b/pkg/agent/core.go index fb82793..04c51be 100644 --- a/pkg/agent/core.go +++ b/pkg/agent/core.go @@ -198,6 +198,9 @@ type AgentCoreConfig struct { NewDispatcher func() tools.Dispatcher NewBackend func(string) (backend.Backend, error) // if nil, defaults to backend.NewWithName Log *olog.Logger // if nil, logging is disabled + // MaxSteps overrides the agent JSON maxSteps when non-zero. + // 0 means use the value from the agent config (or unlimited if absent). + MaxSteps int } // agent is the Core implementation. It owns all agent and session state @@ -323,6 +326,12 @@ func NewAgentCore(cfg AgentCoreConfig) Core { GenerationParams: cfg.Env.genParams, MaxSteps: cfg.Env.maxSteps, } + // A non-zero MaxSteps in AgentCoreConfig takes precedence over the + // value loaded from the agent JSON, allowing callers to impose a + // per-session limit at construction time (e.g. via a CLI flag). + if cfg.MaxSteps > 0 { + run.MaxSteps = cfg.MaxSteps + } if cfg.SessionID != "" { os.MkdirAll(filepath.Join(ollieTmpDir(), cfg.SessionID), 0700) //nolint:errcheck }