fix naming problems
This commit is contained in:
parent
088aa38f1c
commit
119e673299
|
|
@ -23,7 +23,8 @@ type Agent struct {
|
|||
history *History
|
||||
runtime *Runtime
|
||||
cfg agentConfig // per-turn config built from runtime
|
||||
agentName string
|
||||
name string // display name (defaults to id)
|
||||
profile string // config profile name (e.g. "default")
|
||||
agentsDir string
|
||||
baseLayers []string // system prompt layers for /agent reloads
|
||||
promptEnvExtra []string // PRIME_* vars for prompt resolution
|
||||
|
|
@ -34,28 +35,28 @@ type Agent struct {
|
|||
resultCache sync.Map
|
||||
|
||||
// Execution state — owned by the agent, protected by stateMu.
|
||||
state string // "idle", "thinking", "calling: <tool>"
|
||||
reply string // last assistant response
|
||||
cwd string // working directory for tool execution
|
||||
id string // agent identity (unique principal)
|
||||
fifo Fifo // prompt queue
|
||||
state string // "idle", "thinking", "calling: <tool>"
|
||||
reply string // last assistant response
|
||||
cwd string // working directory for tool execution
|
||||
id string // agent identity (unique principal)
|
||||
fifo Fifo // prompt queue
|
||||
toolCallCount atomic.Int64
|
||||
pendingInject atomic.Pointer[string]
|
||||
submitMu sync.Mutex // serializes Submit calls (commands + turns)
|
||||
stateMu sync.RWMutex
|
||||
changeMu sync.Mutex
|
||||
changeCond *sync.Cond
|
||||
submitMu sync.Mutex // serializes Submit calls (commands + turns)
|
||||
stateMu sync.RWMutex
|
||||
changeMu sync.Mutex
|
||||
changeCond *sync.Cond
|
||||
|
||||
// Event handler — receives events from this agent.
|
||||
output EventHandler
|
||||
log *olog.Logger
|
||||
auditLog *olog.Logger
|
||||
sessionID string // the owning session's ID
|
||||
output EventHandler
|
||||
log *olog.Logger
|
||||
auditLog *olog.Logger
|
||||
sessionID string // the owning session's ID
|
||||
startupMessages []string
|
||||
readPlanStep func() string
|
||||
saveSession func() // trigger debounced persistence
|
||||
flushSave func() // immediately flush persistence
|
||||
turnError func(ctx context.Context, errType, errMsg string) HookResult // overridable for tests
|
||||
readPlanStep func() string
|
||||
saveSession func() // trigger debounced persistence
|
||||
flushSave func() // immediately flush persistence
|
||||
turnError func(ctx context.Context, errType, errMsg string) HookResult // overridable for tests
|
||||
}
|
||||
|
||||
// Backend returns the active backend from the runtime.
|
||||
|
|
@ -63,13 +64,16 @@ func (ag *Agent) Backend() backend.Backend {
|
|||
return ag.runtime.Backend
|
||||
}
|
||||
|
||||
// Name returns the agent's name.
|
||||
func (ag *Agent) Name() string { return ag.agentName }
|
||||
// Name returns the agent's display name.
|
||||
func (ag *Agent) Name() string { return ag.name }
|
||||
|
||||
// Profile returns the config profile name (e.g. "default").
|
||||
func (ag *Agent) Profile() string { return ag.profile }
|
||||
|
||||
// SetName changes the agent's display name without reloading config.
|
||||
func (ag *Agent) SetName(name string) {
|
||||
ag.stateMu.Lock()
|
||||
ag.agentName = name
|
||||
ag.name = name
|
||||
ag.stateMu.Unlock()
|
||||
}
|
||||
|
||||
|
|
@ -250,7 +254,7 @@ func (ag *Agent) SaveTo(path, name, cwd string) error {
|
|||
if ag.history == nil {
|
||||
return fmt.Errorf("no active session")
|
||||
}
|
||||
return ag.history.saveTo(path, name, ag.agentName, cwd)
|
||||
return ag.history.saveTo(path, name, ag.Name(), cwd)
|
||||
}
|
||||
|
||||
// Restore restores agent history from a persisted session.
|
||||
|
|
@ -363,7 +367,7 @@ func (ag *Agent) SaveFull(path, sessionID, cwd, remote string) error {
|
|||
if ag.history == nil {
|
||||
return fmt.Errorf("no active session")
|
||||
}
|
||||
return ag.history.saveToFull(path, sessionID, ag.agentName,
|
||||
return ag.history.saveToFull(path, sessionID, ag.Name(), ag.profile,
|
||||
ag.runtime.Backend.Name(), ag.runtime.Backend.Model(), cwd, remote)
|
||||
}
|
||||
|
||||
|
|
@ -521,11 +525,21 @@ func (ag *Agent) ListDetached() []DetachedInfo {
|
|||
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 }
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
|
@ -535,7 +549,9 @@ func (ag *Agent) ListDetached() []DetachedInfo {
|
|||
// 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 }
|
||||
type signaler interface {
|
||||
SignalDetached(int, syscall.Signal) error
|
||||
}
|
||||
if sg, ok := srv.(signaler); ok {
|
||||
return sg.SignalDetached(pid, syscall.Signal(signal))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -146,7 +146,7 @@ func (ag *Agent) cmdAgents(_ context.Context, _ []string) {
|
|||
}
|
||||
seen[name] = true
|
||||
marker := " "
|
||||
if name == ag.agentName {
|
||||
if name == ag.profile {
|
||||
marker = "* "
|
||||
}
|
||||
ag.emit(infoEvent(marker + name))
|
||||
|
|
@ -160,7 +160,7 @@ func (ag *Agent) cmdAgents(_ context.Context, _ []string) {
|
|||
|
||||
func (ag *Agent) cmdAgent(_ context.Context, args []string) {
|
||||
if len(args) == 0 {
|
||||
ag.emit(infoEvent("active agent: " + ag.agentName))
|
||||
ag.emit(infoEvent("active profile: " + ag.profile + " (agent: " + ag.Name() + ")"))
|
||||
return
|
||||
}
|
||||
if ag.IsRunning() {
|
||||
|
|
@ -201,7 +201,7 @@ func (ag *Agent) cmdAgent(_ context.Context, args []string) {
|
|||
}
|
||||
}
|
||||
ag.runtime = rt
|
||||
ag.agentName = name
|
||||
ag.profile = name
|
||||
ag.history = nil
|
||||
ag.wireToolsChanged()
|
||||
ag.notifyChange()
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@ package agent
|
|||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"crypto/rand"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"slices"
|
||||
|
|
@ -38,6 +38,7 @@ Be concise. Capture what another LLM needs to seamlessly continue.`
|
|||
type PersistedAgent struct {
|
||||
ID string `json:"id"`
|
||||
Agent string `json:"agent,omitempty"`
|
||||
Profile string `json:"profile,omitempty"`
|
||||
Backend string `json:"backend,omitempty"`
|
||||
Model string `json:"model,omitempty"`
|
||||
CWD string `json:"cwd,omitempty"`
|
||||
|
|
@ -95,8 +96,8 @@ func (ts *TaskState) render() string {
|
|||
}
|
||||
|
||||
// saveTo writes the full message history to path as JSON.
|
||||
func (s *History) saveTo(path, id, agentName, cwd string) error {
|
||||
return s.saveToFull(path, id, agentName, "", "", cwd, "")
|
||||
func (s *History) saveTo(path, id, displayName, cwd string) error {
|
||||
return s.saveToFull(path, id, displayName, "", "", "", cwd, "")
|
||||
}
|
||||
|
||||
// saveToFull writes session state including backend/model info.
|
||||
|
|
@ -117,10 +118,11 @@ func sanitizeMessages(msgs []backend.Message) []backend.Message {
|
|||
return out
|
||||
}
|
||||
|
||||
func (s *History) saveToFull(path, id, agentName, backendName, modelName, cwd, remote string) error {
|
||||
func (s *History) saveToFull(path, id, displayName, profile, backendName, modelName, cwd, remote string) error {
|
||||
ps := PersistedAgent{
|
||||
ID: id,
|
||||
Agent: agentName,
|
||||
Agent: displayName,
|
||||
Profile: profile,
|
||||
Backend: backendName,
|
||||
Model: modelName,
|
||||
CWD: cwd,
|
||||
|
|
@ -654,7 +656,7 @@ func (s *History) contextDebug() string {
|
|||
// NewResponseID generates a UUIDv4 for a single assistant response.
|
||||
func NewResponseID() string {
|
||||
b := make([]byte, 16)
|
||||
rand.Read(b) //nolint:errcheck
|
||||
rand.Read(b) //nolint:errcheck
|
||||
b[6] = (b[6] & 0x0f) | 0x40 // version 4
|
||||
b[8] = (b[8] & 0x3f) | 0x80 // variant 10xx
|
||||
return fmt.Sprintf("%08x-%04x-%04x-%04x-%012x",
|
||||
|
|
|
|||
|
|
@ -12,9 +12,9 @@ import (
|
|||
type AgentCfg struct {
|
||||
History *History
|
||||
Runtime *Runtime
|
||||
AgentName string
|
||||
Profile string // config profile name (e.g. "default" → agents/default.json)
|
||||
AgentsDir string
|
||||
AgentID string // unique agent identity
|
||||
AgentID string // unique agent identity (uname)
|
||||
CWD string // working directory for tool execution
|
||||
BaseLayers []string
|
||||
PromptEnvExtra []string
|
||||
|
|
@ -35,7 +35,8 @@ func NewAgent(cfg AgentCfg) *Agent {
|
|||
ag := &Agent{
|
||||
history: cfg.History,
|
||||
runtime: cfg.Runtime,
|
||||
agentName: cfg.AgentName,
|
||||
profile: cfg.Profile,
|
||||
name: cfg.AgentID, // display name defaults to uname
|
||||
agentsDir: cfg.AgentsDir,
|
||||
id: cfg.AgentID,
|
||||
cwd: cfg.CWD,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,60 @@
|
|||
package agent
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"ollie/backend"
|
||||
olog "ollie/log"
|
||||
"ollie/toolsrv"
|
||||
)
|
||||
|
||||
// AgentCfg is the configuration for constructing a new Agent.
|
||||
type AgentCfg struct {
|
||||
History *History
|
||||
Runtime *Runtime
|
||||
AgentName string
|
||||
AgentsDir string
|
||||
AgentID string // unique agent identity
|
||||
CWD string // working directory for tool execution
|
||||
BaseLayers []string
|
||||
PromptEnvExtra []string
|
||||
NewToolServer func() toolsrv.Runner
|
||||
NewBackend func(string) (backend.Backend, error)
|
||||
Output EventHandler
|
||||
Log *olog.Logger
|
||||
AuditLog *olog.Logger
|
||||
SessionID string
|
||||
StartupMsgs []string
|
||||
ReadPlanStep func() string
|
||||
SaveSession func()
|
||||
FlushSave func()
|
||||
}
|
||||
|
||||
// NewAgent constructs an Agent from the given configuration.
|
||||
func NewAgent(cfg AgentCfg) *Agent {
|
||||
ag := &Agent{
|
||||
history: cfg.History,
|
||||
runtime: cfg.Runtime,
|
||||
agentName: cfg.AgentName,
|
||||
agentsDir: cfg.AgentsDir,
|
||||
id: cfg.AgentID,
|
||||
cwd: cfg.CWD,
|
||||
baseLayers: cfg.BaseLayers,
|
||||
promptEnvExtra: cfg.PromptEnvExtra,
|
||||
newToolServer: cfg.NewToolServer,
|
||||
newBackend: cfg.NewBackend,
|
||||
output: cfg.Output,
|
||||
log: cfg.Log,
|
||||
auditLog: cfg.AuditLog,
|
||||
sessionID: cfg.SessionID,
|
||||
startupMessages: cfg.StartupMsgs,
|
||||
readPlanStep: cfg.ReadPlanStep,
|
||||
saveSession: cfg.SaveSession,
|
||||
flushSave: cfg.FlushSave,
|
||||
state: "idle",
|
||||
}
|
||||
ag.changeCond = sync.NewCond(&ag.changeMu)
|
||||
ag.turnError = ag.defaultTurnError
|
||||
ag.wireToolsChanged()
|
||||
return ag
|
||||
}
|
||||
|
|
@ -415,7 +415,9 @@ func (ag *Agent) autoWarnLimit(ctx context.Context) int {
|
|||
func (ag *Agent) spawnContext(ctx context.Context) string {
|
||||
result := ag.runtime.Hooks.Run(ctx, HookAgentSpawn, map[string]string{
|
||||
"session_id": ag.sessionID,
|
||||
"agent": ag.agentName,
|
||||
"agent_id": ag.ID(),
|
||||
"profile": ag.Profile(),
|
||||
"agent": ag.Name(),
|
||||
"cwd": ag.effectiveCwd(),
|
||||
"model": ag.runtime.Backend.Model(),
|
||||
}, ag.log)
|
||||
|
|
|
|||
|
|
@ -102,7 +102,9 @@ type Agent struct {
|
|||
history *History
|
||||
runtime *Runtime
|
||||
cfg agentConfig
|
||||
agentName string
|
||||
profileName string
|
||||
agentID string
|
||||
displayName string
|
||||
agentsDir string
|
||||
baseLayers []string
|
||||
promptEnvExtra []string
|
||||
|
|
@ -127,7 +129,9 @@ The full construction config:
|
|||
|---|---|
|
||||
| `History` | Pre-existing session history to restore (nil = new) |
|
||||
| `Runtime` | Pre-built *Runtime (tools, hooks, prompt, params) |
|
||||
| `AgentName` | Name of the active agent config |
|
||||
| `Profile` | Name of the active agent configuration profile |
|
||||
| `ID` | Immutable agent ID (`uname`) |
|
||||
| `Name` | Mutable display name |
|
||||
| `AgentsDir` | Path to agent JSON directory |
|
||||
| `SessionsDir` | Path to persisted session files |
|
||||
| `SessionID` | Unique session identifier |
|
||||
|
|
|
|||
|
|
@ -24,8 +24,8 @@ to its `name` file moves the entire directory subtree.
|
|||
| `/session/{name}/agent/{aid}/cfg` | Agent config KV: backend, model, agent, cwd, params |
|
||||
| `/session/{name}/agent/{aid}/statewait` | **Blocking read** — returns when state changes |
|
||||
| `/session/{name}/agent/{aid}/ctl` | Control commands: `stop`, `kill`, `compact`, `rn <name>` |
|
||||
| `/session/{name}/agent/{aid}/id` | Immutable agent UUID |
|
||||
| `/session/{name}/agent/{aid}/name` | Mutable agent name (write to rename) |
|
||||
| `/session/{name}/agent/{aid}/id` | Immutable agent ID (uname) |
|
||||
| `/session/{name}/agent/{aid}/name` | Mutable display name (write to rename) |
|
||||
|
||||
Global request-response files provide stateless operations without sessions:
|
||||
|
||||
|
|
|
|||
|
|
@ -28,9 +28,6 @@ func listAgents(ctx HandlerCtx) ([]FsNodeDecl, error) {
|
|||
}
|
||||
|
||||
dirName := a.ID()
|
||||
if n := a.Name(); n != "" {
|
||||
dirName = n
|
||||
}
|
||||
|
||||
agentDir := Dir(dirName,
|
||||
// prompt — submit a prompt to the agent
|
||||
|
|
@ -227,37 +224,33 @@ func listAgents(ctx HandlerCtx) ([]FsNodeDecl, error) {
|
|||
return []byte("#!/bin/sh\nexec tail -f \"$(dirname \"$0\")/chat\"\n"), nil
|
||||
})),
|
||||
|
||||
// tools — list all available tools (read) / load a tool (write)
|
||||
Leaf("tools", 0666,
|
||||
Read(func(ctx HandlerCtx) ([]byte, error) {
|
||||
// Use the session's dedicated tools management connection,
|
||||
// NOT a.ToolServer() which would deadlock if the agent
|
||||
// loop is mid-execution (it holds the Conn mutex).
|
||||
if s.toolsConn == nil {
|
||||
return []byte("(no tool server)\n"), nil
|
||||
// tools — list all available tools (read) / load a tool (write)
|
||||
Leaf("tools", 0666,
|
||||
Read(func(ctx HandlerCtx) ([]byte, error) {
|
||||
// Use the session's dedicated tools management connection,
|
||||
// NOT a.ToolServer() which would deadlock if the agent
|
||||
// loop is mid-execution (it holds the Conn mutex).
|
||||
if s.toolsConn == nil {
|
||||
return []byte("(no tool server)\n"), nil
|
||||
}
|
||||
loaded, err := s.toolsConn.ListTools()
|
||||
if err != nil {
|
||||
return []byte(fmt.Sprintf("(error: %v)\n", err)), nil
|
||||
}
|
||||
var sb strings.Builder
|
||||
for _, ti := range loaded {
|
||||
if ti.Description != "" {
|
||||
fmt.Fprintf(&sb, "%-20s %s\n", ti.Name, ti.Description)
|
||||
} else {
|
||||
sb.WriteString(ti.Name + "\n")
|
||||
}
|
||||
loaded, err := s.toolsConn.ListTools()
|
||||
if err != nil {
|
||||
return []byte(fmt.Sprintf("(error: %v)\n", err)), nil
|
||||
}
|
||||
var sb strings.Builder
|
||||
for _, ti := range loaded {
|
||||
if ti.Description != "" {
|
||||
fmt.Fprintf(&sb, "%-20s %s\n", ti.Name, ti.Description)
|
||||
} else {
|
||||
sb.WriteString(ti.Name + "\n")
|
||||
}
|
||||
}
|
||||
return []byte(sb.String()), nil
|
||||
}),
|
||||
Write(func(ctx HandlerCtx, data []byte) error {
|
||||
return s.loadTool(string(data), a)
|
||||
}),
|
||||
),
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
return []byte(sb.String()), nil
|
||||
}),
|
||||
Write(func(ctx HandlerCtx, data []byte) error {
|
||||
return s.loadTool(string(data), a)
|
||||
}),
|
||||
),
|
||||
|
||||
// name — agent name
|
||||
Leaf("name", 0666,
|
||||
|
|
@ -284,6 +277,10 @@ func listAgents(ctx HandlerCtx) ([]FsNodeDecl, error) {
|
|||
return listProcesses(ctx, s, a)
|
||||
}),
|
||||
)
|
||||
agentDir.Rename = func(newName string) error {
|
||||
a.SetName(newName)
|
||||
return nil
|
||||
}
|
||||
out = append(out, agentDir)
|
||||
}
|
||||
return out, nil
|
||||
|
|
@ -332,7 +329,9 @@ func agentCfgContent(a *agent.Agent, s *Session, al *AgentLog) string {
|
|||
fmt.Fprintf(&sb, "name=%s\n", s.id)
|
||||
fmt.Fprintf(&sb, "backend=%s\n", a.BackendName())
|
||||
fmt.Fprintf(&sb, "model=%s\n", a.ModelName())
|
||||
fmt.Fprintf(&sb, "agent=%s\n", a.Name())
|
||||
fmt.Fprintf(&sb, "id=%s\n", a.ID())
|
||||
fmt.Fprintf(&sb, "profile=%s\n", a.Profile())
|
||||
fmt.Fprintf(&sb, "displayName=%s\n", a.Name())
|
||||
fmt.Fprintf(&sb, "cwd=%s\n", s.Core.CWD())
|
||||
fmt.Fprintf(&sb, "remote=%s\n", al.remote)
|
||||
fmt.Fprintf(&sb, "maxTokens=%d\n", p.MaxTokens)
|
||||
|
|
@ -354,6 +353,9 @@ func handleAgentCfg(a *agent.Agent, s *Session, al *AgentLog, data []byte) error
|
|||
}
|
||||
switch parts[0] {
|
||||
case "name":
|
||||
if strings.TrimSpace(parts[1]) != "" {
|
||||
a.SetName(strings.TrimSpace(parts[1]))
|
||||
}
|
||||
case "backend":
|
||||
case "model":
|
||||
default:
|
||||
|
|
@ -396,6 +398,10 @@ func handleAgentCtl(a *agent.Agent, s *Session, data []byte) error {
|
|||
s.Core.SetActiveAgent(strings.Join(cmd[1:], " "))
|
||||
}
|
||||
}
|
||||
case "name":
|
||||
if len(cmd) > 1 {
|
||||
a.SetName(strings.Join(cmd[1:], " "))
|
||||
}
|
||||
case "cwd":
|
||||
if len(cmd) > 1 {
|
||||
a.SetCWD(strings.Join(cmd[1:], " "))
|
||||
|
|
|
|||
|
|
@ -261,6 +261,13 @@ func deleteDir(d *FsNodeDecl, name string, ctx HandlerCtx) error {
|
|||
}
|
||||
|
||||
func renameDir(d *FsNodeDecl, old, new string, ctx HandlerCtx) error {
|
||||
child := findChild(d, old, ctx)
|
||||
if child == nil {
|
||||
return fmt.Errorf("%s: not found", old)
|
||||
}
|
||||
if child.Rename != nil {
|
||||
return child.Rename(new)
|
||||
}
|
||||
return fmt.Errorf("rename not supported")
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -129,9 +129,10 @@ type FsNodeDecl struct {
|
|||
// Request — rdwr: write triggers result, read returns it once.
|
||||
Request func(ctx HandlerCtx, data []byte) ([]byte, error)
|
||||
|
||||
// Tcreate + Tremove (directory ops).
|
||||
// Tcreate + Tremove + Trename (directory ops).
|
||||
Create func(ctx HandlerCtx, name string) error
|
||||
Remove func(ctx HandlerCtx) error
|
||||
Rename func(newName string) error
|
||||
}
|
||||
|
||||
// NodeOption configures a FsNodeDecl at construction time.
|
||||
|
|
|
|||
|
|
@ -149,7 +149,7 @@ func Kill(rs *rootState, id string) {
|
|||
var agentNames []string
|
||||
if sess.Core != nil {
|
||||
for _, ag := range sess.Core.Agents() {
|
||||
agentNames = append(agentNames, ag.Name())
|
||||
agentNames = append(agentNames, ag.ID())
|
||||
}
|
||||
sess.Core.Close()
|
||||
}
|
||||
|
|
@ -366,12 +366,11 @@ func CreateFromRoot(root *Tree, args []string) (string, error) {
|
|||
func CreateAgent(rs *rootState, sessName string, args []string) error {
|
||||
backendOverride := ""
|
||||
modelOverride := ""
|
||||
agentName := ""
|
||||
agentAlias := ""
|
||||
profile := ""
|
||||
name := ""
|
||||
cwd := ""
|
||||
remoteTarget := ""
|
||||
systemPromptOverride := ""
|
||||
name := ""
|
||||
for _, arg := range args {
|
||||
k, v, ok := strings.Cut(arg, "=")
|
||||
if !ok {
|
||||
|
|
@ -388,9 +387,7 @@ func CreateAgent(rs *rootState, sessName string, args []string) error {
|
|||
case "model":
|
||||
modelOverride = v
|
||||
case "agent":
|
||||
agentName = v
|
||||
case "agent_alias":
|
||||
agentAlias = v
|
||||
profile = v
|
||||
case "cwd":
|
||||
cwd = v
|
||||
case "remote":
|
||||
|
|
@ -398,12 +395,9 @@ func CreateAgent(rs *rootState, sessName string, args []string) error {
|
|||
case "system_prompt":
|
||||
systemPromptOverride = v
|
||||
default:
|
||||
return fmt.Errorf("unknown option %q (valid: name, backend, model, agent, agent_alias, cwd, remote, system_prompt)", k)
|
||||
return fmt.Errorf("unknown option %q (valid: name, backend, model, agent, cwd, remote, system_prompt)", k)
|
||||
}
|
||||
}
|
||||
if name != "" && agentAlias == "" {
|
||||
agentAlias = name
|
||||
}
|
||||
|
||||
rs.mu.RLock()
|
||||
sess, ok := rs.sessions[sessName]
|
||||
|
|
@ -415,11 +409,11 @@ func CreateAgent(rs *rootState, sessName string, args []string) error {
|
|||
sess.SetName(sessName)
|
||||
}
|
||||
|
||||
if agentName == "" {
|
||||
if profile == "" {
|
||||
if v := os.Getenv("OLLIE_DEFAULT_AGENT"); v != "" {
|
||||
agentName = v
|
||||
profile = v
|
||||
} else {
|
||||
agentName = "default"
|
||||
profile = "default"
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -438,7 +432,7 @@ func CreateAgent(rs *rootState, sessName string, args []string) error {
|
|||
uname := nextUname(rs)
|
||||
sessID := sess.id
|
||||
|
||||
cfg := LoadAgentConfig(rs.cfg.AgentsDir, agentName, nil)
|
||||
cfg := LoadAgentConfig(rs.cfg.AgentsDir, profile, nil)
|
||||
if cfg != nil {
|
||||
if backendOverride == "" && cfg.Backend != "" {
|
||||
backendOverride = cfg.Backend
|
||||
|
|
@ -561,7 +555,7 @@ func CreateAgent(rs *rootState, sessName string, args []string) error {
|
|||
if sess.Core == nil {
|
||||
core := coresession.New(coresession.Config{
|
||||
Backend: be,
|
||||
AgentName: agentName,
|
||||
Profile: profile,
|
||||
AgentsDir: rs.cfg.AgentsDir,
|
||||
SessionsDir: rs.cfg.SessionsDir,
|
||||
SessionID: sessID,
|
||||
|
|
@ -589,7 +583,7 @@ func CreateAgent(rs *rootState, sessName string, args []string) error {
|
|||
ag := agent.NewAgent(agent.AgentCfg{
|
||||
History: nil,
|
||||
Runtime: rt,
|
||||
AgentName: agentName,
|
||||
Profile: profile,
|
||||
AgentsDir: rs.cfg.AgentsDir,
|
||||
AgentID: uname,
|
||||
CWD: cwd,
|
||||
|
|
@ -620,16 +614,14 @@ func CreateAgent(rs *rootState, sessName string, args []string) error {
|
|||
rs.mu.Unlock()
|
||||
|
||||
ag := sess.Core.Agent()
|
||||
ag.Submit(sess.SessionCtx, "/agent "+agentName)
|
||||
if agentAlias != "" {
|
||||
ag.SetName(agentAlias)
|
||||
} else {
|
||||
ag.SetName(uname)
|
||||
ag.Submit(sess.SessionCtx, "/agent "+profile)
|
||||
if name != "" {
|
||||
ag.SetName(name)
|
||||
}
|
||||
|
||||
rs.cfg.Log.Info("created agent in session %s (backend=%s model=%s agent=%s)",
|
||||
sessName, ag.BackendName(), ag.ModelName(), ag.Name())
|
||||
PostEvent("A new session/" + sessName + "/agent/" + ag.Name())
|
||||
PostEvent("A new session/" + sessName + "/agent/" + ag.ID())
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -645,5 +637,5 @@ func CreateAgentFromRoot(root *Tree, sessName string, args []string) (string, er
|
|||
if sess == nil || sess.Core == nil {
|
||||
return "", fmt.Errorf("agent not created")
|
||||
}
|
||||
return sess.Core.Agent().Name(), nil
|
||||
return sess.Core.Agent().ID(), nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ type Config struct {
|
|||
AgentsDir string
|
||||
SessionsDir string
|
||||
MkdirAll func(string, os.FileMode) error
|
||||
NewCore func(sessionID, agentName, cwd string) (*coresession.Session, error)
|
||||
NewCore func(sessionID, profile, cwd string) (*coresession.Session, error)
|
||||
Yolo bool
|
||||
SkillsRegistry *skills.Registry
|
||||
ModelCache *ModelCache
|
||||
|
|
|
|||
|
|
@ -114,13 +114,16 @@ func restoreSession(rs *rootState, ps *agent.PersistedAgent) error {
|
|||
if cwd == "" {
|
||||
cwd, _ = os.Getwd()
|
||||
}
|
||||
agentName := ps.Agent
|
||||
if agentName == "" {
|
||||
agentName = "default"
|
||||
profile := ps.Profile
|
||||
if profile == "" {
|
||||
profile = ps.Agent
|
||||
if profile == "" {
|
||||
profile = "default"
|
||||
}
|
||||
}
|
||||
sessID := ps.ID
|
||||
|
||||
cfg := LoadAgentConfig(rs.cfg.AgentsDir, agentName, nil)
|
||||
cfg := LoadAgentConfig(rs.cfg.AgentsDir, profile, nil)
|
||||
|
||||
backendName := ps.Backend
|
||||
if backendName == "" && cfg != nil && cfg.Backend != "" {
|
||||
|
|
@ -141,7 +144,10 @@ func restoreSession(rs *rootState, ps *agent.PersistedAgent) error {
|
|||
be.SetModel(modelName)
|
||||
}
|
||||
|
||||
uname := nextUname(rs)
|
||||
uname := ps.ID
|
||||
if uname == "" {
|
||||
uname = nextUname(rs)
|
||||
}
|
||||
var newToolServer func() toolsrv.Runner
|
||||
var promptEnv []string
|
||||
var proc *toolsrv.Process
|
||||
|
|
@ -230,7 +236,7 @@ func restoreSession(rs *rootState, ps *agent.PersistedAgent) error {
|
|||
al := NewAgentLog(remoteTarget)
|
||||
core := coresession.New(coresession.Config{
|
||||
Backend: be,
|
||||
AgentName: agentName,
|
||||
Profile: profile,
|
||||
AgentsDir: rs.cfg.AgentsDir,
|
||||
SessionsDir: rs.cfg.SessionsDir,
|
||||
SessionID: sessID,
|
||||
|
|
@ -266,6 +272,12 @@ func restoreSession(rs *rootState, ps *agent.PersistedAgent) error {
|
|||
// Use the multiplexed connection for both agent execution and 9P management.
|
||||
sess.toolsConn = toolSrv
|
||||
|
||||
// Restore agent display name from persisted session.
|
||||
ag := sess.Core.Agent()
|
||||
if ps.Agent != "" {
|
||||
ag.SetName(ps.Agent)
|
||||
}
|
||||
|
||||
replayMessagesToLog(sess.AgentLog(), ps.Messages)
|
||||
|
||||
// Pre-load autoLoad tools into the remote.
|
||||
|
|
@ -283,6 +295,13 @@ func restoreSession(rs *rootState, ps *agent.PersistedAgent) error {
|
|||
rs.sessions[name] = sess
|
||||
rs.mu.Unlock()
|
||||
|
||||
rs.cfg.Log.Info("restored session %s (backend=%s model=%s agent=%s)", sessID, backendName, modelName, agentName)
|
||||
toolCount := 0
|
||||
if toolSrv != nil {
|
||||
if infos, err := toolSrv.ListTools(); err == nil {
|
||||
toolCount = len(infos)
|
||||
}
|
||||
}
|
||||
rs.cfg.Log.Info("restored session %s: cfg=%v autoload=%d tools=%d (backend=%s model=%s agent=%s)",
|
||||
sessID, cfg != nil, func() int { if cfg != nil { return len(cfg.AutoLoad) }; return -1 }(), toolCount, backendName, modelName, profile)
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -123,7 +123,7 @@ func listSessions(ctx HandlerCtx) ([]FsNodeDecl, error) {
|
|||
if err := CreateAgent(rs, s.Name(), args); err != nil {
|
||||
return nil, fmt.Errorf("create agent: %w", err)
|
||||
}
|
||||
return []byte(s.Core.Agent().Name() + "\n"), nil
|
||||
return []byte(s.Core.Agent().ID() + "\n"), nil
|
||||
})),
|
||||
|
||||
// {aid} — dynamic per-agent directory
|
||||
|
|
@ -196,4 +196,4 @@ func requestAgentNew(ctx HandlerCtx, data []byte) ([]byte, error) {
|
|||
// used as a standalone — agent creation is handled inline in the
|
||||
// session's agent/new handler above.
|
||||
return nil, fmt.Errorf("not available")
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ import (
|
|||
type Config struct {
|
||||
Backend backend.Backend
|
||||
ModelName string
|
||||
AgentName string
|
||||
Profile string // config profile name (e.g. "default")
|
||||
AgentsDir string
|
||||
SessionsDir string
|
||||
SessionID string
|
||||
|
|
@ -44,7 +44,7 @@ type Config struct {
|
|||
|
||||
// Session is the concrete session type. It owns session-level state and
|
||||
// delegates agent operations to its owned agents. Operations that act on
|
||||
// "the agent" (Agent(), CWD, etc.) use the agent identified by activeAgentName.
|
||||
// "the agent" (Agent(), CWD, etc.) use the agent identified by immutable activeAgentID.
|
||||
type Session struct {
|
||||
id string
|
||||
envMu sync.RWMutex
|
||||
|
|
@ -52,14 +52,14 @@ type Session struct {
|
|||
plan []byte
|
||||
prevPrompt string
|
||||
|
||||
agents []*agent.Agent
|
||||
activeAgentName string // name of the agent returned by Agent()
|
||||
log *olog.Logger
|
||||
sessionsDir string
|
||||
listHandlers map[string]func() []string
|
||||
remote string
|
||||
mu sync.RWMutex
|
||||
auditLog *olog.Logger
|
||||
agents []*agent.Agent
|
||||
activeAgentID string // immutable ID of the agent returned by Agent()
|
||||
log *olog.Logger
|
||||
sessionsDir string
|
||||
listHandlers map[string]func() []string
|
||||
remote string
|
||||
mu sync.RWMutex
|
||||
auditLog *olog.Logger
|
||||
|
||||
saveMu sync.Mutex
|
||||
saveDirty bool
|
||||
|
|
@ -69,7 +69,7 @@ type Session struct {
|
|||
// NewSessionID generates a UUIDv4 session identifier.
|
||||
func NewSessionID() string {
|
||||
b := make([]byte, 16)
|
||||
rand.Read(b) //nolint:errcheck
|
||||
rand.Read(b) //nolint:errcheck
|
||||
b[6] = (b[6] & 0x0f) | 0x40 // version 4
|
||||
b[8] = (b[8] & 0x3f) | 0x80 // variant 10xx
|
||||
return fmt.Sprintf("%08x-%04x-%04x-%04x-%012x",
|
||||
|
|
@ -146,7 +146,7 @@ func New(cfg Config) *Session {
|
|||
ag := agent.NewAgent(agent.AgentCfg{
|
||||
History: cfg.History,
|
||||
Runtime: rt,
|
||||
AgentName: cfg.AgentName,
|
||||
Profile: cfg.Profile,
|
||||
AgentsDir: cfg.AgentsDir,
|
||||
AgentID: cfg.AgentID,
|
||||
CWD: paths.ExpandHome(cfg.CWD),
|
||||
|
|
@ -162,23 +162,23 @@ func New(cfg Config) *Session {
|
|||
ReadPlanStep: cfg.ReadPlanStep,
|
||||
SaveSession: a.saveSession,
|
||||
FlushSave: a.flushSave,
|
||||
})
|
||||
})
|
||||
|
||||
a.agents = append(a.agents, ag)
|
||||
a.activeAgentName = ag.Name()
|
||||
a.activeAgentID = ag.ID()
|
||||
ag.SetSessionEnv(a.id)
|
||||
return a
|
||||
}
|
||||
|
||||
// Agent returns the currently active agent by name.
|
||||
// Agent returns the currently active agent by immutable ID.
|
||||
// If no active agent is set, returns the first agent (backward compatible).
|
||||
func (a *Session) Agent() *agent.Agent {
|
||||
if len(a.agents) == 0 {
|
||||
return nil
|
||||
}
|
||||
if a.activeAgentName != "" {
|
||||
if a.activeAgentID != "" {
|
||||
for _, ag := range a.agents {
|
||||
if ag.Name() == a.activeAgentName {
|
||||
if ag.ID() == a.activeAgentID {
|
||||
return ag
|
||||
}
|
||||
}
|
||||
|
|
@ -187,16 +187,15 @@ func (a *Session) Agent() *agent.Agent {
|
|||
return a.agents[0]
|
||||
}
|
||||
|
||||
// SetActiveAgent sets the name of the active agent. Returns nil if found,
|
||||
// or an error if no agent with that name exists.
|
||||
func (a *Session) SetActiveAgent(name string) error {
|
||||
// SetActiveAgent selects an agent by immutable ID.
|
||||
func (a *Session) SetActiveAgent(id string) error {
|
||||
for _, ag := range a.agents {
|
||||
if ag.Name() == name {
|
||||
a.activeAgentName = name
|
||||
if ag.ID() == id {
|
||||
a.activeAgentID = id
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return fmt.Errorf("agent %q not found", name)
|
||||
return fmt.Errorf("agent %q not found", id)
|
||||
}
|
||||
|
||||
// FindAgent returns the agent matching the given name or ID, or nil.
|
||||
|
|
@ -233,8 +232,8 @@ func (a *Session) SaveFuncs() (save func(), flush func()) {
|
|||
// AddAgent appends an agent to the session and sets it as the active agent.
|
||||
func (a *Session) AddAgent(ag *agent.Agent) {
|
||||
a.agents = append(a.agents, ag)
|
||||
if a.activeAgentName == "" {
|
||||
a.activeAgentName = ag.Name()
|
||||
if a.activeAgentID == "" {
|
||||
a.activeAgentID = ag.ID()
|
||||
}
|
||||
ag.SetSessionEnv(a.id)
|
||||
}
|
||||
|
|
@ -337,7 +336,7 @@ func (a *Session) WaitChange(ctx context.Context, field, current string) (string
|
|||
case "cwd":
|
||||
return a.CWD()
|
||||
case "agent":
|
||||
return ag.Name()
|
||||
return ag.ID()
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue