server: handle session/new write+read in server (rdwr pattern), bypass FileConfig

This commit is contained in:
Ollie Agent 2026-08-01 19:31:39 +02:00
parent 7e731d4624
commit daf535308f
2 changed files with 65 additions and 56 deletions

View File

@ -50,6 +50,7 @@ type fid struct {
completeResult []byte // for /complete: result of last write (per-fid state)
generateResult []byte // for /generate: result of last write (per-fid state)
routeResult []byte // for /route: result of last write (per-fid state)
sessionResult []byte // for /session/new: session name from last write
}
// connState tracks all open fids for a single 9P connection.
@ -836,13 +837,19 @@ func (s *Server) read(cs *connState, fc *plan9.Fcall, ctx context.Context) *plan
return s.readDNS(fc, &f.generateResult)
}
// /route: return per-fid result from previous write (offset 0-based)
if path == "/route" {
s.log.Debug("Tread path=%q offset=%d count=%d", path, fc.Offset, fc.Count)
return s.readDNS(fc, &f.routeResult)
}
// /route: return per-fid result from previous write (offset 0-based)
if path == "/route" {
s.log.Debug("Tread path=%q offset=%d count=%d", path, fc.Offset, fc.Count)
return s.readDNS(fc, &f.routeResult)
}
s.log.Debug("Tread path=%q offset=%d count=%d", path, fc.Offset, fc.Count)
// /session/new: return per-fid result from previous write
if path == "/session/new" {
s.log.Debug("Tread path=%q offset=%d count=%d", path, fc.Offset, fc.Count)
return s.readDNS(fc, &f.sessionResult)
}
s.log.Debug("Tread path=%q offset=%d count=%d", path, fc.Offset, fc.Count)
entry, err := s.openEntry(path)
if err != nil {
return errFcall(fc, err.Error())
@ -1010,11 +1017,22 @@ func (s *Server) write(cs *connState, fc *plan9.Fcall) *plan9.Fcall {
if err != nil {
return errFcall(fc, err.Error())
}
f.routeResult = []byte(fmt.Sprintf("backend=%s model=%s", result.Backend, result.Model))
return &plan9.Fcall{Type: plan9.Rwrite, Tag: fc.Tag, Count: uint32(len(fc.Data))}
}
f.routeResult = []byte(fmt.Sprintf("backend=%s model=%s", result.Backend, result.Model))
return &plan9.Fcall{Type: plan9.Rwrite, Tag: fc.Tag, Count: uint32(len(fc.Data))}
}
// Accumulate; the 9P client may split large writes across multiple Twrite messages.
// /session/new: synchronous create-session (per-fid state, like /net/dns)
if f.path == "/session/new" {
cs.mu.Unlock()
name, err := fs.CreateFromRoot(s.sessionTree, strings.Fields(strings.TrimSpace(string(fc.Data))))
if err != nil {
return errFcall(fc, err.Error())
}
f.sessionResult = []byte(name)
return &plan9.Fcall{Type: plan9.Rwrite, Tag: fc.Tag, Count: uint32(len(fc.Data))}
}
// Accumulate; the 9P client may split large writes across multiple Twrite messages.
end := int(fc.Offset) + len(fc.Data)
if end > len(f.writeBuf) {
grown := make([]byte, end)

View File

@ -422,28 +422,19 @@ func rootOpen(rs *rootState, name string) (File, error) {
},
IsBlocking_: true,
}, nil
case "new":
var sessResult string
return &FileConfig{
StatFn: func() (os.FileInfo, error) {
return &SyntheticFileInfo{Name_: "new", Mode_: Perms[PathSessions].Files["new"]}, nil
},
ReadFn: func() ([]byte, error) {
if sessResult != "" {
return []byte(sessResult + "\n"), nil
}
return []byte("name=\n"), nil
},
WriteFn: func(data []byte) error {
name, err := Create(rs, strings.Fields(strings.TrimSpace(string(data))))
if err != nil {
return err
}
sessResult = name
return nil
},
BlockingReadFn: notBlocking,
}, nil
case "new":
return &FileConfig{
StatFn: func() (os.FileInfo, error) {
return &SyntheticFileInfo{Name_: "new", Mode_: Perms[PathSessions].Files["new"]}, nil
},
ReadFn: func() ([]byte, error) {
return []byte("name=\n"), nil
},
WriteFn: func(data []byte) error {
return fmt.Errorf("use ollie-9p rdwr session/new")
},
BlockingReadFn: notBlocking,
}, nil
case "idx":
return &FileConfig{
StatFn: func() (os.FileInfo, error) {
@ -472,30 +463,30 @@ case "new":
if len(parts) == 2 {
return nil, fmt.Errorf("%s: is a directory", name)
}
if len(parts) >= 3 && parts[2] == "new" {
if len(parts) == 3 {
// agent/new — request-response file to create an agent
var agentResult string
return &FileConfig{
StatFn: func() (os.FileInfo, error) {
return &SyntheticFileInfo{Name_: "new", Mode_: Perms[PathSessionFile].Files["new"]}, nil
},
ReadFn: func() ([]byte, error) {
if agentResult != "" {
return []byte(agentResult + "\n"), nil
}
return []byte("cwd=\nbackend=\nmodel=\nagent=\nagent_alias=\nremote=\nsystem_prompt=\n"), nil
},
WriteFn: func(data []byte) error {
if err := CreateAgent(rs, sessID, strings.Fields(strings.TrimSpace(string(data)))); err != nil {
return err
}
agentResult = "ok"
return nil
},
BlockingReadFn: notBlocking,
}, nil
}
if len(parts) >= 3 && parts[2] == "new" {
if len(parts) == 3 {
// agent/new — request-response file to create an agent
var agentResult string
return &FileConfig{
StatFn: func() (os.FileInfo, error) {
return &SyntheticFileInfo{Name_: "new", Mode_: Perms[PathSessionFile].Files["new"]}, nil
},
ReadFn: func() ([]byte, error) {
if agentResult != "" {
return []byte(agentResult + "\n"), nil
}
return []byte("cwd=\nbackend=\nmodel=\nagent=\nagent_alias=\nremote=\nsystem_prompt=\n"), nil
},
WriteFn: func(data []byte) error {
if err := CreateAgent(rs, sessID, strings.Fields(strings.TrimSpace(string(data)))); err != nil {
return err
}
agentResult = "ok"
return nil
},
BlockingReadFn: notBlocking,
}, nil
}
return nil, fmt.Errorf("%s: not a file", name)
}
if len(parts) < 4 {