Skip to content

feat(gateway): wakeups — checks an agent schedules for itself, and notify_agent - #351

Open
sarah-chitty-1 wants to merge 6 commits into
mainfrom
sarahchitty/agent-wakeups
Open

sarah-chitty-1 wants to merge 6 commits into
mainfrom
sarahchitty/agent-wakeups

Conversation

@sarah-chitty-1

@sarah-chitty-1 sarah-chitty-1 commented Oct 27, 2026 •

Copy link
Copy Markdown
Collaborator

What and why

In a multi-day task the agent needs a realistic way to come back later. Today it only hears from the world when the user sim writes, or when a hook wakes it for every world event. Real assistants don't work like that: they set their own reminders, and they get pushed only through real channels (a saved-search alert, a text).

This adds the gateway side of that, opt-in per task:

  • urn:agentenv:wakeups/v1 (env/gateway/wakeups.py): once a task turns it on, the agent gets three gateway tools: schedule_check(note, at | every, until), list_scheduled_checks and cancel_scheduled_check. They run on the env's virtual clock. Caps: at most 10 active checks, and repeats no shorter than 1 hour; both are configurable.
  • notify_agent trigger action: a designer-declared notification on a named channel ({"type": "notify_agent", "channel": "Curbfinch alert", "text": "..."}). It's queued for the agent, not sent as the user. Only the text length is logged.
  • Routes /wakeups/config|state|take|clear. A harness or post-turn hook reads what's due through take. state exposes next_check_at, so a runner can jump the clock to it.
  • configure_wakeups task step: turns the wakeups on for one env. It goes after sync_env_clock and before prompt_agent.
  • register_env_triggers refuses notify_agent on a gateway that lacks the feature.

Nothing changes for tasks that don't use it. The tools exist only after configure_wakeups. The extension is added last on the card, and the new trigger action is opt-in. /wakeups/config returns 409 if a backing server already serves a tool with one of these names.

The design and the decisions behind it are in Linear FD-4433. The post-turn hook that wakes the agent is in a follow-up PR stacked on #265.

How it was tested

  • PYTHONPATH=src:packages/agentenv-protocol/src python -m pytest tst/unit/env/gateway/wakeups_test.py tst/unit/task_step/test_configure_wakeups.py tst/unit/task_step/test_register_env_triggers.py tst/unit/env/gateway/gateway_card_test.py: 118 passed.
  • Full unit suite compared with clean main in the same venv: no new failures. The pre-existing ones are the optional extras: openenv, drivers, mcp_cli_builder.
  • An end-to-end run on a real local gateway is in the follow-up PR (test_agent_wakeups_hook_local.py).
  • Before building, a model probe (18 runs across Opus, GPT and Gemini) checked that models use schedule_check sensibly. They scheduled every 3–24 h, never ran away, and never mistook a labelled notification for the person.

@earakely-scale

🤖 Generated with Claude Code

RetriggerView in GreptileConfidence Score: 5/5

The PR appears safe to merge; no actionable issues remain.

Summary

Adds opt-in scheduled checks and trigger notifications on the gateway’s virtual clock.

  • Agents can schedule checks that arrive when their virtual time is due.
  • Trigger rules can queue labelled messages for the agent.

Diagram

%%{init: {'theme': 'neutral'}}%%
flowchart LR
  Task[configure_wakeups] --> Config[Enable check tools]
  Agent[Agent] --> Schedule[schedule_check]
  Config --> Schedule
  Schedule --> Queue[Wakeups]
  Trigger[notify_agent trigger] --> Queue
  Clock[Virtual clock] --> Queue
  Queue --> State[wakeups/state]
  Queue --> Take[wakeups/take]
  State --> Runner[Runner or hook]
  Runner --> Take
  Take --> Message[Due checks and notifications]
Loading

Reviews (6) · Last reviewed commit: "Merge branch 'main' of github.com:scalea..." · Reviewed by Greptile

…tify_agent

What wakes an agent between its turns, on the env's virtual clock (urn:agentenv:wakeups/v1):

- schedule_check / list_scheduled_checks / cancel_scheduled_check: gateway built-in tools, next to get_time,
  that exist only once a task turns them on (configure_wakeups step, or POST /wakeups/config). A check takes
  `at` (RFC 3339 or a duration from now) and/or `every` with `until`; max_active and min_interval cap them.
- notify_agent: a trigger action that queues a notification ({channel, text}, text templated from the provoking
  call) at the current virtual time. Only task designers write it; the agent has no general alert tool.
- GET /wakeups/state says when the next check is due and what is pending; POST /wakeups/take hands out each due
  check and notification once (a repeating check passed several times in one jump comes out once, with a count).

Nothing here wakes anyone: a post-turn hook reads state, moves the clock and takes what is due. Opt-in throughout:
without configure_wakeups no tool appears, and a gateway without notify_agent is refused by register_env_triggers
before any request.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Comment thread src/agent_env/env/gateway/gateway.py
Comment thread src/agent_env/env/gateway/wakeups.py Outdated
Comment thread src/agent_env/env/gateway/wakeups.py Outdated
Comment thread src/agent_env/env/gateway/wakeups.py Outdated
Comment thread src/agent_env/task_step/task_steps/configure_wakeups.py Outdated
sarah-chitty-1 and others added 3 commits October 27, 2026 10:07
…on times, wait for discovery

- take counts how often a repeating check came due and its next time directly, so a long jump over a short every
  costs nothing, and works the whole batch out before marking anything handed out
- schedule refuses an every (or at) too long to count forward, which would otherwise break every later take
- notifications are due by their parsed time, not their text (fractional seconds sorted wrong)
- /wakeups/config finishes tool discovery before its name check (503 if discovery fails), so a later discovery
  can't replace schedule_check unseen
- configure_wakeups logs a one-line completion, not the request body

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… tools and config take

The state, list_scheduled_checks and the too-short error showed Python's timedelta text (3:00:00), which /wakeups/config
then refused when sent back.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…rahchitty/agent-wakeups

# Conflicts:
#	src/agent_env/task/episode/rules.py
Comment thread src/agent_env/env/gateway/wakeups.py
Comment thread src/agent_env/env/gateway/wakeups.py
sarah-chitty-1 and others added 2 commits October 27, 2026 15:02
…history is bounded

- configure with scheduled_checks false cancels the active checks, since the agent no longer has the tool to
- delivered notifications are dropped, and state keeps only the last 50 finished or cancelled checks

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…rahchitty/agent-wakeups

# Conflicts:
#	src/agent_env/env/gateway/constants.py

This branch has not been deployed

No deployments
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