agent: separate uname (principal) from session ID

Session identity is now a numeric UID (starting at 10000) assigned at
creation and never changed. Session names can be renamed freely without
affecting the 9P identity.

- AgentCoreConfig.Uname carries the principal
- pushSessionEnv sets OLLIE_UNAME on the execute server
- startMount gated by WithMount() option (not env var)
- Close() uses context timeout to avoid hanging on fusermount
This commit is contained in:
Levi Neely 2026-05-19 15:06:16 +02:00
parent e20f9a7a06
commit 1e96d4c7f4
2 changed files with 23 additions and 7 deletions

View File

@ -235,6 +235,7 @@ type AgentCoreConfig struct {
AgentsDir string
SessionsDir string
SessionID string
Uname string // immutable user principal for 9P identity
CWD string // working directory for tool execution and system prompt
Session *Session
Env AgentEnv
@ -257,6 +258,7 @@ type agent struct {
agentsDir string
sessionsDir string
sessionID string
uname string
dispatcher tools.Dispatcher
newDispatcher func() tools.Dispatcher
newBackend func(string) (backend.Backend, error)
@ -311,6 +313,9 @@ func (s *agent) pushSessionEnv() {
if srv, ok := s.dispatcher.GetServer("execute"); ok {
if es, ok := srv.(tools.EnvSetter); ok {
es.SetEnv("OLLIE_SESSION_ID", s.sessionID)
if s.uname != "" {
es.SetEnv("OLLIE_UNAME", s.uname)
}
}
}
}
@ -416,6 +421,7 @@ func NewAgentCore(cfg AgentCoreConfig) Core {
agentsDir: cfg.AgentsDir,
sessionsDir: cfg.SessionsDir,
sessionID: cfg.SessionID,
uname: cfg.Uname,
cwd: paths.ExpandHome(cfg.CWD),
startupMessages: cfg.Env.Messages,
dispatcher: cfg.Env.dispatcher,

View File

@ -42,8 +42,9 @@ type Server struct {
lockDir string
// mountProc is the ollie-9p-mount subprocess for this session's FUSE proxy.
mountProc *os.Process
mountPath string
mountProc *os.Process
mountPath string
mountEnabled bool
// Strict rejects inline {code} steps; only {tool} steps are allowed.
Strict bool
@ -75,6 +76,9 @@ func WithStrict() Option { return func(s *Server) { s.Strict = true } }
// WithYolo skips the landrun sandbox.
func WithYolo() Option { return func(s *Server) { s.Yolo = true } }
// WithMount enables per-session FUSE mount for 9P identity.
func WithMount() Option { return func(s *Server) { s.mountEnabled = true } }
// WithAllowExecutors restricts which executors are available.
func WithAllowExecutors(names []string) Option {
return func(s *Server) {
@ -362,7 +366,8 @@ func (e *Server) SetEnv(key, value string) {
e.envExtra = make(map[string]string)
}
e.envExtra[key] = value
if key == "OLLIE_SESSION_ID" && value != "" {
// Start mount once we have the principal.
if key == "OLLIE_UNAME" && value != "" {
e.startMount(value)
}
e.envMu.Unlock()
@ -370,9 +375,12 @@ func (e *Server) SetEnv(key, value string) {
// startMount launches ollie-9p-mount for this session. Must be called with envMu held.
func (e *Server) startMount(sessionID string) {
if !e.mountEnabled {
return
}
bin, err := exec.LookPath("ollie-9p-mount")
if err != nil {
return // binary not installed; skip silently
return
}
mnt := filepath.Join(os.TempDir(), "ollie-mount-"+sessionID)
os.MkdirAll(mnt, 0755) //nolint:errcheck
@ -404,9 +412,11 @@ func (e *Server) Close() {
e.mountPath = ""
e.envMu.Unlock()
if proc != nil {
exec.Command("fusermount", "-u", mnt).Run() //nolint:errcheck
syscall.Kill(proc.Pid, syscall.SIGKILL) //nolint:errcheck
os.Remove(mnt) //nolint:errcheck
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
exec.CommandContext(ctx, "fusermount", "-u", mnt).Run() //nolint:errcheck
cancel()
syscall.Kill(proc.Pid, syscall.SIGKILL) //nolint:errcheck
os.Remove(mnt) //nolint:errcheck
}
}