Scheduled Invocation
Run agents and swarms on cron, interval, or one-shot schedules with built-in retries, jitter, and lifecycle controls.
#Scheduled invocation
Choose between durable remote triggers and an in-process local scheduler. Remote mode is the default.
#Durable remote schedule
from datetime import timedelta
from maivn import Agent
agent = Agent(name="reporter", agent_id="agent-123", version=2, api_key="...")
daily = (
agent.cron("0 9 * * MON-FRI", tz="America/New_York")
.key("weekday-report")
.invoke("Prepare the weekday report")
)
hourly = (
agent.interval(timedelta(hours=1))
.key("hourly-health")
.invoke("Check service health")
)Registration is idempotent for the stable key and target. It calls the server, so examples require a deployed agent, valid credentials, and network access. Remote cron accepts UTC or an IANA timezone string; interval durations must resolve to whole seconds.
Both cron(..., name="Morning report") and interval(..., name="Hourly check") accept a display name in durable mode. You can change it later with .named(...); .key(...) remains the stable registration identity.
All durable trigger helpers inherit their owning agent's identity, including on_webhook, on_email, on_connection, on_manual, and the other event sources. By default, the agent ID is its name and the version is 1. Set agent_id and version once on the agent for a custom identity, as above; agent.serving(), agent.serve(), and maivn serve use those defaults too. Trigger declarations capture this identity when created. Per-session serving overrides do not change existing trigger targets.
Use .target("another-agent", version=3) to explicitly override a trigger's target. A standalone TriggerInvocationBuilder still requires .target(...) because it has no owning agent. Declaring a trigger does not register it; .invoke(...) or .ainvoke(...) performs registration. Give every trigger a .key(...): Studio lists a declaration without one as unfinished, and does not serve or register it until the key is added.
For example, an inbound webhook uses the same owner without repeating its ID:
incoming = (
agent.on_webhook("vault://triggers/reporter")
.key("incoming-report")
.invoke("Process the incoming webhook event")
)The webhook signing secret must already exist at the given vault reference.
Use the returned Trigger for lifecycle and history operations. fire() returns a TriggerFireReceipt; fire_stream() yields the resulting runtime events.
#Local scheduler
Pass mode="local" explicitly when the current Python process should own the schedule:
from maivn import Agent
agent = Agent(name="local-report", api_key="...")
job = agent.cron("0 9 * * *", mode="local").invoke("Prepare the report")
job.on_fire(lambda receipt: print(receipt.fired_at))
job.on_error(lambda receipt: print(receipt.error))Keep the ScheduledJob alive and shut it down using its documented local lifecycle methods. Local schedules do not survive process restarts.
#Local lifetime and one-shot jobs
Local .invoke(...) starts the job immediately. The scheduler uses background threads; a short script must keep its process alive until shutdown:
import time
try:
while not job.is_done:
time.sleep(1)
except KeyboardInterrupt:
pass
finally:
job.stop(drain=True, timeout=30)agent.at(when).invoke(...) is a local one-shot job. Unlike cron and interval, at has no remote-mode option. Use a timezone-aware datetime. For local retries pass Retry(...); durable remote retries use the trigger builder's .with_options(...) instead.