session: remove Interrupt, Queue, Detach, and all remaining agent pass-throughs
Session now has only genuinely session-level methods: - Agent(), Bus() — access points - Close(), SetEnv() — lifecycle - CWD(), SetCWD(), SetSessionID() — filesystem identity - WaitChange() — field watching for 9P - SaveSession(), saveSession(), flushSave() — persistence Everything else (Interrupt, Queue, Detach, Reactions, etc.) lives on Agent. Callers go through session.Agent() to reach agent operations.
This commit is contained in:
parent
24b9a0bdd3
commit
ec62eb9189
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/simonfxr/pubsub"
|
"github.com/simonfxr/pubsub"
|
||||||
|
|
@ -509,3 +510,82 @@ func (ag *Agent) WaitForChange(ctx context.Context) {
|
||||||
}
|
}
|
||||||
ag.changeMu.Unlock()
|
ag.changeMu.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Detach detaches the current running process to background.
|
||||||
|
func (ag *Agent) Detach() bool {
|
||||||
|
if srv := ag.ExecServer(); srv != nil {
|
||||||
|
if d, ok := srv.(interface{ Detach() bool }); ok {
|
||||||
|
return d.Detach()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// DetachedInfo describes a detached process.
|
||||||
|
type DetachedInfo struct {
|
||||||
|
PID int
|
||||||
|
Command string
|
||||||
|
Started int64
|
||||||
|
Exited bool
|
||||||
|
ExitCode int
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListDetached returns info about all detached processes.
|
||||||
|
func (ag *Agent) ListDetached() []DetachedInfo {
|
||||||
|
srv := ag.ExecServer()
|
||||||
|
if srv == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
type listDetacher interface{ ListDetachedRaw() []any }
|
||||||
|
ld, ok := srv.(listDetacher)
|
||||||
|
if !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
raw := ld.ListDetachedRaw()
|
||||||
|
out := make([]DetachedInfo, 0, len(raw))
|
||||||
|
for _, r := range raw {
|
||||||
|
if m, ok := r.(map[string]any); ok {
|
||||||
|
di := DetachedInfo{}
|
||||||
|
if v, ok := m["pid"].(int); ok { di.PID = v }
|
||||||
|
if v, ok := m["command"].(string); ok { di.Command = v }
|
||||||
|
if v, ok := m["started"].(int64); ok { di.Started = v }
|
||||||
|
if v, ok := m["exited"].(bool); ok { di.Exited = v }
|
||||||
|
if v, ok := m["exit_code"].(int); ok { di.ExitCode = v }
|
||||||
|
out = append(out, di)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// SignalDetached sends a signal to a detached process.
|
||||||
|
func (ag *Agent) SignalDetached(pid int, signal int) error {
|
||||||
|
if srv := ag.ExecServer(); srv != nil {
|
||||||
|
type signaler interface{ 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")
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetDetachedOutput reads output from a detached process.
|
||||||
|
func (ag *Agent) GetDetachedOutput(pid int) (string, error) {
|
||||||
|
if srv := ag.ExecServer(); srv != nil {
|
||||||
|
type outputGetter interface{ GetDetachedOutput(int) (string, error) }
|
||||||
|
if og, ok := srv.(outputGetter); ok {
|
||||||
|
return og.GetDetachedOutput(pid)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "", fmt.Errorf("no execute server available")
|
||||||
|
}
|
||||||
|
|
||||||
|
// DismissDetached removes a finished detached process.
|
||||||
|
func (ag *Agent) DismissDetached(pid int) bool {
|
||||||
|
if srv := ag.ExecServer(); srv != nil {
|
||||||
|
type dismisser interface{ DismissDetached(int) bool }
|
||||||
|
if d, ok := srv.(dismisser); ok {
|
||||||
|
return d.DismissDetached(pid)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,6 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"syscall"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/simonfxr/pubsub"
|
"github.com/simonfxr/pubsub"
|
||||||
|
|
@ -188,19 +187,6 @@ func (a *Session) SetEnv(key, value string) {
|
||||||
func (a *Session) Agent() *agent.Agent { return a.r }
|
func (a *Session) Agent() *agent.Agent { return a.r }
|
||||||
func (a *Session) Bus() *pubsub.Bus { return a.bus }
|
func (a *Session) Bus() *pubsub.Bus { return a.bus }
|
||||||
|
|
||||||
func (a *Session) Interrupt(cause error) bool {
|
|
||||||
return a.r.Interrupt(cause)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *Session) Queue(prompt string) {
|
|
||||||
a.r.Queue(prompt)
|
|
||||||
a.bus.Publish("queued", prompt)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *Session) InjectSystemEvent(content string) {
|
|
||||||
a.Queue("<detached-process-result>\n" + content + "\n</detached-process-result>")
|
|
||||||
}
|
|
||||||
|
|
||||||
// CWD returns the current working directory for tool execution.
|
// CWD returns the current working directory for tool execution.
|
||||||
func (a *Session) CWD() string {
|
func (a *Session) CWD() string {
|
||||||
if c := a.r.Cwd(); c != "" {
|
if c := a.r.Cwd(); c != "" {
|
||||||
|
|
@ -279,10 +265,6 @@ func (a *Session) WaitChange(ctx context.Context, field, current string) (string
|
||||||
return "", false
|
return "", false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Session) emit(ev agent.Event) {
|
|
||||||
a.bus.Publish("event", ev)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *Session) activeSessionPath(id, suffix string) string {
|
func (a *Session) activeSessionPath(id, suffix string) string {
|
||||||
return filepath.Join(a.sessionsDir, "active", id+suffix)
|
return filepath.Join(a.sessionsDir, "active", id+suffix)
|
||||||
}
|
}
|
||||||
|
|
@ -322,78 +304,3 @@ func (a *Session) flushSave() {
|
||||||
func (a *Session) SaveSession(path string) error {
|
func (a *Session) SaveSession(path string) error {
|
||||||
return a.r.SaveFull(path, a.id, a.CWD(), a.remote)
|
return a.r.SaveFull(path, a.id, a.CWD(), a.remote)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Detach operations delegate to the agent's execute server.
|
|
||||||
func (a *Session) Detach() bool {
|
|
||||||
if srv := a.r.ExecServer(); srv != nil {
|
|
||||||
if d, ok := srv.(interface{ Detach() bool }); ok {
|
|
||||||
return d.Detach()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// DetachedInfo describes a detached process.
|
|
||||||
type DetachedInfo struct {
|
|
||||||
PID int
|
|
||||||
Command string
|
|
||||||
Started int64
|
|
||||||
Exited bool
|
|
||||||
ExitCode int
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *Session) ListDetached() []DetachedInfo {
|
|
||||||
srv := a.r.ExecServer()
|
|
||||||
if srv == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
type listDetacher interface{ ListDetachedRaw() []any }
|
|
||||||
ld, ok := srv.(listDetacher)
|
|
||||||
if !ok {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
raw := ld.ListDetachedRaw()
|
|
||||||
out := make([]DetachedInfo, 0, len(raw))
|
|
||||||
for _, r := range raw {
|
|
||||||
if m, ok := r.(map[string]any); ok {
|
|
||||||
di := DetachedInfo{}
|
|
||||||
if v, ok := m["pid"].(int); ok { di.PID = v }
|
|
||||||
if v, ok := m["command"].(string); ok { di.Command = v }
|
|
||||||
if v, ok := m["started"].(int64); ok { di.Started = v }
|
|
||||||
if v, ok := m["exited"].(bool); ok { di.Exited = v }
|
|
||||||
if v, ok := m["exit_code"].(int); ok { di.ExitCode = v }
|
|
||||||
out = append(out, di)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *Session) SignalDetached(pid, signal int) error {
|
|
||||||
if srv := a.r.ExecServer(); srv != nil {
|
|
||||||
type signaler interface{ 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")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *Session) GetDetachedOutput(pid int) (string, error) {
|
|
||||||
if srv := a.r.ExecServer(); srv != nil {
|
|
||||||
type outputGetter interface{ GetDetachedOutput(int) (string, error) }
|
|
||||||
if og, ok := srv.(outputGetter); ok {
|
|
||||||
return og.GetDetachedOutput(pid)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return "", fmt.Errorf("no execute server available")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *Session) DismissDetached(pid int) bool {
|
|
||||||
if srv := a.r.ExecServer(); srv != nil {
|
|
||||||
type dismisser interface{ DismissDetached(int) bool }
|
|
||||||
if d, ok := srv.(dismisser); ok {
|
|
||||||
return d.DismissDetached(pid)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
|
||||||
Reference in New Issue