From cf894920fac5cba767f99d0f886d4bb0cd10b0a2 Mon Sep 17 00:00:00 2001 From: Levi Neely Date: Wed, 29 Jul 2026 19:25:27 +0200 Subject: [PATCH] session: define Core interface at consumer, use concrete *session.Session Core interface defined in 9p/session/core.go lists the methods the 9P layer needs. The real *session.Session satisfies it implicitly. Test stubs continue to work via interface satisfaction. --- main_test.go | 6 ++--- session/core.go | 55 ++++++++++++++++++++++++++++++++++++++++++++++ session/session.go | 8 +++---- 3 files changed, 62 insertions(+), 7 deletions(-) create mode 100644 session/core.go diff --git a/main_test.go b/main_test.go index 3f769d7..7babe14 100644 --- a/main_test.go +++ b/main_test.go @@ -165,7 +165,7 @@ func newTestSessionManagerWithCore(t *testing.T) *session.Manager { Sink: sink, ReadFile: func(string) ([]byte, error) { return []byte("#!/bin/sh\n"), nil }, MkdirAll: func(string, os.FileMode) error { return nil }, - NewCore: func(sessionID, agentName, cwd string) (agent.Session, error) { + NewCore: func(sessionID, agentName, cwd string) (session.Core, error) { return &stubCore{state: "idle", backend_: "stub", model: "m", agentName: agentName, cwd: cwd}, nil }, }) @@ -1221,7 +1221,7 @@ func TestSessionManagerCreateSessionEnvExpansion(t *testing.T) { Sink: sink, ReadFile: func(string) ([]byte, error) { return []byte("#!/bin/sh\n"), nil }, MkdirAll: func(string, os.FileMode) error { return nil }, - NewCore: func(sessionID, agentName, cwd string) (agent.Session, error) { + NewCore: func(sessionID, agentName, cwd string) (session.Core, error) { gotCwd = cwd return &stubCore{state: "idle", backend_: "stub", model: "m", agentName: agentName, cwd: cwd}, nil }, @@ -1248,7 +1248,7 @@ func TestSessionManagerCreateSessionTildeExpansion(t *testing.T) { Sink: sink, ReadFile: func(string) ([]byte, error) { return []byte("#!/bin/sh\n"), nil }, MkdirAll: func(string, os.FileMode) error { return nil }, - NewCore: func(sessionID, agentName, cwd string) (agent.Session, error) { + NewCore: func(sessionID, agentName, cwd string) (session.Core, error) { gotCwd = cwd return &stubCore{state: "idle", backend_: "stub", model: "m", agentName: agentName, cwd: cwd}, nil }, diff --git a/session/core.go b/session/core.go new file mode 100644 index 0000000..e95bcd7 --- /dev/null +++ b/session/core.go @@ -0,0 +1,55 @@ +package session + +import ( + "context" + + "github.com/simonfxr/pubsub" + agent "ollie/session" + "ollie/backend" +) + +// Core is the interface that a session implementation must satisfy. +// Defined here (at the consumer) rather than in ollie/session so that +// the core package exports only a concrete struct with no interface overhead. +// Tests can substitute a lightweight stub. +type Core interface { + Submit(ctx context.Context, input string) + Interrupt(cause error) bool + Inject(prompt string) + Queue(prompt string) + PopQueue() (string, bool) + Bus() *pubsub.Bus + IsRunning() bool + State() string + Reply() string + AgentName() string + BackendName() string + ModelName() string + CWD() string + SetCWD(dir string) error + SetSessionID(newID string) error + Context() []backend.Message + SystemPrompt() string + GenerationParams() backend.GenerationParams + SetGenerationParams(params backend.GenerationParams) error + CompactionModel() string + SetCompactionModel(model string) + Usage() string + Cost() string + CtxSz() string + ListModels() string + WaitChange(ctx context.Context, field, current string) (string, bool) + SaveSession(path string) error + Close() + Detach() bool + ListDetached() []agent.DetachedInfo + SignalDetached(pid, signal int) error + GetDetachedOutput(pid int) (string, error) + DismissDetached(pid int) bool + InjectSystemEvent(content string) + React(emoji string) + ReactTo(responseID, emoji string) error + Reactions() map[string]string + ToolCallCount() int64 + SetEnv(key, value string) +} diff --git a/session/session.go b/session/session.go index 1882fd9..c2d73bd 100644 --- a/session/session.go +++ b/session/session.go @@ -29,7 +29,7 @@ type Session struct { mu sync.RWMutex id string uname string // immutable user principal (numeric UID), set at creation - Core agent.Session + Core Core Ctx context.Context cancel context.CancelFunc log []byte @@ -46,7 +46,7 @@ type Session struct { modelsCacheAt time.Time } -func NewSession(id string, core agent.Session, ctx context.Context, cancel context.CancelFunc) *Session { +func NewSession(id string, core Core, ctx context.Context, cancel context.CancelFunc) *Session { sess := &Session{id: id, Core: core, Ctx: ctx, cancel: cancel} sess.startEventLog() return sess @@ -236,7 +236,7 @@ type ManagerConfig struct { MkdirAll func(string, os.FileMode) error // NewCore, if non-nil, replaces the default backend.New + agent.New // path. It receives the session ID, agent name, and cwd, and returns a Core. - NewCore func(sessionID, agentName, cwd string) (agent.Session, error) + NewCore func(sessionID, agentName, cwd string) (Core, error) // Strict rejects inline code steps; only tool steps are allowed. Strict bool // Yolo skips the landrun sandbox. @@ -1122,7 +1122,7 @@ func (s *Manager) CreateSession(args []string) (string, error) { return "", fmt.Errorf("session already exists: %s", sessID) } - var core agent.Session + var core Core var sessPtr *Session uname := s.nextUname() if s.cfg.NewCore != nil {