agent-zero/plugins/_time_travel
King0James0 c42dffa54e feat: Time Travel shadow-repo retention (orphan cleanup, optional age-out, stale-lock repair)
Time Travel keeps a hidden git repository per workspace under
/a0/usr/.time_travel/workspaces/<id>/repo.git and snapshots on every file
change, but ships no retention: nothing deletes a shadow repository when its
chat or project is removed (chat_remove never touches .time_travel), there is
no delete/prune endpoint in the API or web UI, and a workspace whose 'git add'
ever exceeds GIT_TIMEOUT_SECONDS strands repo.git/index.lock, failing every
later snapshot with 'index.lock: File exists'. Observed on a live instance:
518 shadow repositories / 12 GB, most belonging to long-deleted chats, plus a
permanently wedged workspace.

This adds a throttled retention sweep driven from job_loop:

- Orphans: live workspace paths (project folders, the configured workdir,
  per-chat workdirs) are forward-enumerated and hashed with the existing
  workspace_id_for derivation; a shadow directory matching none of them is
  unreachable from the UI forever and is removed after a grace window.
- Optional age-out: retention_max_age_days (default 0 = keep forever) removes
  a live workspace's history when it has had no snapshot in N days. Time
  Travel lazily re-initializes an empty history on the next snapshot, so this
  is always safe for the feature.
- Stale locks: repo.git/index.lock older than retention_stale_lock_minutes is
  removed (git subprocesses are killed at GIT_TIMEOUT_SECONDS, so no
  legitimate lock lives that long), un-wedging future snapshots.
- Corrupt-repo set-asides (repo.git.invalid*) past the grace window.

Deletion is refused for any path outside the shadow root. Settings render in
the existing plugin-config pattern (default_config.yaml + webui/config.html,
settings_sections: agent): enable toggle, sweep interval, age-out days,
orphan grace, stale-lock age. Durable evidence lives next to the workspaces
dir: retention.json (running totals + last sweep stamp) and retention.log
(one JSON line per sweep naming everything removed, tail-capped at 1000).

Sweeps run in a worker thread off the event loop, at most one in flight,
default every 6 hours.

Tests: tests/test_time_travel_retention.py (config defaults/clamps, the full
sweep matrix, keep-forever default, disabled no-op, deletion guard, marker
accrual, per-sweep history + cap, throttle, and workspace_id_for parity).
2026-08-12 14:34:16 +02:00
..
api Time Travel workspace selector + LiteLLM globals 2026-06-09 17:16:41 +02:00
extensions feat: Time Travel shadow-repo retention (orphan cleanup, optional age-out, stale-lock repair) 2026-08-12 14:34:16 +02:00
helpers feat: Time Travel shadow-repo retention (orphan cleanup, optional age-out, stale-lock repair) 2026-08-12 14:34:16 +02:00
webui feat: Time Travel shadow-repo retention (orphan cleanup, optional age-out, stale-lock repair) 2026-08-12 14:34:16 +02:00
AGENTS.md Time Travel workspace selector + LiteLLM globals 2026-06-09 17:16:41 +02:00
default_config.yaml feat: Time Travel shadow-repo retention (orphan cleanup, optional age-out, stale-lock repair) 2026-08-12 14:34:16 +02:00
plugin.yaml feat: Time Travel shadow-repo retention (orphan cleanup, optional age-out, stale-lock repair) 2026-08-12 14:34:16 +02:00