require.Equal(t, childText, restoredText)
}
+func TestBackupRestoresEachChildWithItsOwnState(t *testing.T) {
+ dir := t.TempDir()
+ daemonChildConfig(t, dir, "Garden")
+ daemonChildConfig(t, dir, "Library")
+
+ server := newModelFake(t)
+ clock := newTestClock()
+ agents, err := openDaemonAgents(t.Context(), dir, server.URL, ModelAPIKey(modelTestKey), defaultConfig().Compactor, clock.Now, discardLog())
+ require.NoError(t, err)
+
+ defer func() {
+ require.NoError(t, agents.Close())
+ }()
+
+ whatsapp, _, _ := whatsappStartTest(t, dir, clock)
+
+ var loops []*TurnLoop
+
+ for _, agent := range agents {
+ loop, err := agent.Loop(whatsapp, func(Event) {
+ })
+ require.NoError(t, err)
+
+ loops = append(loops, loop)
+ _, err = loop.Submit("parent", "Keep the decision of "+agent.agent.config.Name)
+ require.NoError(t, err)
+ require.NoError(t, loop.turn(t.Context()))
+ require.NoError(t, agent.memory.Settle(t.Context()))
+
+ subagent, err := agent.subagents.Create(t.Context())
+ require.NoError(t, err)
+
+ _, err = subagent.Append("user", "Keep the task of "+agent.agent.config.Name)
+ require.NoError(t, err)
+ }
+
+ archive := filepath.Join(t.TempDir(), "children.tar.gz")
+ _, err = backupData(t.Context(), dir, archive, whatsapp, func(ctx context.Context, snapshot func() error) error {
+ return daemonSnapshot(ctx, loops, snapshot)
+ })
+ require.NoError(t, err)
+
+ restored := t.TempDir()
+ require.NoError(t, backupRestore(t.Context(), archive, restored))
+
+ restoredAgents, err := openDaemonAgents(t.Context(), restored, server.URL, ModelAPIKey(modelTestKey), defaultConfig().Compactor, clock.Now, discardLog())
+ require.NoError(t, err)
+
+ defer func() {
+ require.NoError(t, restoredAgents.Close())
+ }()
+
+ require.Len(t, restoredAgents, 3)
+
+ for i, agent := range agents {
+ other := restoredAgents[i]
+ require.Equal(t, agent.agent.config, other.agent.config)
+ require.Equal(t, agent.memory.View(), other.memory.View())
+ require.Equal(t, agent.memory.Len(), other.memory.Len())
+ require.Equal(t, modelReadUsage(t, agent.client.UsagePath()), modelReadUsage(t, other.client.UsagePath()))
+
+ for id := range agent.memory.Len() {
+ before, err := agent.memory.Message(id)
+ require.NoError(t, err)
+
+ after, err := other.memory.Message(id)
+ require.NoError(t, err)
+ require.Equal(t, before, after)
+ }
+
+ entries, err := os.ReadDir(filepath.Join(agent.agent.dir, subagentDirName))
+ require.NoError(t, err)
+ require.Len(t, entries, 1)
+
+ subagent, err := other.subagents.Get(t.Context(), entries[0].Name())
+ require.NoError(t, err)
+
+ text, err := subagent.Show()
+ require.NoError(t, err)
+ require.Contains(t, text, "Keep the task of "+agent.agent.config.Name)
+ }
+}
+
func TestRestoreRefusesNonemptyDataWithoutChangingIt(t *testing.T) {
dir := t.TempDir()
path := filepath.Join(dir, "keep")