remove D-Bus adapter from olliesrv

Delete dbus/dbus.go — the entire D-Bus SessionManager service is gone.
No more 150ms poll loop, no more signal emission, no more method handlers.

- Remove ollie/dbus import, nodbus flag, dbusAdapter variable
- Remove OnSessionCreated/Killed/Renamed hooks from Config
- Remove EnableDBus from Config
- Elevation notifications connect to session bus directly (godbus stays
  as a dependency solely for org.freedesktop.Notifications)

All clients now use 9P exclusively. D-Bus is dead.
This commit is contained in:
Levi Neely 2026-07-31 20:41:02 +02:00
parent 622ede8c21
commit 43d0db520a
6 changed files with 8 additions and 771 deletions

View File

@ -1 +0,0 @@
# Placeholder — real config copied during build (just ollie-remote)

View File

@ -17,12 +17,13 @@ import (
"9fans.net/go/plan9/client"
"ollie/agent"
"ollie/backend"
oldbus "ollie/dbus"
"ollie/elevate"
"ollie/env"
"ollie/fs/session"
olog "ollie/log"
"ollie/mount"
"github.com/godbus/dbus/v5"
"ollie/paths"
"ollie/skills"
"ollie/toolsrv"
@ -33,8 +34,7 @@ const serviceName = "ollie"
var tcpAddr = flag.String("tcp", "", "also listen on TCP address (e.g. :564)")
var strict = flag.Bool("strict", false, "only allow tool steps; reject inline code")
var yolo = flag.Bool("yolo", false, "skip landrun sandbox for shell")
var nodbus = flag.Bool("nodbus", false, "disable D-Bus adapter (for headless/container use)")
var no9p = flag.Bool("no9p", false, "disable 9P listener (D-Bus only mode)")
var no9p = flag.Bool("no9p", false, "disable 9P listener")
// ModelCache caches the list of models from all backends.
type ModelCache struct {
@ -141,12 +141,6 @@ func cmdMount() {
func runServer(sockPath string) {
env.EnsureDefaults()
// -no9p requires D-Bus (otherwise there's no interface at all)
if *no9p && *nodbus {
fmt.Fprintln(os.Stderr, "error: -no9p and -nodbus are mutually exclusive")
os.Exit(1)
}
// Remove stale socket
if !*no9p {
if _, err := os.Stat(sockPath); err == nil {
@ -168,9 +162,6 @@ func runServer(sockPath string) {
// Create the skills registry
skillsRegistry := skills.NewRegistry()
// D-Bus adapter (initialized after manager so callbacks can reference it).
var dbusAdapter *oldbus.DBusAdapter
// Elevate broker (initialized after manager; closures capture the pointer).
var elevateBroker *elevate.Broker
@ -189,42 +180,12 @@ func runServer(sockPath string) {
Yolo: *yolo,
NoMount: *no9p || *tcpAddr != "",
Enable9P: !*no9p,
EnableDBus: !*nodbus && (*no9p || *tcpAddr == ""),
InvalidateModels: modelCache.Invalidate,
OnSessionCreated: func(id string, sess *fs.Session) {
if dbusAdapter != nil {
dbusAdapter.OnSessionCreated(id, sess)
}
},
OnSessionKilled: func(id string) {
if dbusAdapter != nil {
dbusAdapter.OnSessionKilled(id)
}
},
OnSessionRenamed: func(oldID, newID string) {
if dbusAdapter != nil {
dbusAdapter.OnSessionRenamed(oldID, newID)
}
},
})
var srv *Server
// Server creation deferred until after elevate broker is ready (see below)
// Start D-Bus adapter.
// -tcp implies -nodbus (remote/headless deployments have no session bus).
// -no9p forces D-Bus on (it's the only interface).
if !*nodbus && (*no9p || *tcpAddr == "") {
dbusAdapter = oldbus.StartDBus(sessionTree)
if dbusAdapter != nil {
dbusAdapter.WatchExisting()
}
if *no9p && dbusAdapter == nil {
fmt.Fprintln(os.Stderr, "error: -no9p mode requires D-Bus but session bus is unavailable")
os.Exit(1)
}
}
// Start elevation broker
elevateSocket := os.Getenv("OLLIE_ELEVATE_SOCKET")
if elevateSocket == "" {
@ -272,8 +233,9 @@ func runServer(sockPath string) {
fmt.Fprintf(os.Stderr, "warning: %v\n", err)
} else {
defer elevateBroker.Close()
if dbusAdapter != nil {
initElevateNotifier(dbusAdapter.Conn(), elevateBroker)
// Desktop notifications for elevation prompts (uses D-Bus notifications API directly)
if conn, err := dbus.SessionBus(); err == nil {
initElevateNotifier(conn, elevateBroker)
}
}
}
@ -335,19 +297,12 @@ func runServer(sockPath string) {
fmt.Printf("olliesrv listening on tcp %s\n", *tcpAddr)
}
if *no9p {
fmt.Println("olliesrv running in D-Bus only mode")
}
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
<-sigChan
fmt.Println("shutting down")
daemonCancel() // signal all sessions via context propagation
if dbusAdapter != nil {
dbusAdapter.Close()
}
if srv != nil {
srv.Shutdown()
}

View File

@ -1,697 +0,0 @@
package dbus
import (
"context"
"encoding/json"
"fmt"
"os"
"sort"
"strings"
"sync"
"time"
"github.com/godbus/dbus/v5"
"github.com/godbus/dbus/v5/introspect"
"ollie/agent"
"ollie/backend"
"ollie/fs/session"
)
const (
busName = "org.ollie.SessionManager"
busPath = "/org/ollie/SessionManager"
busIface = "org.ollie.SessionManager"
)
// DBusAdapter exposes sessions over D-Bus.
type DBusAdapter struct {
conn *dbus.Conn
tree *fs.Tree
mu sync.RWMutex
watchers map[string]context.CancelFunc // sessionID -> cancel for state watcher
}
// StartDBus connects to the session bus, claims the well-known name, and
// exports the adapter. Returns nil (no-op) if the bus is unavailable.
func StartDBus(tree *fs.Tree) *DBusAdapter {
conn, err := dbus.ConnectSessionBus()
if err != nil {
fmt.Fprintf(os.Stderr, "dbus: session bus unavailable: %v\n", err)
return nil
}
reply, err := conn.RequestName(busName, dbus.NameFlagDoNotQueue)
if err != nil || reply != dbus.RequestNameReplyPrimaryOwner {
fmt.Fprintf(os.Stderr, "dbus: cannot claim %s (ollied already running?)\n", busName)
conn.Close()
return nil
}
a := &DBusAdapter{
conn: conn,
tree: tree,
watchers: make(map[string]context.CancelFunc),
}
conn.Export(a, busPath, busIface)
conn.Export(introspect.Introspectable(introspectXML), busPath, "org.freedesktop.DBus.Introspectable")
fmt.Printf("dbus: exported %s\n", busName)
return a
}
// WatchExisting starts watchers for all sessions that already exist.
// Called after the adapter is initialized to cover sessions restored at startup.
func (a *DBusAdapter) WatchExisting() {
if a == nil {
return
}
for _, sess := range fs.All(a.tree) {
id := sess.RunnableID()
a.startWatcher(id, sess)
// Emit SessionCreated so GUI clients that connected before restore
// (via serviceRegistered) learn about pre-existing sessions.
a.conn.Emit(busPath, busIface+".SessionCreated", id)
}
}
func (a *DBusAdapter) Close() {
if a == nil || a.conn == nil {
return
}
a.mu.Lock()
for _, cancel := range a.watchers {
cancel()
}
a.mu.Unlock()
a.conn.Close()
}
// Conn returns the underlying D-Bus connection, or nil if the adapter is nil.
func (a *DBusAdapter) Conn() *dbus.Conn {
if a == nil {
return nil
}
return a.conn
}
// --- Lifecycle callbacks ---
func (a *DBusAdapter) OnSessionCreated(id string, sess *fs.Session) {
if a == nil {
return
}
a.startWatcher(id, sess)
go a.conn.Emit(busPath, busIface+".SessionCreated", id)
}
func (a *DBusAdapter) OnSessionKilled(id string) {
if a == nil {
return
}
a.stopWatcher(id)
go a.conn.Emit(busPath, busIface+".SessionKilled", id)
}
func (a *DBusAdapter) OnSessionRenamed(oldID, newID string) {
if a == nil {
return
}
a.stopWatcher(oldID)
// The session object is the same, just re-lookup
sess := fs.Lookup(a.tree, newID)
if sess != nil {
a.startWatcher(newID, sess)
}
go a.conn.Emit(busPath, busIface+".SessionRenamed", oldID, newID)
}
// startWatcher launches goroutines for StateChanged and ChatUpdated signals.
func (a *DBusAdapter) startWatcher(id string, sess *fs.Session) {
ctx, cancel := context.WithCancel(context.Background())
a.mu.Lock()
a.watchers[id] = cancel
a.mu.Unlock()
// State watcher
go func() {
current := sess.Core.Agent().State()
for {
next, ok := sess.Core.WaitChange(ctx, agent.WatchState, current)
if !ok {
return
}
current = next
a.conn.Emit(busPath, busIface+".StateChanged", id, current)
if current == "idle" {
a.sendNotification("Ollie", "Session "+id+" finished")
}
}
}()
// Agent watcher — emits ConfigChanged when the agent name changes
go func() {
current := sess.Core.Agent().Name()
for {
next, ok := sess.Core.WaitChange(ctx, "agent", current)
if !ok {
return
}
current = next
a.conn.Emit(busPath, busIface+".ConfigChanged", id, "agent", current)
}
}()
// Chat watcher (polls log version)
go func() {
var lastLen int
var lastVers uint32
for {
select {
case <-ctx.Done():
return
case <-time.After(150 * time.Millisecond):
}
l, v := sess.LogInfo()
if v == lastVers {
continue
}
if l > lastLen {
// Read the new chunk
sess.Mu().RLock()
chunk := string(sess.Log()[lastLen:])
sess.Mu().RUnlock()
a.conn.Emit(busPath, busIface+".ChatUpdated", id, int64(lastLen), chunk)
}
lastLen = l
lastVers = v
}
}()
// Detached process exit watcher
go func() {
exitedPIDs := make(map[int]bool) // already-reported PIDs
for {
select {
case <-ctx.Done():
return
case <-time.After(1 * time.Second):
}
infos := sess.Core.Agent().ListDetached()
// Clean up stale entries from exitedPIDs
activePIDs := make(map[int]bool, len(infos))
for _, info := range infos {
activePIDs[info.PID] = true
}
for pid := range exitedPIDs {
if !activePIDs[pid] {
delete(exitedPIDs, pid)
}
}
for _, info := range infos {
if info.Exited && !exitedPIDs[info.PID] {
exitedPIDs[info.PID] = true
a.conn.Emit(busPath, busIface+".ProcessExited", id, int32(info.PID), int32(info.ExitCode))
}
}
}
}()
}
func (a *DBusAdapter) stopWatcher(id string) {
a.mu.Lock()
if cancel, ok := a.watchers[id]; ok {
cancel()
delete(a.watchers, id)
}
a.mu.Unlock()
}
// --- D-Bus exported methods ---
func (a *DBusAdapter) CreateSession(cwd, backendName, modelName, agentName, systemPrompt, remote string) (string, *dbus.Error) {
var args []string
if cwd != "" {
args = append(args, "cwd="+cwd)
}
if backendName != "" {
args = append(args, "backend="+backendName)
}
if modelName != "" {
args = append(args, "model="+modelName)
}
if agentName != "" {
args = append(args, "agent="+agentName)
}
if systemPrompt != "" {
args = append(args, "system_prompt="+systemPrompt)
}
if remote != "" {
args = append(args, "remote="+remote)
}
if cwd == "" {
dir, _ := os.Getwd()
args = append(args, "cwd="+dir)
}
id, err := fs.CreateFromRoot(a.tree, args)
if err != nil {
return "", dbus.MakeFailedError(err)
}
return id, nil
}
func (a *DBusAdapter) ListSessions() ([]string, *dbus.Error) {
var result []string
for _, sess := range fs.All(a.tree) {
id := sess.RunnableID()
result = append(result, fmt.Sprintf("%s\t%s\t%s\t%s",
id, sess.Core.Agent().State(), sess.Core.Agent().ModelName(), sess.Core.Agent().Name()))
}
return result, nil
}
func (a *DBusAdapter) KillSession(sessionID string) (bool, *dbus.Error) {
sess := fs.Lookup(a.tree, sessionID)
if sess == nil {
return false, nil
}
fs.KillFromRoot(a.tree, sessionID)
return true, nil
}
func (a *DBusAdapter) RenameSession(sessionID, newName string) (bool, *dbus.Error) {
if err := fs.RenameFromRoot(a.tree, sessionID, newName); err != nil {
return false, dbus.MakeFailedError(err)
}
return true, nil
}
func (a *DBusAdapter) Submit(sessionID, prompt string) (bool, *dbus.Error) {
sess := fs.Lookup(a.tree, sessionID)
if sess == nil {
return false, nil
}
go sess.Core.Agent().Submit(sess.SessionCtx, prompt)
return true, nil
}
func (a *DBusAdapter) Interrupt(sessionID string) (bool, *dbus.Error) {
sess := fs.Lookup(a.tree, sessionID)
if sess == nil {
return false, nil
}
sess.Core.Agent().Interrupt(agent.ErrInterrupted)
return true, nil
}
func (a *DBusAdapter) GetState(sessionID string) (string, *dbus.Error) {
sess := fs.Lookup(a.tree, sessionID)
if sess == nil {
return "", nil
}
return sess.Core.Agent().State(), nil
}
func (a *DBusAdapter) GetChat(sessionID string, offset int64) (string, int64, *dbus.Error) {
sess := fs.Lookup(a.tree, sessionID)
if sess == nil {
return "", offset, nil
}
sess.Mu().RLock()
log := sess.Log()
if offset < 0 {
offset = 0
}
if offset > int64(len(log)) {
offset = int64(len(log))
}
text := string(log[offset:])
newOffset := int64(len(log))
sess.Mu().RUnlock()
return text, newOffset, nil
}
func (a *DBusAdapter) GetUsage(sessionID string) (string, *dbus.Error) {
sess := fs.Lookup(a.tree, sessionID)
if sess == nil {
return "", nil
}
return sess.Core.Agent().UsageStr(), nil
}
func (a *DBusAdapter) GetCost(sessionID string) (string, *dbus.Error) {
sess := fs.Lookup(a.tree, sessionID)
if sess == nil {
return "", nil
}
return sess.Core.Agent().CostStr(), nil
}
func (a *DBusAdapter) GetConfig(sessionID string) (string, *dbus.Error) {
sess := fs.Lookup(a.tree, sessionID)
if sess == nil {
return "", nil
}
var sb strings.Builder
fmt.Fprintf(&sb, "model=%s\n", sess.Core.Agent().ModelName())
fmt.Fprintf(&sb, "backend=%s\n", sess.Core.Agent().BackendName())
fmt.Fprintf(&sb, "agent=%s\n", sess.Core.Agent().Name())
fmt.Fprintf(&sb, "cwd=%s\n", sess.Core.CWD())
fmt.Fprintf(&sb, "state=%s\n", sess.Core.Agent().State())
return sb.String(), nil
}
func (a *DBusAdapter) SetConfig(sessionID, key, value string) (bool, *dbus.Error) {
sess := fs.Lookup(a.tree, sessionID)
if sess == nil {
return false, nil
}
switch key {
case "model":
sess.Core.Agent().Submit(sess.SessionCtx, "/model "+value)
case "cwd":
if err := sess.Core.SetCWD(value); err != nil {
return false, nil
}
default:
return false, nil
}
return true, nil
}
func (a *DBusAdapter) GetContext(sessionID string) (string, *dbus.Error) {
sess := fs.Lookup(a.tree, sessionID)
if sess == nil {
return "", nil
}
msgs := sess.Core.Agent().Context()
var sb strings.Builder
enc := json.NewEncoder(&sb)
enc.SetEscapeHTML(false)
for _, msg := range msgs {
enc.Encode(msg)
}
return sb.String(), nil
}
func (a *DBusAdapter) ListBackends() ([]string, *dbus.Error) {
return backend.Backends(), nil
}
func (a *DBusAdapter) ListModelsByBackend(backendName string) ([]string, *dbus.Error) {
be, err := backend.NewWithName(backendName)
if err != nil {
return nil, dbus.MakeFailedError(err)
}
models := be.Models(context.Background())
sort.Strings(models)
return models, nil
}
func (a *DBusAdapter) ListModels(sessionID string) ([]string, *dbus.Error) {
sess := fs.Lookup(a.tree, sessionID)
if sess == nil {
return nil, nil
}
raw := sess.CachedListModels()
models := strings.Split(strings.TrimSpace(raw), "\n")
if len(models) == 1 && models[0] == "" {
return nil, nil
}
sort.Strings(models)
return models, nil
}
func (a *DBusAdapter) ListAgents() ([]string, *dbus.Error) {
var result []string
for _, dir := range agent.AgentsDirs() {
entries, err := os.ReadDir(dir)
if err != nil {
continue
}
for _, e := range entries {
name := e.Name()
if strings.HasSuffix(name, ".json") {
result = append(result, strings.TrimSuffix(name, ".json"))
}
}
}
return result, nil
}
// --- Detached processes ---
func (a *DBusAdapter) DetachProcess(sessionID string) (bool, *dbus.Error) {
sess := fs.Lookup(a.tree, sessionID)
if sess == nil {
return false, dbus.MakeFailedError(fmt.Errorf("session not found: %s", sessionID))
}
if !sess.Core.Agent().Detach() {
return false, dbus.MakeFailedError(fmt.Errorf("no running process to detach"))
}
a.conn.Emit(busPath, busIface+".ProcessDetached", sessionID)
return true, nil
}
func (a *DBusAdapter) ListDetached(sessionID string) ([]string, *dbus.Error) {
sess := fs.Lookup(a.tree, sessionID)
if sess == nil {
return nil, dbus.MakeFailedError(fmt.Errorf("session not found: %s", sessionID))
}
infos := sess.Core.Agent().ListDetached()
out := make([]string, len(infos))
for i, info := range infos {
status := "running"
if info.Exited {
status = fmt.Sprintf("exited(%d)", info.ExitCode)
}
out[i] = fmt.Sprintf("%d\t%s\t%d\t%s", info.PID, info.Command, info.Started, status)
}
return out, nil
}
func (a *DBusAdapter) SignalDetached(sessionID string, pid int32, signal string) (bool, *dbus.Error) {
sess := fs.Lookup(a.tree, sessionID)
if sess == nil {
return false, dbus.MakeFailedError(fmt.Errorf("session not found: %s", sessionID))
}
var sig int
switch strings.ToUpper(signal) {
case "TERM", "SIGTERM", "15":
sig = 15
case "KILL", "SIGKILL", "9":
sig = 9
default:
return false, dbus.MakeFailedError(fmt.Errorf("unsupported signal: %s", signal))
}
if err := sess.Core.Agent().SignalDetached(int(pid), sig); err != nil {
return false, dbus.MakeFailedError(err)
}
return true, nil
}
func (a *DBusAdapter) GetDetachedOutput(sessionID string, pid int32) (string, *dbus.Error) {
sess := fs.Lookup(a.tree, sessionID)
if sess == nil {
return "", dbus.MakeFailedError(fmt.Errorf("session not found: %s", sessionID))
}
output, err := sess.Core.Agent().GetDetachedOutput(int(pid))
if err != nil {
return "", dbus.MakeFailedError(err)
}
return output, nil
}
func (a *DBusAdapter) DismissDetached(sessionID string, pid int32) (bool, *dbus.Error) {
sess := fs.Lookup(a.tree, sessionID)
if sess == nil {
return false, dbus.MakeFailedError(fmt.Errorf("session not found: %s", sessionID))
}
return sess.Core.Agent().DismissDetached(int(pid)), nil
}
// --- Generate / Route ---
func (a *DBusAdapter) Generate(prompt, system, backendName, model string) (string, *dbus.Error) {
result, err := backend.Generate(context.Background(), backend.GenerateRequest{
Prompt: prompt,
System: system,
Backend: backendName,
Model: model,
})
if err != nil {
return "", dbus.MakeFailedError(err)
}
return result, nil
}
func (a *DBusAdapter) Route(task, backendName string) (string, *dbus.Error) {
result, err := backend.Route(context.Background(), backend.RouteRequest{
Task: task,
Backend: backendName,
})
if err != nil {
return "", dbus.MakeFailedError(err)
}
return fmt.Sprintf("backend=%s model=%s", result.Backend, result.Model), nil
}
func (a *DBusAdapter) Complete(cwd, filePath, prefix, suffix, extraContext string) (string, *dbus.Error) {
result, err := agent.Complete(cwd, filePath, prefix, suffix, extraContext)
if err != nil {
return "", dbus.MakeFailedError(err)
}
return result, nil
}
func (a *DBusAdapter) GetEnv(sessionID string) (string, *dbus.Error) {
sess := fs.Lookup(a.tree, sessionID)
if sess == nil {
return "", nil
}
var sb strings.Builder
fmt.Fprintf(&sb, "OLLIE_SESSION_ID=%s\n", sess.RunnableID())
for _, e := range os.Environ() {
if strings.HasPrefix(e, "OLLIE_") && !strings.HasPrefix(e, "OLLIE_SESSION_ID=") {
sb.WriteString(e)
sb.WriteByte('\n')
}
}
return sb.String(), nil
}
func (a *DBusAdapter) GetPlan(sessionID string) (string, *dbus.Error) {
sess := fs.Lookup(a.tree, sessionID)
if sess == nil {
return "", nil
}
sess.Mu().RLock()
plan := string(sess.Plan())
sess.Mu().RUnlock()
return plan, nil
}
func (a *DBusAdapter) SetPlan(sessionID, plan string) (bool, *dbus.Error) {
sess := fs.Lookup(a.tree, sessionID)
if sess == nil {
return false, nil
}
sess.Mu().Lock()
sess.SetPlan([]byte(plan))
sess.Mu().Unlock()
return true, nil
}
func (a *DBusAdapter) GetSystemPrompt(sessionID string) (string, *dbus.Error) {
sess := fs.Lookup(a.tree, sessionID)
if sess == nil {
return "", nil
}
return sess.Core.Agent().SystemPrompt(), nil
}
func (a *DBusAdapter) GetContextSize(sessionID string) (string, *dbus.Error) {
sess := fs.Lookup(a.tree, sessionID)
if sess == nil {
return "", nil
}
return sess.Core.Agent().CtxSz(), nil
}
func (a *DBusAdapter) GetPreviousPrompt(sessionID string) (string, *dbus.Error) {
sess := fs.Lookup(a.tree, sessionID)
if sess == nil {
return "", nil
}
sess.Mu().RLock()
p := string(sess.PrevPrompt())
sess.Mu().RUnlock()
return p, nil
}
func (a *DBusAdapter) Compact(sessionID string) (bool, *dbus.Error) {
sess := fs.Lookup(a.tree, sessionID)
if sess == nil {
return false, nil
}
sess.Core.Agent().Submit(sess.SessionCtx, "/compact")
return true, nil
}
func (a *DBusAdapter) ClearContext(sessionID string) (bool, *dbus.Error) {
sess := fs.Lookup(a.tree, sessionID)
if sess == nil {
return false, nil
}
sess.Core.Agent().Submit(sess.SessionCtx, "/clear")
return true, nil
}
// --- Notifications ---
func (a *DBusAdapter) sendNotification(title, body string) {
go func() {
obj := a.conn.Object("org.freedesktop.Notifications", "/org/freedesktop/Notifications")
obj.Call("org.freedesktop.Notifications.Notify", 0,
"olliesrv", uint32(0), "system-run", title, body,
[]string{}, map[string]dbus.Variant{}, int32(5000))
}()
}
// Introspection XML.
var introspectXML = `<node>
<interface name="` + busIface + `">
<method name="CreateSession">
<arg direction="in" type="s" name="cwd"/>
<arg direction="in" type="s" name="backend"/>
<arg direction="in" type="s" name="model"/>
<arg direction="in" type="s" name="agent"/>
<arg direction="in" type="s" name="systemPrompt"/>
<arg direction="in" type="s" name="remote"/>
<arg direction="out" type="s"/>
</method>
<method name="ListSessions"><arg direction="out" type="as"/></method>
<method name="KillSession"><arg direction="in" type="s" name="sessionID"/><arg direction="out" type="b"/></method>
<method name="RenameSession"><arg direction="in" type="s" name="sessionID"/><arg direction="in" type="s" name="newName"/><arg direction="out" type="b"/></method>
<method name="Submit"><arg direction="in" type="s" name="sessionID"/><arg direction="in" type="s" name="prompt"/><arg direction="out" type="b"/></method>
<method name="Interrupt"><arg direction="in" type="s" name="sessionID"/><arg direction="out" type="b"/></method>
<method name="GetState"><arg direction="in" type="s" name="sessionID"/><arg direction="out" type="s"/></method>
<method name="GetChat"><arg direction="in" type="s" name="sessionID"/><arg direction="in" type="x" name="offset"/><arg direction="out" type="s"/><arg direction="out" type="x"/></method>
<method name="GetUsage"><arg direction="in" type="s" name="sessionID"/><arg direction="out" type="s"/></method>
<method name="GetCost"><arg direction="in" type="s" name="sessionID"/><arg direction="out" type="s"/></method>
<method name="GetConfig"><arg direction="in" type="s" name="sessionID"/><arg direction="out" type="s"/></method>
<method name="SetConfig"><arg direction="in" type="s" name="sessionID"/><arg direction="in" type="s" name="key"/><arg direction="in" type="s" name="value"/><arg direction="out" type="b"/></method>
<method name="GetContext"><arg direction="in" type="s" name="sessionID"/><arg direction="out" type="s"/></method>
<method name="ListBackends"><arg direction="out" type="as"/></method>
<method name="ListModelsByBackend"><arg direction="in" type="s" name="backend"/><arg direction="out" type="as"/></method>
<method name="ListModels"><arg direction="in" type="s" name="sessionID"/><arg direction="out" type="as"/></method>
<method name="ListAgents"><arg direction="out" type="as"/></method>
<method name="DetachProcess"><arg direction="in" type="s" name="sessionID"/><arg direction="out" type="b"/></method>
<method name="ListDetached"><arg direction="in" type="s" name="sessionID"/><arg direction="out" type="as"/></method>
<method name="SignalDetached"><arg direction="in" type="s" name="sessionID"/><arg direction="in" type="i" name="pid"/><arg direction="in" type="s" name="signal"/><arg direction="out" type="b"/></method>
<method name="GetDetachedOutput"><arg direction="in" type="s" name="sessionID"/><arg direction="in" type="i" name="pid"/><arg direction="out" type="s"/></method>
<method name="DismissDetached"><arg direction="in" type="s" name="sessionID"/><arg direction="in" type="i" name="pid"/><arg direction="out" type="b"/></method>
<method name="Generate"><arg direction="in" type="s" name="prompt"/><arg direction="in" type="s" name="system"/><arg direction="in" type="s" name="backend"/><arg direction="in" type="s" name="model"/><arg direction="out" type="s"/></method>
<method name="Route"><arg direction="in" type="s" name="task"/><arg direction="in" type="s" name="backend"/><arg direction="out" type="s"/></method>
<method name="Complete"><arg direction="in" type="s" name="cwd"/><arg direction="in" type="s" name="file"/><arg direction="in" type="s" name="prefix"/><arg direction="in" type="s" name="suffix"/><arg direction="in" type="s" name="context"/><arg direction="out" type="s"/></method>
<method name="GetEnv"><arg direction="in" type="s" name="sessionID"/><arg direction="out" type="s"/></method>
<method name="GetPlan"><arg direction="in" type="s" name="sessionID"/><arg direction="out" type="s"/></method>
<method name="SetPlan"><arg direction="in" type="s" name="sessionID"/><arg direction="in" type="s" name="plan"/><arg direction="out" type="b"/></method>
<method name="GetSystemPrompt"><arg direction="in" type="s" name="sessionID"/><arg direction="out" type="s"/></method>
<method name="GetContextSize"><arg direction="in" type="s" name="sessionID"/><arg direction="out" type="s"/></method>
<method name="GetPreviousPrompt"><arg direction="in" type="s" name="sessionID"/><arg direction="out" type="s"/></method>
<method name="Compact"><arg direction="in" type="s" name="sessionID"/><arg direction="out" type="b"/></method>
<method name="ClearContext"><arg direction="in" type="s" name="sessionID"/><arg direction="out" type="b"/></method>
<signal name="SessionCreated"><arg type="s" name="sessionID"/></signal>
<signal name="SessionKilled"><arg type="s" name="sessionID"/></signal>
<signal name="SessionRenamed"><arg type="s" name="oldID"/><arg type="s" name="newID"/></signal>
<signal name="StateChanged"><arg type="s" name="sessionID"/><arg type="s" name="state"/></signal>
<signal name="ChatUpdated"><arg type="s" name="sessionID"/><arg type="x" name="offset"/><arg type="s" name="text"/></signal>
<signal name="ProcessDetached"><arg type="s" name="sessionID"/></signal>
<signal name="ProcessExited"><arg type="s" name="sessionID"/><arg type="i" name="pid"/><arg type="i" name="exitCode"/></signal>
<signal name="ConfigChanged"><arg type="s" name="sessionID"/><arg type="s" name="key"/><arg type="s" name="value"/></signal>
</interface>
</node>`

View File

@ -194,7 +194,7 @@ func Create(rs *rootState, args []string) (string, error) {
envMap[k] = v
}
}
opModel := prompts.OperationalModel(rs.cfg.Enable9P, rs.cfg.EnableDBus, envMap)
opModel := prompts.OperationalModel(rs.cfg.Enable9P, false, envMap)
platform := "linux"
isGitRepo := false
@ -256,9 +256,6 @@ func Create(rs *rootState, args []string) (string, error) {
rs.cfg.Log.Info("new session %s (backend=%s model=%s agent=%s)",
sessID, core.Agent().BackendName(), core.Agent().ModelName(), core.Agent().Name())
if rs.cfg.OnSessionCreated != nil {
rs.cfg.OnSessionCreated(sessID, sess)
}
return sessID, nil
}

View File

@ -30,9 +30,6 @@ func Kill(rs *rootState, id string) {
}
removePersistedAgent(rs, id)
rs.cfg.Log.Info("killed session %s", id)
if rs.cfg.OnSessionKilled != nil {
rs.cfg.OnSessionKilled(id)
}
}
}
@ -70,9 +67,6 @@ func Rename(rs *rootState, old, new string) error {
sess.AppendLog([]byte(fmt.Sprintf(":: session renamed: %s -> %s\n", old, new)))
rs.cfg.Log.Info("renamed session %s -> %s", old, new)
if rs.cfg.OnSessionRenamed != nil {
rs.cfg.OnSessionRenamed(old, new)
}
return nil
}
@ -296,7 +290,7 @@ func restoreSession(rs *rootState, ps *agent.PersistedAgent) error {
envMap[k] = v
}
}
opModel := prompts.OperationalModel(rs.cfg.Enable9P, rs.cfg.EnableDBus, envMap)
opModel := prompts.OperationalModel(rs.cfg.Enable9P, false, envMap)
platform := "linux"
isGitRepo := false
for _, e := range promptEnv {
@ -358,8 +352,5 @@ func restoreSession(rs *rootState, ps *agent.PersistedAgent) error {
rs.mu.Unlock()
rs.cfg.Log.Info("restored session %s (backend=%s model=%s agent=%s)", sessID, backendName, modelName, agentName)
if rs.cfg.OnSessionCreated != nil {
rs.cfg.OnSessionCreated(sessID, sess)
}
return nil
}

View File

@ -47,20 +47,12 @@ type Config struct {
NoMount bool
// Enable9P indicates the 9P listener is active (for operational model injection).
Enable9P bool
// EnableDBus indicates the D-Bus adapter is active (for operational model injection).
EnableDBus bool
// InvalidateModels clears the model cache, forcing a refresh.
InvalidateModels func()
// ToolRegistry is the shared tool registry for lazy tool promotion.
ToolRegistry *toolsrv.Registry
// SkillsRegistry is the shared skills registry for skill loading.
SkillsRegistry *skills.Registry
// OnSessionCreated is called after a new session is added.
OnSessionCreated func(id string, sess *Session)
// OnSessionKilled is called after a session is removed.
OnSessionKilled func(id string)
// OnSessionRenamed is called after a session is renamed.
OnSessionRenamed func(oldID, newID string)
}
// rootState is the package-level state stored in root.Data.