Local watchdog and tamper-evident timeline for long-running AI agents
Project description
SafeLoop
SafeLoop is the local flight recorder and recovery layer for long-running AI agents.
It records local file side effects, checkpoints repo changes, verifies tamper-evident artifacts, supports exact local undo for covered file changes, and marks external side effects as not tracked in this release instead of pretending they are blindly reversible.
In short: roll back covered local files, compensate or manually review external effects, and audit everything. See docs/recoverability-first.md for the product boundary and the threat model for audit boundaries.
Current source version: SafeLoop 0.2.0. The release workflow publishes GitHub releases and PyPI packages from version tags; until v0.2.0 is tagged and published, install from this repository.
Problem
Agents can run for minutes or hours and leave operators asking:
- what command actually ran?
- what files changed, and when?
- are the artifacts intact or tampered with?
- can a checkpoint be locally undone?
- did anything external happen that needs manual compensation?
SafeLoop 0.2.0 hardens the local watchdog, delta-audit packet, and control-plane evidence workflow for those questions.
Install
SafeLoop requires Python 3.11 or newer.
After the PyPI package is published, install it with pipx:
pipx install safeloop
safeloop --help
Or install it into a virtual environment:
python3 -m venv .venv
. .venv/bin/activate
python -m pip install safeloop
safeloop --version
Before the PyPI package is published, install from the repository:
pipx install git+https://github.com/clawdia-saka/safeloop.git
If you need the exact 0.2.0 release source once the tag exists, install from Git:
pipx install git+https://github.com/clawdia-saka/safeloop.git@v0.2.0 # after the v0.2.0 tag exists
Git tag install into a local virtual environment:
python3 -m venv .venv && . .venv/bin/activate && python -m pip install 'safeloop @ git+https://github.com/clawdia-saka/safeloop.git@v0.2.0' # after the v0.2.0 tag exists
Quickstart
Minimal end-to-end local rollback smoke test:
rm -rf /tmp/safeloop-demo-repo /tmp/safeloop-demo-runs
mkdir -p /tmp/safeloop-demo-repo
cd /tmp/safeloop-demo-repo
git init -q
git config user.email demo@example.test
git config user.name demo
echo base > note.txt
git add note.txt && git commit -q -m init
safeloop watch-run --task-id demo --repo "$PWD" --run-root /tmp/safeloop-demo-runs -- \
python -c "from pathlib import Path; Path('note.txt').write_text('changed by safeloop\\n')"
RUN_DIR="$(find /tmp/safeloop-demo-runs -maxdepth 1 -type d -name 'run-*' | head -1)"
RUN_ID="$(basename "$RUN_DIR")"
safeloop review "$RUN_DIR"
safeloop explain "$RUN_DIR"
safeloop rollback plan "$RUN_DIR" "$RUN_ID" --files note.txt
safeloop rollback apply "$RUN_DIR" "$RUN_ID" --files note.txt
cat note.txt # base
Actions outside the local repo are manual-review/compensation only; SafeLoop never claims exact rollback for GitHub, messaging, email, webhook, or other systems beyond the repo. External side effects are manual-review/compensation only.
safeloop explain RUN_DIR is the operator-language view:
- Rollback: restore covered local repo files from verified artifacts.
- Compensation: record a cleanup or correction plan for actions outside the local repo; this remains
exact_rollback: false. - Manual handoff: route evidence to an operator when SafeLoop cannot safely verify or complete recovery automatically.
- Action groups: bundle related files, hunks, checkpoints, and optional action IDs so a person can review one unit of work.
Demo commands
The public demo path is intentionally five commands:
safeloop watch-run
safeloop timeline
safeloop verify-artifacts
safeloop rollback plan
safeloop rollback apply
Install for local development from a checkout:
python -m pip install -e .
Run an agent command under the watchdog with the current 0.2.0 flow:
safeloop watch-run \
--task-id demo \
--repo /path/to/repo \
-- python agent.py
The command prints the run directory, normally under ~/.safeloop/runs/<run-id>/ unless --run-root is provided.
Inspect the timeline, verify the tamper-evident artifact packet, then create an operator-readable rollback plan:
safeloop timeline RUN_DIR
safeloop timeline RUN_DIR --json
safeloop timeline RUN_DIR --checkpoint cp-0001
safeloop verify-artifacts RUN_DIR
safeloop rollback plan RUN_DIR RUN_ID cp-0001
Apply covered local-file rollback only after reviewing rollback-plan.json:
safeloop rollback apply RUN_DIR RUN_ID cp-0001
Compatibility aliases remain available for older scripts:
safeloop watch --loop ...is an alias-compatible form ofsafeloop watch-run ....safeloop undo RUN_DIR RUN_ID cp-0001 --dry-run|--applyremains available for the older dry-run/apply UX.
Or run the checked-in demo script for the release packet flow (watch-run → timeline → verify-artifacts → rollback plan/apply):
bash examples/watchdog_demo.sh
For the external side-effect boundary, run:
bash examples/recoverability_external_effect_demo.sh
That demo rolls back a covered local file while leaving fake external API evidence marked external_review_required. A static one-page visual is available at examples/recoverability_demo.html.
For the full public-packet demo, run:
bash examples/full_demo.sh
That script ties the current release flow together in one local workspace: watch-run captures local change evidence, timeline and verify-artifacts build the audit trail, review and rollback plan produce the operator packet, rollback apply restores the covered repo file, and scripts/public_readiness.py --check verifies the packet language. The simulated external ticket remains manual-review/compensation territory with exact_rollback=false; SafeLoop does not claim rollback for anything outside the demo repo.
For concrete compensation examples and adapter contract shapes for actions outside the local repo, see docs/compensation.md, docs/compensation-adapter-contracts.md, and run the local-only fixtures:
bash examples/compensation_github_issue_demo.sh
bash examples/compensation_message_demo.sh
They show that covered local rollback and compensation for actions outside the local repo are separate: the compensation plan records a concrete cleanup/correction action, keeps exact_rollback: false, and preserves manual review when GitHub, messaging, email, or webhook state must be verified.
For realistic local-only agent run examples, see docs/real-world-agent-runs.md:
python examples/coding_agent_run.py --output-dir /tmp/safeloop-real-world/coding
python examples/research_intel_run.py --output-dir /tmp/safeloop-real-world/research
python examples/browser_api_action_run.py --output-dir /tmp/safeloop-real-world/outside-action
These examples cover a coding run with test evidence and a rollback plan, a research/intel brief with a stale/low-confidence marker, and a browser/API-like outside action that remains blocked for manual review with exact_rollback: false.
For a local-only retrieved-context integration pattern, see docs/gbrain-integration-demo.md and run:
bash examples/gbrain_context_demo.sh
That demo uses a mock Gbrain fixture as evidence input only: Gbrain is not the scheduler or control plane, and SafeLoop owns action evidence, rollback planning, and manual review artifacts.
What it does today
SafeLoop 0.2.0 writes local watchdog and review-aid artifacts such as:
RUN_DIR/
run.json
timeline.jsonl
command.stdout.txt
command.stderr.txt
process-result.json
side-effects.jsonl
rollback-plan.json
checkpoints/
cp-0001/
checkpoint.json
manifest.json
diff.patch
restore-manifest.json
summary.md
undo-preflight.json
undo-result.json
verification/
verify-artifacts-result.json
Implemented surfaces:
watch --loop(watch-runcompatibility alias): executes a command in a repo, captures stdout/stderr, records process result, creates run-local monotonic checkpoints for changed repo state, and binds artifacts into both checkpoint metadata and the timeline hash chain.- Artifact contract:
run.json,timeline.jsonl, checkpoint metadata,restore-manifest.jsonwithschema_version: restore-manifest.v2, complete command capture files, and an always-presentside-effects.jsonlplaceholder. - Checkpoint parent binding: each checkpoint records parent, sequence, allocation mode, previous state digest, current state digest, and artifact digests.
verify-artifacts: checks timeline hash chain, bound artifact digests, checkpoint parent chain, process-result digest, command capture completeness, invalid partial finalization, and run final hash; writesverification/verify-artifacts-result.json.timeline: prints run status, command metadata, checkpoint table, undo status, side-effect status, and latest hash.rollback plan/rollback apply: baseline operator UX for covered local file changes.rollback planwrites deterministicrollback-plan.json(schema_version: rollback-plan.v1) androllback applywrites checkpoint-localrollback-result.json(schema_version: rollback-result.v1) with post-applyverify-artifactsstatus. External side effects are classified for manual review/compensation, never exact rollback.undo: compatibility alias for the older dry-run/apply UX and artifact paths (undo-preflight.json,rollback-plan.json, andundo-result.json).- Existing runtime boundary demos, including repeated resume, remain documented as local lifecycle examples alongside the watchdog release.
- Framework integrations (LangGraph, CrewAI, AutoGen, browser agents, hosted adapters) are future surfaces; the current public path is the local CLI flow above.
Compensation is not rollback
SafeLoop separates exact local undo from external compensation.
| Term | Means | Does not mean |
|---|---|---|
| Local rollback | SafeLoop applies a reviewed rollback plan for covered local file changes. | External systems, hidden state, or every possible side effect were reset. |
| Compensation | SafeLoop ran a configured cleanup hook after execution began. | Exact rollback, time travel, or an “as-if-never-happened” state. |
compensation_failed |
SafeLoop tried cleanup and the hook failed. | The original side effect was safely undone or can be ignored. |
A compensated run means the configured compensation hook completed; it does not mean exact rollback or an “as-if-never-happened” state. A compensation_failed run means cleanup is incomplete or uncertain and needs operator review. See docs/faq.md, docs/specs/state-machine-and-journal-schema.md, and docs/case-studies/boundary-scenarios.md for the boundary examples.
What it does not claim
SafeLoop is intentionally narrow in this release:
- not tamper-proof; artifacts are tamper-evident local artifacts
- not a hosted control plane yet
- external actions are not blindly reversible
- gitignored files are out of scope unless configured
- no hosted HTTP dashboard v2 or SaaS control plane
- no Slack/GitHub/Vercel external adapter authority
- no remote transparency log or full rollback-to semantics yet
Development
Canonical runtime/API contracts:
- State machine and journal schema:
docs/specs/state-machine-and-journal-schema.md
Run tests:
pytest -q
Build and locally smoke-test a distribution without publishing:
python -m build
python3 -m venv /tmp/safeloop-wheel-smoke
/tmp/safeloop-wheel-smoke/bin/python -m pip install dist/safeloop-0.2.0-py3-none-any.whl
/tmp/safeloop-wheel-smoke/bin/safeloop --help
/tmp/safeloop-wheel-smoke/bin/safeloop --version
For the public MVP readiness boundary, see docs/public-mvp-readiness.md. For maintainer release, tag, and PyPI steps, see docs/release.md.
Rollback public readiness skeleton
The public readiness skeleton for SafeLoop 0.2.0 demonstrates the local rollback workflow end to end:
watch a long-running local task, review and explain rollback groups, plan/apply rollback to start,
plan/apply selected files, plan/apply selected hunks, and run policy-check. The scripted demo is
examples/rollback_selective_demo.sh.
Boundary language for public docs: exact rollback is only claimed for covered local file changes. External side effects require compensation or manual review and are not exact rollback. Local artifacts are tamper-evident review aids, not tamper-proof guarantees. SafeLoop does not claim a remote transparency log unless one is explicitly implemented and configured.
Historical references
These documents describe earlier release boundaries and remain useful for context, but SafeLoop 0.2.0 is the current source version described above:
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file safeloop-0.2.0.tar.gz.
File metadata
- Download URL: safeloop-0.2.0.tar.gz
- Upload date:
- Size: 164.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
07fefec2967c39d04186e18371ad36c33554e750fdc13d3fdde684c5868646d2
|
|
| MD5 |
00d7054c0b358e886c495390bd3a777d
|
|
| BLAKE2b-256 |
e19f7915064f54f074c9f021167294889303a11160e77f2070ec18971df1be78
|
Provenance
The following attestation bundles were made for safeloop-0.2.0.tar.gz:
Publisher:
release.yml on clawdia-saka/safeloop
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
safeloop-0.2.0.tar.gz -
Subject digest:
07fefec2967c39d04186e18371ad36c33554e750fdc13d3fdde684c5868646d2 - Sigstore transparency entry: 1571599698
- Sigstore integration time:
-
Permalink:
clawdia-saka/safeloop@5968959d9c2fe849edfbb8ad6245576ae2d0a356 -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/clawdia-saka
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@5968959d9c2fe849edfbb8ad6245576ae2d0a356 -
Trigger Event:
push
-
Statement type:
File details
Details for the file safeloop-0.2.0-py3-none-any.whl.
File metadata
- Download URL: safeloop-0.2.0-py3-none-any.whl
- Upload date:
- Size: 103.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
11b59a8d6277a343d5ec07b776682f9964e1ce038e34907b6f6fc656ebea0210
|
|
| MD5 |
c53f5cb6ce0dbace24ecf75d84fdecfa
|
|
| BLAKE2b-256 |
c74d3e866351fb2677a8172d790adf9c23f4f49ab6ac5aaf0c31ec84ae1d37d3
|
Provenance
The following attestation bundles were made for safeloop-0.2.0-py3-none-any.whl:
Publisher:
release.yml on clawdia-saka/safeloop
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
safeloop-0.2.0-py3-none-any.whl -
Subject digest:
11b59a8d6277a343d5ec07b776682f9964e1ce038e34907b6f6fc656ebea0210 - Sigstore transparency entry: 1571599707
- Sigstore integration time:
-
Permalink:
clawdia-saka/safeloop@5968959d9c2fe849edfbb8ad6245576ae2d0a356 -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/clawdia-saka
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@5968959d9c2fe849edfbb8ad6245576ae2d0a356 -
Trigger Event:
push
-
Statement type: