update kde submodule; session/fs changes

This commit is contained in:
Levi Neely 2026-08-05 17:51:37 +02:00
parent 3febb30de8
commit 0cefd03e9a
6 changed files with 142 additions and 93 deletions

View File

@ -13,6 +13,7 @@ import (
"ollie/backend"
"ollie/elevate"
"ollie/fsedsl"
"ollie/paths"
"ollie/session"
"ollie/toolsrv"
)
@ -240,6 +241,10 @@ func readSessionIdx(_ HandlerCtx) ([]byte, error) {
return session.BuildIndex(), nil
}
func readAgentIdx(ctx HandlerCtx) ([]byte, error) {
return session.BuildAgentIndex(ctx.Session.Core), nil
}
func requestSessionNew(_ HandlerCtx, data []byte) ([]byte, error) {
args := strings.Fields(string(data))
name := ""
@ -586,7 +591,7 @@ func readAgentCfg(ctx HandlerCtx) ([]byte, error) {
fmt.Fprintf(&sb, "id=%s\n", ctx.Agent.ID())
fmt.Fprintf(&sb, "profile=%s\n", ctx.Agent.Profile())
fmt.Fprintf(&sb, "displayName=%s\n", ctx.Agent.Name())
fmt.Fprintf(&sb, "cwd=%s\n", ctx.Session.Core.CWD())
fmt.Fprintf(&sb, "cwd=%s\n", ctx.Agent.Cwd())
fmt.Fprintf(&sb, "remote=%s\n", ctx.AgentLog.remote)
fmt.Fprintf(&sb, "maxTokens=%d\n", p.MaxTokens)
fmt.Fprintf(&sb, "temperature=%v\n", p.Temperature)
@ -658,7 +663,7 @@ func writeAgentCtl(ctx HandlerCtx, data []byte) error {
}
func readAgentCwd(ctx HandlerCtx) ([]byte, error) {
return []byte(ctx.Session.Core.CWD() + "\n"), nil
return []byte(ctx.Agent.Cwd() + "\n"), nil
}
func writeAgentCwd(ctx HandlerCtx, data []byte) error {
@ -666,7 +671,12 @@ func writeAgentCwd(ctx HandlerCtx, data []byte) error {
if input == "" {
return nil
}
return ctx.Session.Core.SetCWD(input)
input = paths.ExpandHome(input)
if _, err := os.Stat(input); err != nil {
return fmt.Errorf("cwd: %w", err)
}
ctx.Agent.SetCWD(input)
return nil
}
func readAgentUsage(ctx HandlerCtx) ([]byte, error) {

View File

@ -233,6 +233,11 @@ var treeSpec = DirNode("/",
Request(requestAgentNew),
),
FileNode("idx", 0444,
Doc("Agent index. Format: name<tab>id<tab>cwd<tab>backend<tab>model per line."),
Read(readAgentIdx),
),
Each("{aname}", agentBindings,
Doc("Agent directory. Addressable by name or immutable ID."),

2
kde

@ -1 +1 @@
Subproject commit 2c7b5e6fc54b0eb78244650e6049a7b5e77a38b9
Subproject commit 73e262ffafe3cc32012e35e9f142261bd7707794

View File

@ -33,6 +33,7 @@ type PersistedAgent struct {
Profile string `json:"profile"`
Backend string `json:"backend"`
Model string `json:"model"`
CWD string `json:"cwd"`
Messages []backend.Message `json:"messages"`
TaskState *agent.TaskState `json:"taskState,omitempty"`
// Usage tracking
@ -65,7 +66,6 @@ func PersistSession(name string) error {
ps := PersistedSession{
ID: sess.ID(),
Name: sess.Name(),
CWD: sess.CWD(),
Remote: sess.Remote(),
Paused: sess.IsPaused(),
}
@ -77,6 +77,7 @@ func PersistSession(name string) error {
Profile: ag.Profile(),
Backend: ag.BackendName(),
Model: ag.ModelName(),
CWD: ag.Cwd(),
Messages: ag.Messages(),
TaskState: ag.TaskState(),
}
@ -192,51 +193,66 @@ func restoreSessionFromFile(path string) (*RestoredSession, error) {
}
func restoreMultiAgentSession(ps *PersistedSession) (*RestoredSession, error) {
cwd := ps.CWD
if cwd == "" {
cwd, _ = os.Getwd()
}
// For paused sessions, skip tool server setup entirely.
// It will be set up on Resume().
var infra *SessionInfra
if !ps.Paused {
var err error
infra, err = SetupToolServer(ToolServerConfig{
Ctx: serverCtx,
CWD: cwd,
RemoteTarget: ps.Remote,
Yolo: pkgYolo,
})
if err != nil {
return nil, err
}
}
// Create empty session
// Create empty session first
ctx, cancel := context.WithCancel(serverCtx)
sess := NewEmpty(ps.ID, ctx, cancel)
sess.SetName(ps.Name)
sess.SetRemote(ps.Remote)
if infra != nil {
sess.SetProc(infra.Proc)
sess.SetKeeper(infra.Keeper)
sess.SetToolsConn(infra.ToolsConn)
}
if ps.Paused {
sess.SetPaused(true)
}
var restoredAgents []RestoredAgent
// Build prompt env (works without live infra)
promptEnv := agent.PromptEnv(cwd)
if infra != nil {
promptEnv = infra.PromptEnv
}
// Restore each agent
// Restore each agent with its own CWD
for _, pa := range ps.Agents {
// Get agent's CWD (fall back to session CWD for backwards compat, then pwd)
cwd := pa.CWD
if cwd == "" {
cwd = ps.CWD
}
if cwd == "" {
cwd, _ = os.Getwd()
}
// For paused sessions, skip tool server setup
var infra *SessionInfra
if !ps.Paused {
var err error
// Reuse existing infra if available (from earlier agents)
var reuseFrom *InfraConfig
if sess.Proc() != nil {
reuseFrom = &InfraConfig{
Proc: sess.Proc(),
Keeper: sess.Keeper(),
ToolsConn: sess.ToolsConn(),
}
}
infra, err = SetupToolServer(ToolServerConfig{
Ctx: serverCtx,
CWD: cwd,
RemoteTarget: ps.Remote,
Yolo: pkgYolo,
ReuseFrom: reuseFrom,
})
if err != nil {
pkgLog.Error("restore agent %s: setup tool server: %v", pa.ID, err)
continue
}
// Store infra on session (first agent sets it up)
if sess.Proc() == nil {
sess.SetProc(infra.Proc)
sess.SetKeeper(infra.Keeper)
sess.SetToolsConn(infra.ToolsConn)
}
}
// Build prompt env
promptEnv := agent.PromptEnv(cwd)
if infra != nil {
promptEnv = infra.PromptEnv
}
cfg := LoadAgentConfig(pkgAgentsDir, pa.Profile, nil)
be, err := backend.NewWithName(pa.Backend)
@ -311,8 +327,8 @@ func restoreMultiAgentSession(ps *PersistedSession) (*RestoredSession, error) {
Register(sess.Name(), sess)
toolCount := 0
if infra != nil && infra.ToolsConn != nil {
if infos, err := infra.ToolsConn.ListTools(); err == nil {
if sess.ToolsConn() != nil {
if infos, err := sess.ToolsConn().ListTools(); err == nil {
toolCount = len(infos)
}
}

View File

@ -242,10 +242,12 @@ func BuildIndex() []byte {
for _, e := range entries {
sess := e.sess
connected := sess.IsConnected()
cwd := sess.CWD()
var be, model string
var cwd, be, model string
var agentList []string
for _, ag := range sess.Agents() {
if cwd == "" {
cwd = ag.Cwd()
}
if be == "" {
be = ag.BackendName()
model = ag.ModelName()
@ -259,6 +261,17 @@ func BuildIndex() []byte {
return []byte(sb.String())
}
// BuildAgentIndex builds the agent index for a session.
// Format: name<tab>id<tab>cwd<tab>backend<tab>model per line.
func BuildAgentIndex(sess *Session) []byte {
var sb strings.Builder
for _, ag := range sess.Agents() {
fmt.Fprintf(&sb, "%s\t%s\t%s\t%s\t%s\n",
ag.Name(), ag.ID(), ag.Cwd(), ag.BackendName(), ag.ModelName())
}
return []byte(sb.String())
}
// CreateEmpty creates an empty session (no agent).
// If remote is non-empty, spawns a remote tool server via SSH.
func CreateEmpty(name, remote string) (*Session, error) {
@ -324,7 +337,7 @@ func CreateAgent(sessName string, args []string) (*agent.Agent, error) {
// Parse args
profile := "default"
var agentName, backendName, modelName, remoteTarget string
var agentName, backendName, modelName, remoteTarget, cwdOverride string
for _, arg := range args {
if k, v, ok := strings.Cut(arg, "="); ok {
switch k {
@ -336,6 +349,8 @@ func CreateAgent(sessName string, args []string) (*agent.Agent, error) {
modelName = v
case "remote":
remoteTarget = v
case "cwd":
cwdOverride = v
}
} else if profile == "default" {
profile = arg
@ -346,7 +361,10 @@ func CreateAgent(sessName string, args []string) (*agent.Agent, error) {
uname := NextUname()
sessID := sess.ID()
cwd := sess.CWD()
if cwdOverride == "" {
return nil, fmt.Errorf("cwd= is required when creating an agent")
}
cwd := cwdOverride
// Set up tool server - reuse existing session infrastructure
infra, err := SetupToolServer(ToolServerConfig{
@ -377,14 +395,22 @@ func CreateAgent(sessName string, args []string) (*agent.Agent, error) {
env = append(env, infra.PromptEnv...)
rt := agent.BuildRuntime(cfg, infra.ToolsConn, cwd, env, layers.SystemPrompt, layers.EnvBlock)
// Apply backend/model overrides to runtime
if backendName != "" {
if be, err := backend.NewWithName(backendName); err == nil {
rt.Backend = be
}
// Create backend - use override if provided, otherwise use config or env default
beName := backendName
if beName == "" {
beName = rt.CfgBackend
}
be, err := backend.NewWithName(beName)
if err != nil {
return nil, fmt.Errorf("create backend: %w", err)
}
rt.Backend = be
// Apply model override if provided
if modelName != "" {
rt.Backend.SetModel(modelName)
} else if rt.CfgModel != "" {
rt.Backend.SetModel(rt.CfgModel)
}
// Create agent

View File

@ -272,18 +272,6 @@ func (a *Session) SetEnv(key, value string) {
}
}
// CWD returns the current working directory for tool execution
// (from the first agent).
func (a *Session) CWD() string {
if len(a.agents) > 0 {
if c := a.agents[0].Cwd(); c != "" {
return c
}
}
wd, _ := os.Getwd()
return wd
}
// Remote returns the remote target (e.g., SSH host) if any.
func (a *Session) Remote() string {
return a.remote
@ -294,20 +282,6 @@ func (a *Session) SetRemote(remote string) {
a.remote = remote
}
// SetCWD validates and sets the working directory on all agents.
func (a *Session) SetCWD(dir string) error {
dir = paths.ExpandHome(dir)
if dir != "" {
if _, err := os.Stat(dir); err != nil {
return fmt.Errorf("cwd: %w", err)
}
}
for _, ag := range a.agents {
ag.SetCWD(dir)
}
return nil
}
// SetSessionID renames the session.
func (a *Session) SetSessionID(newID string) error {
oldID := a.id
@ -555,23 +529,41 @@ func (a *Session) Resume() error {
}
a.toolsConn = conn
} else {
// Session was restored paused without infra - set up now.
cwd := a.CWD()
infra, err := SetupToolServer(ToolServerConfig{
Ctx: ctx,
CWD: cwd,
RemoteTarget: a.remote,
Yolo: pkgYolo,
})
if err != nil {
return fmt.Errorf("resume setup failed: %w", err)
}
a.proc = infra.Proc
a.keeper = infra.Keeper
a.toolsConn = infra.ToolsConn
// Update agents with the new tool server connection.
for _, ag := range a.agents {
// Session was restored paused without infra - set up per-agent.
for i, ag := range a.agents {
cwd := ag.Cwd()
if cwd == "" {
cwd, _ = os.Getwd()
}
// First agent spawns the tool server, others reuse
var reuseFrom *InfraConfig
if a.proc != nil {
reuseFrom = &InfraConfig{
Proc: a.proc,
Keeper: a.keeper,
ToolsConn: a.toolsConn,
}
}
infra, err := SetupToolServer(ToolServerConfig{
Ctx: ctx,
CWD: cwd,
RemoteTarget: a.remote,
Yolo: pkgYolo,
ReuseFrom: reuseFrom,
})
if err != nil {
return fmt.Errorf("resume setup for agent %s failed: %w", ag.ID(), err)
}
// First agent sets session infra
if i == 0 {
a.proc = infra.Proc
a.keeper = infra.Keeper
a.toolsConn = infra.ToolsConn
}
ag.SetToolServer(infra.NewToolServer, infra.ToolsConn)
}
}