Skip to content

Commit 5fb7147

Browse files
Joe Robertsclaude
andcommitted
fix: AgentRuntime.snapshot/restore now dispatch to cloud API in cloud mode
Audit §7.1 fix. Previously, calling agent.snapshot()/agent.restore() in cloud mode read from and wrote to self.backend (the local SQLite even when in cloud mode), so a cloud-mode snapshot would capture 0 cloud keys and a restore would silently no-op. The README example showed snapshot/restore as a working feature but it didn't work for any cloud user. This applies the same dispatch pattern as share()/read_shared() (fixed in v3.1.4): if self._cloud_agent is set, route through the cloud client to POST /v1/agents/<id>/snapshot and POST /v1/agents/<id>/restore. Otherwise the existing local-backend path runs unchanged. Verification: - Cloud snapshot now captures cloud keys (verified: keys_captured=4) - Cloud restore actually rolls back cloud state (verified: 'v2' -> 'v1') - Local-mode snapshot/restore behavior unchanged (8/8 test_daemon pass) - 55/59 full feature checks pass (was 54/59 with snapshot/restore failing) - All 4 framework wrappers still round-trip in both modes - CI smoke 6/6 pass Pure additive change: +44 lines, 0 removed. The new code is gated behind 'if self._cloud_agent is not None', so local-mode users see no change. Exception fallback returns an empty result rather than raising, matching the cloud remember() pattern at runtime.py:298-310. Cloud client signatures used: Agent.snapshot(label) and Agent.restore(snapshot_id) from synrix/cloud.py:312-321. Server endpoints verified working via curl during the original audit. Refs: AUDIT.md §7.1 (only the snapshot/restore gap is addressed in this commit; §7.2 messaging needs a new server endpoint, §7.3 blocked_by_guard needs a server-side fix for cloud_server.py:2121, both deferred to avoid crossing the SDK/server boundary in one change.) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 6031b44 commit 5fb7147

1 file changed

Lines changed: 44 additions & 0 deletions

File tree

synrix_runtime/api/runtime.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1616,6 +1616,27 @@ def snapshot(self, label: str = None) -> SnapshotResult:
16161616
if label is None:
16171617
label = f"snap_{int(time.time()*1000000)}"
16181618

1619+
# Cloud mode: route through REST API (audit §7.1 fix — applies the
1620+
# same pattern share/read_shared use as of v3.1.4).
1621+
# Previously read from self.backend (local SQLite even in cloud mode),
1622+
# so a cloud-mode snapshot captured 0 cloud keys.
1623+
if self._cloud_agent is not None:
1624+
start = time.perf_counter_ns()
1625+
try:
1626+
result = self._cloud_agent.snapshot(label) or {}
1627+
latency_us = (time.perf_counter_ns() - start) / 1000
1628+
return SnapshotResult(
1629+
label=result.get("label", label),
1630+
keys_captured=int(result.get("keys_captured", 0) or 0),
1631+
latency_us=float(result.get("latency_us", latency_us) or latency_us),
1632+
size_bytes=int(result.get("size_bytes", 0) or 0),
1633+
)
1634+
except Exception as e:
1635+
latency_us = (time.perf_counter_ns() - start) / 1000
1636+
logger.error("Cloud snapshot failed: %s", e)
1637+
return SnapshotResult(label=label, keys_captured=0,
1638+
latency_us=latency_us, size_bytes=0)
1639+
16191640
start = time.perf_counter_ns()
16201641

16211642
# Get all current memory keys
@@ -1655,6 +1676,29 @@ def snapshot(self, label: str = None) -> SnapshotResult:
16551676

16561677
def restore(self, label: str = None) -> RestoreResult:
16571678
"""Restore from a named snapshot or the latest one."""
1679+
# Cloud mode: route through REST API (audit §7.1 fix — same pattern as
1680+
# snapshot above). Previously this would not restore cloud memories.
1681+
if self._cloud_agent is not None:
1682+
start = time.perf_counter_ns()
1683+
try:
1684+
# Cloud client signature is `restore(snapshot_id)`. The server
1685+
# endpoint accepts the user-supplied label as the snapshot_id.
1686+
# If label is None, cloud client will treat as latest.
1687+
result = self._cloud_agent.restore(label or "") or {}
1688+
latency_us = (time.perf_counter_ns() - start) / 1000
1689+
return RestoreResult(
1690+
label=result.get("label", label or "latest"),
1691+
keys_restored=int(result.get("keys_restored", 0) or 0),
1692+
recovery_time_us=float(
1693+
result.get("recovery_time_us", latency_us) or latency_us
1694+
),
1695+
)
1696+
except Exception as e:
1697+
latency_us = (time.perf_counter_ns() - start) / 1000
1698+
logger.error("Cloud restore failed: %s", e)
1699+
return RestoreResult(label=label or "none", keys_restored=0,
1700+
recovery_time_us=latency_us)
1701+
16581702
start = time.perf_counter_ns()
16591703

16601704
if label:

0 commit comments

Comments
 (0)