all: replace toolsrv.Runner interface with *toolsrv.Conn
The agent always talks to a *Conn (never a *Server directly). Replace the Runner interface with the concrete type throughout: - Runtime.ToolServer is now *toolsrv.Conn - BuildRuntime takes *toolsrv.Conn - session.Config, SessionInfra, InfraConfig use *toolsrv.Conn - All inline type assertions in agent.go deleted — methods called directly on *Conn (SetEnv, SetCWD, Close, Detach, ListDetachedRaw, SignalDetached, GetDetachedOutput, DismissDetached, SetAllowTools, SetOnToolsChanged, Ping, ToolRegistryRevision) - Added Conn.ToolRegistryRevision() that returns 0 (remote connections don't support revision tracking; use push-based OnToolsChanged instead) - Deleted the Runner interface and its test - Renamed LoadToolOnRunner → LoadToolOnConn
This commit is contained in:
parent
3b83b9d373
commit
06a996e967
108
agent/agent.go
108
agent/agent.go
|
|
@ -28,7 +28,7 @@ type Agent struct {
|
||||||
systemPrompt string // system prompt for /agent reloads
|
systemPrompt string // system prompt for /agent reloads
|
||||||
envBlock string // environment block for /agent reloads
|
envBlock string // environment block for /agent reloads
|
||||||
promptEnvExtra []string // PRIME_* vars for prompt resolution
|
promptEnvExtra []string // PRIME_* vars for prompt resolution
|
||||||
newToolServer func() toolsrv.Runner
|
newToolServer func() *toolsrv.Conn
|
||||||
newBackend func(string) (backend.Backend, error)
|
newBackend func(string) (backend.Backend, error)
|
||||||
currentAction atomic.Pointer[actionHandle]
|
currentAction atomic.Pointer[actionHandle]
|
||||||
warnedContext bool
|
warnedContext bool
|
||||||
|
|
@ -239,7 +239,7 @@ func (ag *Agent) SetOutput(h EventHandler) {
|
||||||
|
|
||||||
// SetToolServer updates the tool server connection and factory.
|
// SetToolServer updates the tool server connection and factory.
|
||||||
// Used when resuming a paused session that was restored without infra.
|
// Used when resuming a paused session that was restored without infra.
|
||||||
func (ag *Agent) SetToolServer(newToolServer func() toolsrv.Runner, conn toolsrv.Runner) {
|
func (ag *Agent) SetToolServer(newToolServer func() *toolsrv.Conn, conn *toolsrv.Conn) {
|
||||||
ag.newToolServer = newToolServer
|
ag.newToolServer = newToolServer
|
||||||
if ag.runtime != nil {
|
if ag.runtime != nil {
|
||||||
ag.runtime.ToolServer = conn
|
ag.runtime.ToolServer = conn
|
||||||
|
|
@ -316,16 +316,12 @@ func (ag *Agent) SetSessionEnv(sessionID string) {
|
||||||
if ag.runtime.ToolServer == nil {
|
if ag.runtime.ToolServer == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if srv := ag.runtime.ToolServer; srv != nil {
|
ag.runtime.ToolServer.SetEnv("OLLIE_SESSION_ID", sessionID)
|
||||||
if es, ok := srv.(interface{ SetEnv(string, string) }); ok {
|
if ag.id != "" {
|
||||||
es.SetEnv("OLLIE_SESSION_ID", sessionID)
|
ag.runtime.ToolServer.SetEnv("OLLIE_UNAME", ag.id)
|
||||||
if ag.id != "" {
|
}
|
||||||
es.SetEnv("OLLIE_UNAME", ag.id)
|
if u := os.Getenv("USER"); u != "" {
|
||||||
}
|
ag.runtime.ToolServer.SetEnv("USER", u)
|
||||||
if u := os.Getenv("USER"); u != "" {
|
|
||||||
es.SetEnv("USER", u)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -334,11 +330,7 @@ func (ag *Agent) SetEnv(key, value string) {
|
||||||
if ag.runtime.ToolServer == nil {
|
if ag.runtime.ToolServer == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if srv := ag.runtime.ToolServer; srv != nil {
|
ag.runtime.ToolServer.SetEnv(key, value)
|
||||||
if es, ok := srv.(interface{ SetEnv(string, string) }); ok {
|
|
||||||
es.SetEnv(key, value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close releases agent resources (dispatcher, execute server).
|
// Close releases agent resources (dispatcher, execute server).
|
||||||
|
|
@ -346,11 +338,7 @@ func (ag *Agent) Close() {
|
||||||
if ag.runtime.ToolServer == nil {
|
if ag.runtime.ToolServer == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if srv := ag.runtime.ToolServer; srv != nil {
|
ag.runtime.ToolServer.Close()
|
||||||
if c, ok := srv.(interface{ Close() }); ok {
|
|
||||||
c.Close()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetCWD updates the agent's working directory, preamble references, and dispatcher.
|
// SetCWD updates the agent's working directory, preamble references, and dispatcher.
|
||||||
|
|
@ -363,11 +351,7 @@ func (ag *Agent) SetCWD(dir string) {
|
||||||
ag.runtime.Preamble = strings.ReplaceAll(ag.runtime.Preamble, oldCwd, dir)
|
ag.runtime.Preamble = strings.ReplaceAll(ag.runtime.Preamble, oldCwd, dir)
|
||||||
}
|
}
|
||||||
if ag.runtime.ToolServer != nil {
|
if ag.runtime.ToolServer != nil {
|
||||||
if srv := ag.runtime.ToolServer; srv != nil {
|
ag.runtime.ToolServer.SetCWD(dir)
|
||||||
if ws, ok := srv.(interface{ SetCWD(string) }); ok {
|
|
||||||
ws.SetCWD(dir)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
ag.notifyChange()
|
ag.notifyChange()
|
||||||
}
|
}
|
||||||
|
|
@ -466,27 +450,18 @@ func (ag *Agent) wireToolsChanged() {
|
||||||
if ag.runtime == nil || ag.runtime.ToolServer == nil {
|
if ag.runtime == nil || ag.runtime.ToolServer == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
type hasHook interface {
|
ag.runtime.ToolServer.SetOnToolsChanged(ag.refreshToolListing)
|
||||||
SetOnToolsChanged(func(string))
|
|
||||||
}
|
|
||||||
if srv, ok := ag.runtime.ToolServer.(hasHook); ok {
|
|
||||||
srv.SetOnToolsChanged(ag.refreshToolListing)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// toolsNeedRefresh reports whether the tool registry has changed since the
|
// toolsNeedRefresh reports whether the tool registry has changed since the
|
||||||
// last time Tools was populated. If the tool server doesn't support revision
|
// last time Tools was populated. If the tool server doesn't support revision
|
||||||
// tracking (e.g. remote Conn), this always returns true (safe fallback).
|
// tracking (e.g. remote Conn returns 0), this always returns true (safe fallback).
|
||||||
func (ag *Agent) toolsNeedRefresh() bool {
|
func (ag *Agent) toolsNeedRefresh() bool {
|
||||||
type revisionProvider interface {
|
if ag.runtime.ToolServer == nil {
|
||||||
ToolRegistryRevision() uint64
|
return true
|
||||||
}
|
}
|
||||||
srv, ok := ag.runtime.ToolServer.(revisionProvider)
|
rev := ag.runtime.ToolServer.ToolRegistryRevision()
|
||||||
if !ok {
|
if rev == 0 || rev != ag.runtime.toolRegistryRevision {
|
||||||
return true // no revision support — always refetch
|
|
||||||
}
|
|
||||||
rev := srv.ToolRegistryRevision()
|
|
||||||
if rev != ag.runtime.toolRegistryRevision {
|
|
||||||
ag.runtime.toolRegistryRevision = rev
|
ag.runtime.toolRegistryRevision = rev
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
@ -577,7 +552,7 @@ func (ag *Agent) ListModels() []string {
|
||||||
|
|
||||||
// ToolServer returns the tool execution server, or nil if unavailable.
|
// ToolServer returns the tool execution server, or nil if unavailable.
|
||||||
// Exported for use by the 9P filesystem layer to sync tool registries.
|
// Exported for use by the 9P filesystem layer to sync tool registries.
|
||||||
func (ag *Agent) ToolServer() toolsrv.Runner {
|
func (ag *Agent) ToolServer() *toolsrv.Conn {
|
||||||
return ag.runtime.ToolServer
|
return ag.runtime.ToolServer
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -600,12 +575,10 @@ func (ag *Agent) BroadcastChange() {
|
||||||
|
|
||||||
// Detach detaches the current running process to background.
|
// Detach detaches the current running process to background.
|
||||||
func (ag *Agent) Detach() bool {
|
func (ag *Agent) Detach() bool {
|
||||||
if srv := ag.runtime.ToolServer; srv != nil {
|
if ag.runtime.ToolServer == nil {
|
||||||
if d, ok := srv.(interface{ Detach() bool }); ok {
|
return false
|
||||||
return d.Detach()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return false
|
return ag.runtime.ToolServer.Detach()
|
||||||
}
|
}
|
||||||
|
|
||||||
// DetachedInfo describes a detached process.
|
// DetachedInfo describes a detached process.
|
||||||
|
|
@ -619,16 +592,10 @@ type DetachedInfo struct {
|
||||||
|
|
||||||
// ListDetached returns info about all detached processes.
|
// ListDetached returns info about all detached processes.
|
||||||
func (ag *Agent) ListDetached() []DetachedInfo {
|
func (ag *Agent) ListDetached() []DetachedInfo {
|
||||||
srv := ag.runtime.ToolServer
|
if ag.runtime.ToolServer == nil {
|
||||||
if srv == nil {
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
type listDetacher interface{ ListDetachedRaw() []any }
|
raw := ag.runtime.ToolServer.ListDetachedRaw()
|
||||||
ld, ok := srv.(listDetacher)
|
|
||||||
if !ok {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
raw := ld.ListDetachedRaw()
|
|
||||||
out := make([]DetachedInfo, 0, len(raw))
|
out := make([]DetachedInfo, 0, len(raw))
|
||||||
for _, r := range raw {
|
for _, r := range raw {
|
||||||
if m, ok := r.(map[string]any); ok {
|
if m, ok := r.(map[string]any); ok {
|
||||||
|
|
@ -656,35 +623,24 @@ func (ag *Agent) ListDetached() []DetachedInfo {
|
||||||
|
|
||||||
// SignalDetached sends a signal to a detached process.
|
// SignalDetached sends a signal to a detached process.
|
||||||
func (ag *Agent) SignalDetached(pid int, signal int) error {
|
func (ag *Agent) SignalDetached(pid int, signal int) error {
|
||||||
if srv := ag.runtime.ToolServer; srv != nil {
|
if ag.runtime.ToolServer == nil {
|
||||||
type signaler interface {
|
return fmt.Errorf("no execute server available")
|
||||||
SignalDetached(int, syscall.Signal) error
|
|
||||||
}
|
|
||||||
if sg, ok := srv.(signaler); ok {
|
|
||||||
return sg.SignalDetached(pid, syscall.Signal(signal))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return fmt.Errorf("no execute server available")
|
return ag.runtime.ToolServer.SignalDetached(pid, syscall.Signal(signal))
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetDetachedOutput reads output from a detached process.
|
// GetDetachedOutput reads output from a detached process.
|
||||||
func (ag *Agent) GetDetachedOutput(pid int) (string, error) {
|
func (ag *Agent) GetDetachedOutput(pid int) (string, error) {
|
||||||
if srv := ag.runtime.ToolServer; srv != nil {
|
if ag.runtime.ToolServer == nil {
|
||||||
type outputGetter interface{ GetDetachedOutput(int) (string, error) }
|
return "", fmt.Errorf("no execute server available")
|
||||||
if og, ok := srv.(outputGetter); ok {
|
|
||||||
return og.GetDetachedOutput(pid)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return "", fmt.Errorf("no execute server available")
|
return ag.runtime.ToolServer.GetDetachedOutput(pid)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DismissDetached removes a finished detached process.
|
// DismissDetached removes a finished detached process.
|
||||||
func (ag *Agent) DismissDetached(pid int) bool {
|
func (ag *Agent) DismissDetached(pid int) bool {
|
||||||
if srv := ag.runtime.ToolServer; srv != nil {
|
if ag.runtime.ToolServer == nil {
|
||||||
type dismisser interface{ DismissDetached(int) bool }
|
return false
|
||||||
if d, ok := srv.(dismisser); ok {
|
|
||||||
return d.DismissDetached(pid)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return false
|
return ag.runtime.ToolServer.DismissDetached(pid)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ import (
|
||||||
// env provides additional environment variables injected into prompt resolution
|
// env provides additional environment variables injected into prompt resolution
|
||||||
// subprocesses (e.g. OLLIE_SESSION_ID=xxx).
|
// subprocesses (e.g. OLLIE_SESSION_ID=xxx).
|
||||||
// The caller is responsible for registering all servers on d before calling this.
|
// The caller is responsible for registering all servers on d before calling this.
|
||||||
func BuildRuntime(cfg *AgentConfig, srv toolsrv.Runner, cwd string, env []string, systemPrompt, envBlock string) *Runtime {
|
func BuildRuntime(cfg *AgentConfig, srv *toolsrv.Conn, cwd string, env []string, systemPrompt, envBlock string) *Runtime {
|
||||||
var messages []string
|
var messages []string
|
||||||
|
|
||||||
var allToolInfos []toolsrv.ToolInfo
|
var allToolInfos []toolsrv.ToolInfo
|
||||||
|
|
@ -45,10 +45,8 @@ func BuildRuntime(cfg *AgentConfig, srv toolsrv.Runner, cwd string, env []string
|
||||||
}
|
}
|
||||||
genParams = cfg.GenParams()
|
genParams = cfg.GenParams()
|
||||||
maxSteps = cfg.MaxSteps
|
maxSteps = cfg.MaxSteps
|
||||||
if len(cfg.AllowTools) > 0 {
|
if len(cfg.AllowTools) > 0 && srv != nil {
|
||||||
if rs, ok := srv.(interface{ SetAllowTools([]string) }); ok {
|
srv.SetAllowTools(cfg.AllowTools)
|
||||||
rs.SetAllowTools(cfg.AllowTools)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ type AgentCfg struct {
|
||||||
SystemPrompt string
|
SystemPrompt string
|
||||||
EnvBlock string
|
EnvBlock string
|
||||||
PromptEnvExtra []string
|
PromptEnvExtra []string
|
||||||
NewToolServer func() toolsrv.Runner
|
NewToolServer func() *toolsrv.Conn
|
||||||
NewBackend func(string) (backend.Backend, error)
|
NewBackend func(string) (backend.Backend, error)
|
||||||
Output EventHandler
|
Output EventHandler
|
||||||
Log *olog.Logger
|
Log *olog.Logger
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ import (
|
||||||
// agents replaces it atomically.
|
// agents replaces it atomically.
|
||||||
type Runtime struct {
|
type Runtime struct {
|
||||||
Backend backend.Backend
|
Backend backend.Backend
|
||||||
ToolServer toolsrv.Runner // the execute server (tool runtime, env, cwd)
|
ToolServer *toolsrv.Conn // the tool server connection (tool runtime, env, cwd)
|
||||||
Preamble string // compiled system prompt
|
Preamble string // compiled system prompt
|
||||||
Tools []backend.Tool
|
Tools []backend.Tool
|
||||||
ToolMeta map[string]toolsrv.ToolInfo // keyed by tool name — source of truth for all per-tool metadata
|
ToolMeta map[string]toolsrv.ToolInfo // keyed by tool name — source of truth for all per-tool metadata
|
||||||
|
|
|
||||||
|
|
@ -65,7 +65,7 @@ func (s *SessionNode) Ctx() context.Context {
|
||||||
return s.Core.Ctx()
|
return s.Core.Ctx()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SessionNode) ToolsConn() toolsrv.Runner {
|
func (s *SessionNode) ToolsConn() *toolsrv.Conn {
|
||||||
return s.Core.ToolsConn()
|
return s.Core.ToolsConn()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -277,8 +277,8 @@ func restoreMultiAgentSession(ps *PersistedSession) (*RestoredSession, error) {
|
||||||
env := []string{"OLLIE_SESSION_ID=" + ps.ID, "OLLIE_UNAME=" + pa.ID}
|
env := []string{"OLLIE_SESSION_ID=" + ps.ID, "OLLIE_UNAME=" + pa.ID}
|
||||||
env = append(env, promptEnv...)
|
env = append(env, promptEnv...)
|
||||||
|
|
||||||
var toolsConn toolsrv.Runner
|
var toolsConn *toolsrv.Conn
|
||||||
var newToolServer func() toolsrv.Runner
|
var newToolServer func() *toolsrv.Conn
|
||||||
if infra != nil {
|
if infra != nil {
|
||||||
toolsConn = infra.ToolsConn
|
toolsConn = infra.ToolsConn
|
||||||
newToolServer = infra.NewToolServer
|
newToolServer = infra.NewToolServer
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@ type Config struct {
|
||||||
CWD string
|
CWD string
|
||||||
History *agent.History
|
History *agent.History
|
||||||
Runtime *agent.Runtime
|
Runtime *agent.Runtime
|
||||||
NewToolServer func() toolsrv.Runner
|
NewToolServer func() *toolsrv.Conn
|
||||||
NewBackend func(string) (backend.Backend, error)
|
NewBackend func(string) (backend.Backend, error)
|
||||||
Log *olog.Logger
|
Log *olog.Logger
|
||||||
MaxSteps int
|
MaxSteps int
|
||||||
|
|
@ -72,7 +72,7 @@ type Session struct {
|
||||||
// Tool server lifecycle
|
// Tool server lifecycle
|
||||||
proc *toolsrv.Process // toolsrv subprocess
|
proc *toolsrv.Process // toolsrv subprocess
|
||||||
keeper *toolsrv.ProcessKeeper // resilient process manager
|
keeper *toolsrv.ProcessKeeper // resilient process manager
|
||||||
toolsConn toolsrv.Runner // multiplexed RPC connection
|
toolsConn *toolsrv.Conn // multiplexed RPC connection
|
||||||
paused bool // true when paused
|
paused bool // true when paused
|
||||||
|
|
||||||
// Tool restrictions
|
// Tool restrictions
|
||||||
|
|
@ -426,14 +426,14 @@ func (s *Session) SetKeeper(keeper *toolsrv.ProcessKeeper) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ToolsConn returns the tool server connection.
|
// ToolsConn returns the tool server connection.
|
||||||
func (s *Session) ToolsConn() toolsrv.Runner {
|
func (s *Session) ToolsConn() *toolsrv.Conn {
|
||||||
s.mu.RLock()
|
s.mu.RLock()
|
||||||
defer s.mu.RUnlock()
|
defer s.mu.RUnlock()
|
||||||
return s.toolsConn
|
return s.toolsConn
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetToolsConn sets the tool server connection.
|
// SetToolsConn sets the tool server connection.
|
||||||
func (s *Session) SetToolsConn(conn toolsrv.Runner) {
|
func (s *Session) SetToolsConn(conn *toolsrv.Conn) {
|
||||||
s.mu.Lock()
|
s.mu.Lock()
|
||||||
defer s.mu.Unlock()
|
defer s.mu.Unlock()
|
||||||
s.toolsConn = conn
|
s.toolsConn = conn
|
||||||
|
|
@ -463,10 +463,7 @@ func (s *Session) IsConnected() bool {
|
||||||
if paused || conn == nil {
|
if paused || conn == nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if pinger, ok := conn.(interface{ Ping() error }); ok {
|
return conn.Ping() == nil
|
||||||
return pinger.Ping() == nil
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pause stops the tool server to save resources.
|
// Pause stops the tool server to save resources.
|
||||||
|
|
@ -490,9 +487,7 @@ func (s *Session) Pause() error {
|
||||||
}
|
}
|
||||||
// Close any existing connection.
|
// Close any existing connection.
|
||||||
if s.toolsConn != nil {
|
if s.toolsConn != nil {
|
||||||
if c, ok := s.toolsConn.(interface{ Close() }); ok {
|
s.toolsConn.Close()
|
||||||
c.Close()
|
|
||||||
}
|
|
||||||
s.toolsConn = nil
|
s.toolsConn = nil
|
||||||
}
|
}
|
||||||
s.paused = true
|
s.paused = true
|
||||||
|
|
@ -591,24 +586,24 @@ func (s *Session) LoadTool(name string, ag *agent.Agent) error {
|
||||||
if blocked {
|
if blocked {
|
||||||
return fmt.Errorf("tool %q is disallowed for this session", name)
|
return fmt.Errorf("tool %q is disallowed for this session", name)
|
||||||
}
|
}
|
||||||
var runner toolsrv.Runner
|
var conn *toolsrv.Conn
|
||||||
if s.toolsConn != nil {
|
if s.toolsConn != nil {
|
||||||
runner = s.toolsConn
|
conn = s.toolsConn
|
||||||
} else if ag != nil {
|
} else if ag != nil {
|
||||||
runner = ag.ToolServer()
|
conn = ag.ToolServer()
|
||||||
}
|
}
|
||||||
return LoadToolOnRunner(runner, name)
|
return LoadToolOnConn(conn, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
// LoadToolOnRunner loads a tool on the given runner.
|
// LoadToolOnConn loads a tool on the given connection.
|
||||||
func LoadToolOnRunner(runner toolsrv.Runner, name string) error {
|
func LoadToolOnConn(conn *toolsrv.Conn, name string) error {
|
||||||
if runner == nil {
|
if conn == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
args, _ := json.Marshal(map[string]string{"name": name})
|
args, _ := json.Marshal(map[string]string{"name": name})
|
||||||
if _, err := runner.CallTool(ctx, "tool_load", json.RawMessage(args)); err != nil {
|
if _, err := conn.CallTool(ctx, "tool_load", json.RawMessage(args)); err != nil {
|
||||||
return fmt.Errorf("remote load: %w", err)
|
return fmt.Errorf("remote load: %w", err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|
|
||||||
|
|
@ -15,8 +15,8 @@ import (
|
||||||
type SessionInfra struct {
|
type SessionInfra struct {
|
||||||
Proc *toolsrv.Process
|
Proc *toolsrv.Process
|
||||||
Keeper *toolsrv.ProcessKeeper
|
Keeper *toolsrv.ProcessKeeper
|
||||||
ToolsConn toolsrv.Runner
|
ToolsConn *toolsrv.Conn
|
||||||
NewToolServer func() toolsrv.Runner
|
NewToolServer func() *toolsrv.Conn
|
||||||
PromptEnv []string
|
PromptEnv []string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -36,7 +36,7 @@ type ToolServerConfig struct {
|
||||||
type InfraConfig struct {
|
type InfraConfig struct {
|
||||||
Proc *toolsrv.Process
|
Proc *toolsrv.Process
|
||||||
Keeper *toolsrv.ProcessKeeper
|
Keeper *toolsrv.ProcessKeeper
|
||||||
ToolsConn toolsrv.Runner
|
ToolsConn *toolsrv.Conn
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetupToolServer spawns the tool server process and creates the infrastructure.
|
// SetupToolServer spawns the tool server process and creates the infrastructure.
|
||||||
|
|
@ -47,9 +47,9 @@ func SetupToolServer(cfg ToolServerConfig) (*SessionInfra, error) {
|
||||||
// Reuse existing infrastructure if provided.
|
// Reuse existing infrastructure if provided.
|
||||||
if cfg.ReuseFrom != nil {
|
if cfg.ReuseFrom != nil {
|
||||||
r := cfg.ReuseFrom
|
r := cfg.ReuseFrom
|
||||||
var newToolServer func() toolsrv.Runner
|
var newToolServer func() *toolsrv.Conn
|
||||||
if r.Keeper != nil {
|
if r.Keeper != nil {
|
||||||
newToolServer = func() toolsrv.Runner {
|
newToolServer = func() *toolsrv.Conn {
|
||||||
conn, err := r.Keeper.Dial()
|
conn, err := r.Keeper.Dial()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil
|
return nil
|
||||||
|
|
@ -57,13 +57,13 @@ func SetupToolServer(cfg ToolServerConfig) (*SessionInfra, error) {
|
||||||
return conn
|
return conn
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
newToolServer = func() toolsrv.Runner {
|
newToolServer = func() *toolsrv.Conn {
|
||||||
return r.ToolsConn
|
return r.ToolsConn
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Update CWD if the connection supports it.
|
// Update CWD on the reused connection.
|
||||||
if cwc, ok := r.ToolsConn.(interface{ SetCWD(string) }); ok {
|
if r.ToolsConn != nil {
|
||||||
cwc.SetCWD(cfg.CWD)
|
r.ToolsConn.SetCWD(cfg.CWD)
|
||||||
}
|
}
|
||||||
return &SessionInfra{
|
return &SessionInfra{
|
||||||
Proc: r.Proc,
|
Proc: r.Proc,
|
||||||
|
|
@ -75,7 +75,7 @@ func SetupToolServer(cfg ToolServerConfig) (*SessionInfra, error) {
|
||||||
}
|
}
|
||||||
var proc *toolsrv.Process
|
var proc *toolsrv.Process
|
||||||
var keeper *toolsrv.ProcessKeeper
|
var keeper *toolsrv.ProcessKeeper
|
||||||
var newToolServer func() toolsrv.Runner
|
var newToolServer func() *toolsrv.Conn
|
||||||
var promptEnv []string
|
var promptEnv []string
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
|
|
@ -93,7 +93,7 @@ func SetupToolServer(cfg ToolServerConfig) (*SessionInfra, error) {
|
||||||
CWD: cfg.CWD,
|
CWD: cfg.CWD,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
newToolServer = func() toolsrv.Runner {
|
newToolServer = func() *toolsrv.Conn {
|
||||||
conn, err := keeper.Dial()
|
conn, err := keeper.Dial()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil
|
return nil
|
||||||
|
|
@ -117,7 +117,7 @@ func SetupToolServer(cfg ToolServerConfig) (*SessionInfra, error) {
|
||||||
keeper = toolsrv.NewProcessKeeper(cfg.Ctx, proc, func(ctx context.Context) (*toolsrv.Process, error) {
|
keeper = toolsrv.NewProcessKeeper(cfg.Ctx, proc, func(ctx context.Context) (*toolsrv.Process, error) {
|
||||||
return toolsrv.Spawn(ctx, cfg.CWD, dialOpts...)
|
return toolsrv.Spawn(ctx, cfg.CWD, dialOpts...)
|
||||||
})
|
})
|
||||||
newToolServer = func() toolsrv.Runner {
|
newToolServer = func() *toolsrv.Conn {
|
||||||
conn, err := keeper.Dial()
|
conn, err := keeper.Dial()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil
|
return nil
|
||||||
|
|
@ -178,24 +178,22 @@ func BuildPromptLayers(cfg *agent.AgentConfig, cwd, sessID, uname string, prompt
|
||||||
// LoadAutoLoadTools loads all tools from the agent config's autoLoad list.
|
// LoadAutoLoadTools loads all tools from the agent config's autoLoad list.
|
||||||
// It first sends the session ID and uname to the tool server so that
|
// It first sends the session ID and uname to the tool server so that
|
||||||
// ollie-remote can attach its tool registry before processing tool_load RPCs.
|
// ollie-remote can attach its tool registry before processing tool_load RPCs.
|
||||||
func LoadAutoLoadTools(cfg *agent.AgentConfig, runner toolsrv.Runner, sessID, uname string, logError func(string, ...any)) {
|
func LoadAutoLoadTools(cfg *agent.AgentConfig, conn *toolsrv.Conn, sessID, uname string, logError func(string, ...any)) {
|
||||||
if cfg == nil || runner == nil {
|
if cfg == nil || conn == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send session ID to the tool server first — ollie-remote uses this
|
// Send session ID to the tool server first — ollie-remote uses this
|
||||||
// to attach its tool registry via the OnEnvSet callback.
|
// to attach its tool registry via the OnEnvSet callback.
|
||||||
if es, ok := runner.(interface{ SetEnv(string, string) }); ok {
|
if sessID != "" {
|
||||||
if sessID != "" {
|
conn.SetEnv("OLLIE_SESSION_ID", sessID)
|
||||||
es.SetEnv("OLLIE_SESSION_ID", sessID)
|
}
|
||||||
}
|
if uname != "" {
|
||||||
if uname != "" {
|
conn.SetEnv("OLLIE_UNAME", uname)
|
||||||
es.SetEnv("OLLIE_UNAME", uname)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tl := range cfg.AutoLoad {
|
for _, tl := range cfg.AutoLoad {
|
||||||
if err := LoadToolOnRunner(runner, tl); err != nil {
|
if err := LoadToolOnConn(conn, tl); err != nil {
|
||||||
if logError != nil {
|
if logError != nil {
|
||||||
logError("autoLoad tool %q: %v", tl, err)
|
logError("autoLoad tool %q: %v", tl, err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -103,6 +103,10 @@ func (c *Conn) SetOnToolsChanged(fn func(string)) {
|
||||||
c.pendingMu.Unlock()
|
c.pendingMu.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ToolRegistryRevision returns 0 for remote connections (revision tracking
|
||||||
|
// is not supported over RPC; use SetOnToolsChanged for push-based refresh).
|
||||||
|
func (c *Conn) ToolRegistryRevision() uint64 { return 0 }
|
||||||
|
|
||||||
func (c *Conn) Detach() bool {
|
func (c *Conn) Detach() bool {
|
||||||
resp, err := c.call("detach", nil)
|
resp, err := c.call("detach", nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@
|
||||||
package toolsrv
|
package toolsrv
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -29,10 +28,3 @@ type ToolInfo struct {
|
||||||
// the soft step-budget guardrail.
|
// the soft step-budget guardrail.
|
||||||
ResetsCounter bool
|
ResetsCounter bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// Runner is the minimal interface satisfied by any tool server (local or remote).
|
|
||||||
// Consumers that need polymorphism over Server and RemoteServer use this.
|
|
||||||
type Runner interface {
|
|
||||||
ListTools() ([]ToolInfo, error)
|
|
||||||
CallTool(ctx context.Context, tool string, args json.RawMessage) (json.RawMessage, error)
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,53 +0,0 @@
|
||||||
package toolsrv_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"encoding/json"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"ollie/toolsrv"
|
|
||||||
)
|
|
||||||
|
|
||||||
// stubRunner is a minimal Runner used to verify the contract.
|
|
||||||
type stubRunner struct {
|
|
||||||
name string
|
|
||||||
tools []toolsrv.ToolInfo
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *stubRunner) ListTools() ([]toolsrv.ToolInfo, error) { return s.tools, nil }
|
|
||||||
func (s *stubRunner) CallTool(_ context.Context, tool string, _ json.RawMessage) (json.RawMessage, error) {
|
|
||||||
return json.RawMessage(`{"tool":"` + tool + `"}`), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func newStub(name string, toolNames ...string) *stubRunner {
|
|
||||||
var ti []toolsrv.ToolInfo
|
|
||||||
for _, n := range toolNames {
|
|
||||||
ti = append(ti, toolsrv.ToolInfo{Name: n, Description: n + " desc"})
|
|
||||||
}
|
|
||||||
return &stubRunner{name: name, tools: ti}
|
|
||||||
}
|
|
||||||
|
|
||||||
// checkRunnerContract verifies Runner invariants.
|
|
||||||
func checkRunnerContract(t *testing.T, r toolsrv.Runner) {
|
|
||||||
t.Helper()
|
|
||||||
tl, err := r.ListTools()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("ListTools: %v", err)
|
|
||||||
}
|
|
||||||
if tl == nil {
|
|
||||||
t.Fatal("ListTools returned nil")
|
|
||||||
}
|
|
||||||
for _, ti := range tl {
|
|
||||||
res, err := r.CallTool(context.Background(), ti.Name, json.RawMessage(`{}`))
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("CallTool(%q): %v", ti.Name, err)
|
|
||||||
}
|
|
||||||
if len(res) == 0 {
|
|
||||||
t.Errorf("CallTool(%q) returned empty result", ti.Name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStubRunnerContract(t *testing.T) {
|
|
||||||
checkRunnerContract(t, newStub("s", "a", "b"))
|
|
||||||
}
|
|
||||||
Loading…
Reference in New Issue