Proactivity (Scheduled Follow-ups)
How an agent schedules its own future follow-up. A one-shot, same-thread continuation via the schedule_follow_up tool. Channel-agnostic, deterministically pinned to the originating thread, gated by an editable config flag.
Proactivity lets an agent wake itself later in the same conversation, e.g. "check back in 2 minutes and tell me if the deploy finished." It is exposed as a single tool, schedule_follow_up, and is off by default.
What it is (and isn't)
schedule_follow_up is a tool, not an agent or a flow. It does not override a channel's manager/IC fleet. When the follow-up fires it re-enters the originating thread and is handled by the normal routing for that thread.
| Primitive | Use for | |
|---|---|---|
| One-shot, "remind me later in this thread" | schedule_follow_up tool (this page) | Defer-and-continue in the same conversation |
| Recurring "every morning…" | A cron flow (cron_manage / flow CRUD) | Persisted, scheduled, governed |
| A real calendar event | The calendar integration (gw_calendar_*) | An event on someone's Google Calendar |
The router correctly routes "schedule a follow-up" toward the calendar tool. To invoke proactivity, phrase it as a deferred re-check ("wait 2 minutes, then re-check this thread") or name the tool explicitly.
How it works
agent calls schedule_follow_up({ instruction, delayMinutes | runAt })
→ enqueues a Cloud Task (queue: flow-agent-continue) at runAt
→ at runAt, agentFollowUpHandler (OIDC-verified) fires:
→ asserts the outbound allowlist for the origin channel
→ re-dispatches the instruction into the SAME thread (humanInitiated=false)
→ the thread's normal agent runs and posts the resultThe tool accepts no destination/channel/recipient parameter — the follow-up always re-dispatches into the originating thread, derived from the run's threadId/source. This is deliberate: it means an agent can never redirect a follow-up to a different channel or person, which is what keeps proactivity safe (see deterministic outbound).
Channel-agnostic
Because it keys off threadId + source and re-dispatches through the shared channel dispatcher, a follow-up fires back on whatever channel it came from:
- @mention in a Slack thread → posts back into that Slack thread
- comment on a Linear issue → posts back into that Linear thread
- a web thread → continues that web thread
Safety rails
- No destination param. Pinned to the originating thread by construction.
- Outbound allowlist re-assert. Before any post, the handler checks
isOutboundDestinationAllowed; if the origin channel is not allowlisted (e.g.#random), it aborts silently and posts nothing. - Delay clamp.
1 minute to 30 days(the 30-day ceiling matches the Cloud Tasks schedule horizon). Longer-horizon reminders need a cron flow, not this tool.
Enabling it
Proactivity is gated by the editable config config/nova-proactivity (read via readEditableConfig, ~60s cache):
{
"enabled": true, // master switch, default false
"allowedAgentIds": [] // empty ⇒ ALL agents; or scope: ["nova-product", "generalist"]
}enabled: false(default) → the tool is never granted; agents have no self-scheduling capability.allowedAgentIds: [](empty) → granted to every agent.allowedAgentIds: ["…"]→ granted only to those agent ids. Grant the routing entry points (router,generalist) and/or specific channel agents. An @mention is often handled bygeneralist, not the channel-named agent, so scope accordingly.
The tool is injected at runtime by maybeGrantScheduleFollowUpTool; you do not edit each agent's tools[] array.
Tool parameters
| Param | Type | Notes |
|---|---|---|
instruction | string (required) | What the agent should do when the follow-up fires, in the same thread. |
delayMinutes | number | Minutes from now. Clamped to 1 to 43200 (30 days). |
runAt | ISO string | Absolute time. Same clamp. Use one of delayMinutes / runAt. |
There is intentionally no channel, recipient, or destination field.
Examples
Slack — ping when a deploy finishes (in #nova-eng):
@nova wait 2 minutes, then re-check the deploy and reply in this thread with the status.
// the agent's tool call
schedule_follow_up({
"delayMinutes": 2,
"instruction": "Re-check whether the deploy finished; summarize status + any errors."
})
// → "Follow-up scheduled for 2026-06-29T17:25:00Z in this thread."
// 2 min later, the agent posts back into the same thread:
// "Deploy follow-up: platform live; Sentry shows 10 unresolved errors, …"Support — watch a cert (in #nova-support):
@nova check this domain's cert every hour until it's issued, and tell me here when it goes ACTIVE.
DM works the same; the follow-up posts back into the DM thread.
Cost
A follow-up is one ordinary agent run re-dispatched into the thread. Typical cost (Sonnet, ~2 rounds incl. a status-check tool call) ≈ $0.03–0.10; bounded by the per-run cost cap. See service tiers and observability for spend tracking and the budget-policy enforcement that gates it.
Limitations
- Same thread only, cannot deliver to a different channel, a specific member's DM, email, or an inbox. Those are deliberate enhancements that would each need allowlist/identity gating + HITL.
- Max horizon 30 days (Cloud Tasks limit). Use a cron flow for longer or recurring schedules.
- It can
@mentiona user inside the originating thread (instruction text is free-form); it just can't redirect delivery to them.
Flows
Flows are multi-step workflows of agents and integration actions, triggered manually, on a cron, or by an external event.
Governance & Safety
How Nova Admin keeps autonomous agents safe. Covers HITL approvals, the outbound allowlist, invocation gating, budget enforcement, and memory retention. Most controls ship observe-first behind editable config flags.