9p: add chat.search file for block lookup by ID

Write a block ID to chat.search, read the block content back.
Returns error if block not found.
This commit is contained in:
Levi Neely 2026-10-09 16:15:30 +02:00
parent 46aa901763
commit c8f42b2400
2 changed files with 51 additions and 2 deletions

View File

@ -7,6 +7,7 @@
package agent package agent
import ( import (
"bytes"
"fmt" "fmt"
"sync" "sync"
"sync/atomic" "sync/atomic"
@ -99,6 +100,43 @@ func (ag *Agent) ChatRead(base string) ([]byte, string, error) {
return data, fmt.Sprintf("%d", newOffset), nil return data, fmt.Sprintf("%d", newOffset), nil
} }
// ChatSearchByID returns the block content for the given block ID.
// Returns the block text (without markers) and true if found, empty and false otherwise.
func (ag *Agent) ChatSearchByID(blockID string) ([]byte, bool) {
if blockID == "" {
return nil, false
}
ag.chatMu.RLock()
log := ag.chatLog
ag.chatMu.RUnlock()
// Parse blocks looking for the target ID
// Format: [[[role:name#id]]] ... [[[end]]]
target := "#" + blockID + "]]]"
idx := bytes.Index(log, []byte(target))
if idx < 0 {
return nil, false
}
// Find the start of this block header
headerStart := bytes.LastIndex(log[:idx], []byte("[[["))
if headerStart < 0 {
return nil, false
}
// Find [[[end]]] after the header
endMarker := []byte("[[[end]]]")
endIdx := bytes.Index(log[idx:], endMarker)
if endIdx < 0 {
return nil, false
}
blockEnd := idx + endIdx + len(endMarker)
// Extract block including header and end marker
block := log[headerStart:blockEnd]
return block, true
}
// --- Plan methods --- // --- Plan methods ---
// Plan returns a copy of the plan. // Plan returns a copy of the plan.

View File

@ -851,8 +851,19 @@ func buildAgentChildren(a *agent.Agent, s *session.Session) []virtfs.FsNodeDecl
return data, nil return data, nil
}), }),
virtfs.Stream(a.ChatRead, a.ChatSignal), virtfs.Stream(a.ChatRead, a.ChatSignal),
), ),
virtfs.FileNode("state", 0444, // world-readable (non-sensitive) virtfs.FileNode("chat.search", 0660, // rdwr - write blockId, read block
virtfs.Doc("Search chat by block ID. Write ID, read block content."),
virtfs.Rdwr(func(_ context.Context, data []byte) ([]byte, error) {
blockID := strings.TrimSpace(string(data))
block, found := a.ChatSearchByID(blockID)
if !found {
return nil, fmt.Errorf("block not found: %s", blockID)
}
return block, nil
}),
),
virtfs.FileNode("state", 0444, // world-readable (non-sensitive)
virtfs.Doc("Current agent state (idle, calling, thinking, paused)"), virtfs.Doc("Current agent state (idle, calling, thinking, paused)"),
virtfs.Read(func() ([]byte, error) { virtfs.Read(func() ([]byte, error) {
return []byte(a.State() + "\n"), nil return []byte(a.State() + "\n"), nil