Skip to content

feat: RemoteGuard — remote kill switch for AI agents#219

Open
bmdhodl wants to merge 4 commits intomainfrom
feature/remote-kill-switch
Open

feat: RemoteGuard — remote kill switch for AI agents#219
bmdhodl wants to merge 4 commits intomainfrom
feature/remote-kill-switch

Conversation

@bmdhodl
Copy link
Owner

@bmdhodl bmdhodl commented Feb 20, 2026

Summary

  • RemoteGuard — new guard that polls the dashboard /api/v1/status endpoint via a background thread. When the dashboard sends a kill signal, check() raises AgentKilled. Linked BudgetGuard limits are updated in-place from remote budget changes. Falls back gracefully on network failure (agent keeps running with local guards).
  • BudgetGuard.from_remote() — class method that fetches initial budget config from the dashboard API with local fallback defaults.
  • HttpSink heartbeat — new heartbeat_interval and heartbeat_guards params emit periodic heartbeat events with current guard state, so the dashboard knows the agent is alive and its current spend.

This is the SDK side of the remote kill switch (Move 1 of the platform-first strategy). The dashboard API endpoints (POST /api/v1/agents/:id/kill, GET /api/v1/status) are the next step in the dashboard repo.

What changed

File Change
sdk/agentguard/guards.py +AgentKilled exception, +RemoteGuard class, +BudgetGuard.from_remote()
sdk/agentguard/__init__.py Export new symbols
sdk/agentguard/sinks/http.py +heartbeat mechanism
sdk/tests/test_guards.py +15 RemoteGuard tests, +3 BudgetGuard.from_remote() tests
sdk/tests/test_http_sink.py +2 heartbeat tests
sdk/tests/test_exports.py Updated expected export set
sdk/tests/test_architecture.py Added RemoteGuard to THREAD_SAFE_CLASSES

Design decisions

  • Zero new dependencies — uses urllib.request (same as HttpSink)
  • Daemon thread — background poller is a daemon thread (same pattern as HttpSink flush thread)
  • Thread-safe_lock on all mutable state, added to THREAD_SAFE_CLASSES
  • Graceful degradation — network failures are silently caught; agent keeps running with local guards
  • guards.py stays at 797 lines (under 800 limit)

Test plan

  • RemoteGuard receives kill signal → raises AgentKilled
  • RemoteGuard receives budget update → modifies linked BudgetGuard
  • Network failure → agent continues (graceful fallback)
  • auto_check() delegates to check()
  • reset() clears kill state
  • start() is idempotent
  • BudgetGuard.from_remote() fetches remote config
  • BudgetGuard.from_remote() falls back on network failure
  • Heartbeat emits events with guard state at configured interval
  • Heartbeat disabled by default
  • 530 tests pass, 93.73% coverage, all 9 architectural invariants pass

🤖 Generated with Claude Code

The differentiator: stop agents from a dashboard, not just watch them fail.

- RemoteGuard: polls dashboard /api/v1/status for kill signals and budget
  updates via background thread. Falls back gracefully on network failure.
- AgentKilled exception: raised when dashboard sends kill signal.
- BudgetGuard.from_remote(): fetch budget config from dashboard API with
  local fallback defaults.
- HttpSink heartbeat: periodic heartbeat events with guard state so the
  dashboard knows the agent is alive and current spend.
- 17 new tests (530 total), 93.73% coverage, all architectural invariants pass.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@chatgpt-codex-connector
Copy link

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, you can upgrade your account or add credits to your account and enable them for code reviews in your settings.

bmdhodl and others added 3 commits February 20, 2026 17:34
Bandit B310 flagged urllib.urlopen for allowing file:// schemes.
Added explicit http/https scheme validation before urlopen calls
and nosec comments on validated lines. Also trimmed docstrings to
stay under 800-line module limit.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant