subagent = &Subagent{
name: name,
+ dir: root,
log: log,
syncer: syncer,
}
return subagent, nil
}
+// Snapshot flushes and locks the open logs while snapshot reads the subagent
+// files. snapshot must not call Subagents or an open Subagent.
+func (s *Subagents) Snapshot(ctx context.Context, snapshot func() error) error {
+ s.mutex.Lock()
+ defer s.mutex.Unlock()
+
+ for _, subagent := range s.open {
+ subagent.mutex.Lock()
+ defer subagent.mutex.Unlock()
+
+ err := subagent.Flush(ctx)
+ if err != nil {
+ return fmt.Errorf("flush subagent snapshot: %w", err)
+ }
+ }
+
+ err := snapshot()
+ if err != nil {
+ return fmt.Errorf("copy subagent snapshot: %w", err)
+ }
+
+ return nil
+}
+
// Close makes the writes of each open log durable and closes it.
func (s *Subagents) Close() error {
s.mutex.Lock()
defer s.mutex.Unlock()
var err error
+
for name, subagent := range s.open {
err = errors.Join(err, subagent.close())
+
delete(s.open, name)
}
return err
}
-// Subagent is one subagent and its log. Its mutex protects the log, because
-// the run of the subagent writes it while the agent reads it with zoom.
+// Subagent is one subagent, its directory, and its log. Its mutex protects the
+// log, because the run of the subagent writes it while the agent reads it with
+// zoom.
type Subagent struct {
name string
+ dir string
mutex sync.Mutex
log *Log
syncer *Syncer
return a.syncer.Flush(ctx)
}
+// First returns the first message of the log.
+func (a *Subagent) First() (Message, error) {
+ a.mutex.Lock()
+ defer a.mutex.Unlock()
+
+ return a.log.Message(0)
+}
+
// Show returns the whole log, each message as "i|kind: text", with a blank line
// between messages.
func (a *Subagent) Show() (string, error) {
return strings.Join(lines, "\n\n"), nil
}
+// WorkDir returns the directory where the tools of the subagent run: work in
+// its directory.
+func (a *Subagent) WorkDir() string {
+ return filepath.Join(a.dir, "work")
+}
+
// close makes the writes durable and closes the log.
func (a *Subagent) close() error {
a.mutex.Lock()