9p: expose offset file in session directory

Records the byte position of chat EOF immediately before each prompt
is submitted. Clients can read offset after a turn completes and use
tail -c +$((offset+1)) to extract just the new output without needing
a dedicated reply file.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Levi Neely 2026-04-16 22:05:03 +02:00
parent e277937619
commit de1bf9612d
2 changed files with 11 additions and 2 deletions

View File

@ -83,8 +83,9 @@ type session struct {
cancel context.CancelFunc
// chatLog is an append-only record of the conversation. Reads are served
// directly from this buffer by offset, so tail -f works via polling.
chatLog []byte
chatVers uint32 // incremented on each append; used as Qid.Vers
chatLog []byte
chatVers uint32 // incremented on each append; used as Qid.Vers
chatOffset int // byte position of chat EOF immediately before last prompt submit
// mutableVers tracks changes to tailable mutable-state files
// (state, backend, agent, model, usage, cwd). Bumped on change;
// reported via Qid.Vers in stat so tail -f detects truncation/rewrite.

View File

@ -19,6 +19,7 @@ var sessionFileList = []struct {
{"enqueue", 0200},
{"dequeue", 0444},
{"chat", 0444},
{"offset", 0444},
{"state", 0444},
{"backend", 0666},
{"agent", 0666},
@ -78,6 +79,8 @@ func (s *SessionFileStore) Get(name string) ([]byte, error) {
copy(data, s.sess.chatLog)
s.sess.mu.RUnlock()
return data, nil
case "offset":
return []byte(s.content("offset")), nil
case "dequeue":
item, ok := s.sess.core.PopQueue()
if !ok {
@ -101,6 +104,9 @@ func (s *SessionFileStore) Put(name string, data []byte) error {
}
switch name {
case "prompt":
s.sess.mu.Lock()
s.sess.chatOffset = len(s.sess.chatLog)
s.sess.mu.Unlock()
s.sess.core.Submit(s.sess.ctx, input, s.makePublish())
s.sess.trackMutable()
@ -174,6 +180,8 @@ func (s *SessionFileStore) content(name string) string {
return s.sess.core.ListServers() + "\n"
case "systemprompt":
return s.sess.core.SystemPrompt()
case "offset":
return fmt.Sprintf("%d\n", s.sess.chatOffset)
}
return ""
}