diff --git a/internal/p9/server.go b/internal/p9/server.go index 065c099..9f6b6cc 100644 --- a/internal/p9/server.go +++ b/internal/p9/server.go @@ -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. diff --git a/internal/p9/sessionfilestore.go b/internal/p9/sessionfilestore.go index e328bed..ad14985 100644 --- a/internal/p9/sessionfilestore.go +++ b/internal/p9/sessionfilestore.go @@ -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 "" }