+// load reads the names of work.json. A missing file names none.
+func (r *SubagentRuns) load() ([]string, error) {
+ data, err := os.ReadFile(r.workPath())
+ if errors.Is(err, os.ErrNotExist) {
+ return nil, nil
+ }
+
+ if err != nil {
+ return nil, fmt.Errorf("read running subagents: %w", err)
+ }
+
+ var file SubagentWorkFile
+
+ err = json.Unmarshal(data, &file)
+ if err != nil {
+ return nil, fmt.Errorf("decode running subagents: %w", err)
+ }
+
+ return file.Running, nil
+}
+
+// save writes the names of the running subagents to work.json. The caller
+// holds the mutex.
+func (r *SubagentRuns) save() error {
+ file := SubagentWorkFile{
+ Running: append([]string{}, slices.Sorted(maps.Keys(r.running))...),
+ }
+
+ data, err := json.Marshal(file)
+ if err != nil {
+ return fmt.Errorf("encode running subagents: %w", err)
+ }
+
+ err = writeFileAtomic(r.workPath(), append(data, '\n'))
+ if err != nil {
+ return fmt.Errorf("save running subagents: %w", err)
+ }
+
+ return nil
+}
+
+func (r *SubagentRuns) workPath() string {
+ return filepath.Join(r.agent.dir, subagentWorkFileName)
+}
+
+// run runs the chains of run and reports and logs its end with the length of
+// the run, unless Close ended it. A chain follows while messages wait after the final answer of the chain before
+// it, and no chain failed. A chain that Stop ends gives the reply "stopped".