From 8e7dd60a8bad66c607df2272ee5672455c7ea75d Mon Sep 17 00:00:00 2001 From: ollie Date: Tue, 16 Jun 2026 12:30:36 +0200 Subject: [PATCH] agent: remove $OLLIE mount dependency from core - Add ListHandlers map to AgentCoreConfig/agent struct; /skills and /tools dispatch through it instead of reading local paths directly - Add ReadPlanStep to AgentCoreConfig; wired by 9p/ directly from sess.plan - Export NextUncheckedStep([]byte) as pure helper - Fix prependOlliePath to use OLLIE_CFG_PATH/scripts/x/ instead of $OLLIE/x/ - Add OLLIE_CFG_PATH to EnsureDefaults and managed env var list --- pkg/agent/commands.go | 22 ++++++---------------- pkg/agent/core.go | 23 +++++++++++++++++------ pkg/env/env.go | 2 ++ pkg/tools/execute/server.go | 16 ++++++---------- 4 files changed, 31 insertions(+), 32 deletions(-) diff --git a/pkg/agent/commands.go b/pkg/agent/commands.go index 9b6fa3f..3f29e11 100644 --- a/pkg/agent/commands.go +++ b/pkg/agent/commands.go @@ -25,20 +25,10 @@ func (s *agent) handleCommand(ctx context.Context, input string) bool { cmd := parts[0] args := parts[1:] - listMountDir := func(subdir string) { - mount := os.Getenv("OLLIE") - if mount == "" { - home, _ := os.UserHomeDir() - mount = home + "/mnt/ollie" - } - entries, err := os.ReadDir(mount + "/" + subdir) - if err != nil { - s.emit(infoEvent(fmt.Sprintf("%s: %v", cmd, err))) - return - } - for _, e := range entries { - if !e.IsDir() { - s.emit(infoEvent(" " + e.Name())) + listFromHandler := func(name string) { + if h := s.listHandlers[name]; h != nil { + for _, item := range h() { + s.emit(infoEvent(" " + item)) } } } @@ -423,8 +413,8 @@ func (s *agent) handleCommand(ctx context.Context, input string) bool { s.emit(infoEvent("cwd: " + dir)) }, - "/skills": func(args []string) { listMountDir("sk") }, - "/tools": func(args []string) { listMountDir("t") }, + "/skills": func(args []string) { listFromHandler("skills") }, + "/tools": func(args []string) { listFromHandler("tools") }, "/sp": func(args []string) { s.emit(infoEvent(s.runtime.Preamble)) diff --git a/pkg/agent/core.go b/pkg/agent/core.go index fe9d4cc..b275d73 100644 --- a/pkg/agent/core.go +++ b/pkg/agent/core.go @@ -235,6 +235,13 @@ type AgentCoreConfig struct { // MaxSteps overrides the agent JSON maxSteps when non-zero. // 0 means use the value from the agent config (or unlimited if absent). MaxSteps int + // ReadPlanStep, if non-nil, is called to get the next unchecked plan step. + // When provided, it is used directly instead of reading from the filesystem. + ReadPlanStep func() string + // ListHandlers provides harness-specific list implementations for slash + // commands such as /skills, /tools, /agents. Keyed by command name without + // the leading slash. Commands with no entry emit nothing. + ListHandlers map[string]func() []string } // agent is the Core implementation. It owns all agent and session state @@ -252,6 +259,7 @@ type agent struct { newDispatcher func() tools.Dispatcher newBackend func(string) (backend.Backend, error) readPlanStep func() string + listHandlers map[string]func() []string turnError func(ctx context.Context, errType, errMsg string) HookResult // overridable for tests cwd string startupMessages []string @@ -342,6 +350,11 @@ func readNextUnchecked(path string) string { if err != nil || len(data) == 0 { return "" } + return NextUncheckedStep(data) +} + +// NextUncheckedStep returns the first unchecked step from plan bytes. +func NextUncheckedStep(data []byte) string { for _, line := range strings.Split(string(data), "\n") { trimmed := strings.TrimSpace(line) if strings.HasPrefix(trimmed, "- [ ]") { @@ -385,12 +398,9 @@ func NewAgentCore(cfg AgentCoreConfig) Core { var readPlanStep func() string if cfg.SessionID != "" { os.MkdirAll(filepath.Join(ollieTmpDir(), cfg.SessionID), 0700) //nolint:errcheck - if mount := os.Getenv("OLLIE"); mount != "" { - planPath := filepath.Join(mount, "s", cfg.SessionID, "plan") - readPlanStep = func() string { - return readNextUnchecked(planPath) - } - } + } + if cfg.ReadPlanStep != nil { + readPlanStep = cfg.ReadPlanStep } log := cfg.Log @@ -413,6 +423,7 @@ func NewAgentCore(cfg AgentCoreConfig) Core { newDispatcher: cfg.NewDispatcher, newBackend: cfg.NewBackend, readPlanStep: readPlanStep, + listHandlers: cfg.ListHandlers, state: "idle", bus: pubsub.NewBus(), } diff --git a/pkg/env/env.go b/pkg/env/env.go index 27200c8..6ef63da 100644 --- a/pkg/env/env.go +++ b/pkg/env/env.go @@ -16,6 +16,7 @@ import ( // managed is the ordered list of env vars that ollie exposes to frontends. var managed = []string{ "OLLIE", + "OLLIE_CFG_PATH", "OLLIE_TOOLS_PATH", "OLLIE_AGENTS_PATH", "OLLIE_SKILLS_PATH", @@ -39,6 +40,7 @@ func EnsureDefaults() { } defaults := map[string]string{ "OLLIE": filepath.Join(home, "mnt", "ollie"), + "OLLIE_CFG_PATH": filepath.Join(home, ".config", "ollie"), "OLLIE_TOOLS_PATH": filepath.Join(home, ".config", "ollie", "tools"), "OLLIE_AGENTS_PATH": filepath.Join(home, ".config", "ollie", "agents"), "OLLIE_SKILLS_PATH": filepath.Join(home, ".config", "ollie", "skills"), diff --git a/pkg/tools/execute/server.go b/pkg/tools/execute/server.go index 92b5197..fb3b86b 100644 --- a/pkg/tools/execute/server.go +++ b/pkg/tools/execute/server.go @@ -643,11 +643,7 @@ func (e *Server) executeWithStdin(ctx context.Context, code, language string, ti filtered = append(filtered, ev) } } - olliePath := e.envExtra["OLLIE"] - if olliePath == "" { - olliePath = os.Getenv("OLLIE") - } - cmd.Env = prependOlliePath(filtered, olliePath) + cmd.Env = prependOlliePath(filtered, paths.CfgDir()) cmd.Env = append(cmd.Env, "OLLIE_TOOLS_PATH="+ToolsPath()) for k, v := range e.envExtra { cmd.Env = append(cmd.Env, k+"="+v) @@ -682,13 +678,13 @@ func (e *Server) executeWithStdin(ctx context.Context, code, language string, ti return string(output), nil } -// prependOlliePath returns env with $OLLIE/x/ prepended to PATH so wrapper -// scripts placed there (e.g. ~/.config/ollie/scripts/x/) shadow system binaries. -func prependOlliePath(env []string, ollie string) []string { - if ollie == "" { +// prependOlliePath returns env with $OLLIE_CFG_PATH/scripts/x/ prepended to PATH so wrapper +// scripts placed there shadow system binaries. +func prependOlliePath(env []string, cfgDir string) []string { + if cfgDir == "" { return env } - xdir := filepath.Join(ollie, "x") + xdir := filepath.Join(cfgDir, "scripts", "x") result := make([]string, 0, len(env)) for _, e := range env { if strings.HasPrefix(e, "PATH=") {