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:
parent
46aa901763
commit
c8f42b2400
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue