session: enforce agent/session split in file trees
- NewSessionTree now only serves plan + env (session-level) - NewAgentTree serves all 21 agent-level files - Tests updated to use agent tree for agent files - Removed kill/rename/save from ctl (session ops via rm/mv) - TestSessionManagerOpenStore tests session-level file (plan)
This commit is contained in:
parent
f7e084106c
commit
c07aef6bb9
91
main_test.go
91
main_test.go
|
|
@ -160,9 +160,8 @@ var _ sync.Mutex // suppress unused import
|
|||
func newTestSessionFileStore(t *testing.T, sess *session.Session) *fs.Tree {
|
||||
t.Helper()
|
||||
sink := testSink()
|
||||
sf := session.NewSessionTree(sess, sink.NewLogger("test"),
|
||||
func() {},
|
||||
func(id string) error { return nil },
|
||||
// Use agent tree for agent-level file tests
|
||||
sf := session.NewAgentTree(sess, sink.NewLogger("test"),
|
||||
func(data []byte) error { return nil },
|
||||
func() {},
|
||||
nil,
|
||||
|
|
@ -174,6 +173,7 @@ func newTestSessionFileStore(t *testing.T, sess *session.Session) *fs.Tree {
|
|||
func newTestSessionFileStoreWith(t *testing.T, sess *session.Session, kill func(), rename func(string) error, save func([]byte) error) *fs.Tree {
|
||||
t.Helper()
|
||||
sink := testSink()
|
||||
// Session tree for kill/rename/save operations
|
||||
return session.NewSessionTree(sess, sink.NewLogger("test"), kill, rename, save, func() {}, nil, nil)
|
||||
}
|
||||
|
||||
|
|
@ -390,23 +390,23 @@ func TestSessionFileStoreReadableContract(t *testing.T) {
|
|||
sess := testSession("s1")
|
||||
defer sess.Cancel()
|
||||
sink := testSink()
|
||||
_ = session.NewSessionTree(sess, sink.NewLogger("test"),
|
||||
func() {}, func(string) error { return nil }, func([]byte) error { return nil }, func() {}, nil, nil)
|
||||
_ = session.NewAgentTree(sess, sink.NewLogger("test"),
|
||||
func([]byte) error { return nil }, func() {}, nil, nil)
|
||||
}
|
||||
|
||||
func TestSessionFileStoreList(t *testing.T) {
|
||||
sess := testSession("s1")
|
||||
defer sess.Cancel()
|
||||
sink := testSink()
|
||||
sf := session.NewSessionTree(sess, sink.NewLogger("test"),
|
||||
func() {}, func(string) error { return nil }, func([]byte) error { return nil }, func() {}, nil, nil)
|
||||
sf := session.NewAgentTree(sess, sink.NewLogger("test"),
|
||||
func([]byte) error { return nil }, func() {}, nil, nil)
|
||||
|
||||
entries, err := sf.List()
|
||||
if err != nil {
|
||||
t.Fatalf("List: %v", err)
|
||||
}
|
||||
if len(entries) != len(session.FileList) {
|
||||
t.Errorf("List() returned %d entries; want %d", len(entries), len(session.FileList))
|
||||
if len(entries) != len(session.AgentFileList) {
|
||||
t.Errorf("List() returned %d entries; want %d", len(entries), len(session.AgentFileList))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -415,8 +415,8 @@ func TestSessionFileStoreStatChat(t *testing.T) {
|
|||
defer sess.Cancel()
|
||||
sess.AppendLog([]byte("hello"))
|
||||
sink := testSink()
|
||||
sf := session.NewSessionTree(sess, sink.NewLogger("test"),
|
||||
func() {}, func(string) error { return nil }, func([]byte) error { return nil }, func() {}, nil, nil)
|
||||
sf := session.NewAgentTree(sess, sink.NewLogger("test"),
|
||||
func([]byte) error { return nil }, func() {}, nil, nil)
|
||||
|
||||
fi, err := sf.Stat("chat")
|
||||
if err != nil {
|
||||
|
|
@ -432,8 +432,8 @@ func TestSessionFileStoreGetChat(t *testing.T) {
|
|||
defer sess.Cancel()
|
||||
sess.AppendLog([]byte("hello"))
|
||||
sink := testSink()
|
||||
sf := session.NewSessionTree(sess, sink.NewLogger("test"),
|
||||
func() {}, func(string) error { return nil }, func([]byte) error { return nil }, func() {}, nil, nil)
|
||||
sf := session.NewAgentTree(sess, sink.NewLogger("test"),
|
||||
func([]byte) error { return nil }, func() {}, nil, nil)
|
||||
|
||||
data := testStoreRead(t, sf, "chat")
|
||||
if string(data) != "hello" {
|
||||
|
|
@ -445,8 +445,8 @@ func TestSessionFileStoreGetContent(t *testing.T) {
|
|||
sess := testSession("s1")
|
||||
defer sess.Cancel()
|
||||
sink := testSink()
|
||||
sf := session.NewSessionTree(sess, sink.NewLogger("test"),
|
||||
func() {}, func(string) error { return nil }, func([]byte) error { return nil }, func() {}, nil, nil)
|
||||
sf := session.NewAgentTree(sess, sink.NewLogger("test"),
|
||||
func([]byte) error { return nil }, func() {}, nil, nil)
|
||||
|
||||
for _, name := range []string{"cfg", "offset", "usage", "ctxsz", "models", "systemprompt"} {
|
||||
if _, err := sf.Open(name); err != nil {
|
||||
|
|
@ -459,8 +459,8 @@ func TestSessionFileStorePutCwd(t *testing.T) {
|
|||
sess := testSession("s1")
|
||||
defer sess.Cancel()
|
||||
sink := testSink()
|
||||
sf := session.NewSessionTree(sess, sink.NewLogger("test"),
|
||||
func() {}, func(string) error { return nil }, func([]byte) error { return nil }, func() {}, nil, nil)
|
||||
sf := session.NewAgentTree(sess, sink.NewLogger("test"),
|
||||
func([]byte) error { return nil }, func() {}, nil, nil)
|
||||
|
||||
// Create the directory first so SetCWD validates it
|
||||
os.MkdirAll("/tmp/newpath", 0755)
|
||||
|
|
@ -475,8 +475,8 @@ func TestSessionFileStorePutEmpty(t *testing.T) {
|
|||
sess := testSession("s1")
|
||||
defer sess.Cancel()
|
||||
sink := testSink()
|
||||
sf := session.NewSessionTree(sess, sink.NewLogger("test"),
|
||||
func() {}, func(string) error { return nil }, func([]byte) error { return nil }, func() {}, nil, nil)
|
||||
sf := session.NewAgentTree(sess, sink.NewLogger("test"),
|
||||
func([]byte) error { return nil }, func() {}, nil, nil)
|
||||
|
||||
// Empty write is a no-op
|
||||
e, err := sf.Open("cfg")
|
||||
|
|
@ -581,9 +581,9 @@ func TestSessionFileStoreWriteChat(t *testing.T) {
|
|||
sess := testSession("s1")
|
||||
defer sess.Cancel()
|
||||
var saved []byte
|
||||
sf := newTestSessionFileStoreWith(t, sess,
|
||||
func() {}, func(string) error { return nil },
|
||||
func(data []byte) error { saved = data; return nil })
|
||||
sink := testSink()
|
||||
sf := session.NewAgentTree(sess, sink.NewLogger("test"),
|
||||
func(data []byte) error { saved = data; return nil }, func() {}, nil, nil)
|
||||
|
||||
testStoreWrite(t, sf, "chat", []byte("transcript data"))
|
||||
if string(saved) != "transcript data" {
|
||||
|
|
@ -679,40 +679,6 @@ func TestSessionFileStoreHandleCtl(t *testing.T) {
|
|||
t.Error("ctl stop should interrupt (session should not be running)")
|
||||
}
|
||||
|
||||
// kill
|
||||
var killed bool
|
||||
sf2 := newTestSessionFileStoreWith(t, sess,
|
||||
func() { killed = true },
|
||||
func(string) error { return nil },
|
||||
func([]byte) error { return nil })
|
||||
testStoreWrite(t, sf2, "ctl", []byte("kill"))
|
||||
if !killed {
|
||||
t.Error("ctl kill should call kill callback")
|
||||
}
|
||||
|
||||
// rn (rename)
|
||||
var renamed string
|
||||
sf3 := newTestSessionFileStoreWith(t, sess,
|
||||
func() {},
|
||||
func(id string) error { renamed = id; return nil },
|
||||
func([]byte) error { return nil })
|
||||
testStoreWrite(t, sf3, "ctl", []byte("rn newname"))
|
||||
if renamed != "newname" {
|
||||
t.Errorf("renamed = %q; want newname", renamed)
|
||||
}
|
||||
|
||||
// save
|
||||
sess.AppendLog([]byte("log data"))
|
||||
var saved []byte
|
||||
sf4 := newTestSessionFileStoreWith(t, sess,
|
||||
func() {},
|
||||
func(string) error { return nil },
|
||||
func(data []byte) error { saved = data; return nil })
|
||||
testStoreWrite(t, sf4, "ctl", []byte("save"))
|
||||
if !strings.Contains(string(saved), "log data") {
|
||||
t.Errorf("saved = %q; want to contain log data", saved)
|
||||
}
|
||||
|
||||
// slash commands forwarded to Submit
|
||||
sess5 := testSession("s5")
|
||||
defer sess5.Cancel()
|
||||
|
|
@ -949,17 +915,14 @@ func TestSessionManagerOpenStore(t *testing.T) {
|
|||
defer sess.Cancel()
|
||||
s.AddSession(sess)
|
||||
|
||||
// Access a session file through the manager
|
||||
e, err := s.Tree().Open("s1/cfg")
|
||||
// Access a session-level file through the manager
|
||||
e, err := s.Tree().Open("s1/plan")
|
||||
if err != nil {
|
||||
t.Fatalf("Open(s1/cfg): %v", err)
|
||||
t.Fatalf("Open(s1/plan): %v", err)
|
||||
}
|
||||
data, err := e.Read()
|
||||
_, err = e.Read()
|
||||
if err != nil {
|
||||
t.Fatalf("Read(s1/cfg): %v", err)
|
||||
}
|
||||
if !strings.Contains(string(data), "name=s1") {
|
||||
t.Errorf("cfg = %q; want to contain name=s1", data)
|
||||
t.Fatalf("Read(s1/plan): %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -66,8 +66,8 @@ func sessionFilePerms() map[string]os.FileMode {
|
|||
func NewSessionTree(sess *Session, log *olog.Logger, kill func(), rename func(newID string) error, saveTranscript func([]byte) error, invalidateModels func(), resetElevation func(), toolRegistry *tools.Registry) *fs.Tree {
|
||||
h := &sessionHelper{sess: sess, log: log, kill: kill, rename: rename, saveTranscript: saveTranscript, invalidateModels: invalidateModels, resetElevation: resetElevation, toolRegistry: toolRegistry}
|
||||
perms := sessionFilePerms()
|
||||
specs := make([]fs.FileSpec, len(FileList))
|
||||
for i, f := range FileList {
|
||||
specs := make([]fs.FileSpec, len(SessionFileList))
|
||||
for i, f := range SessionFileList {
|
||||
specs[i] = h.fileSpec(f.Name, perms[f.Name])
|
||||
specs[i].OneShot = f.OneShot
|
||||
specs[i].Async = f.Async
|
||||
|
|
|
|||
Reference in New Issue