scheduleMessageTool function
Implementation
AgentTool scheduleMessageTool(
ScheduledMessageQueue queue, {
/// The calling agent's own mailbox when it is NOT the host's main agent
/// (gh-970): a subagent scheduling a self-reminder must default to its
/// OWN mailbox — the queue's `selfMailbox` always resolves the host's
/// main mailbox, which silently redirected every subagent reminder into
/// main's inbox. Resolved per call; null (the main agent) keeps the
/// legacy defaulting. Hosts wire it to the subagent scope, e.g.
/// `() => activeSubagentId() == null ? null : manager.mailboxOf(...)`.
String? Function()? senderMailbox,
}) {
return AgentTool(
name: 'schedule_message',
description:
'Send a delayed message to an agent mailbox (your own by default) '
'— schedule a follow-up task to check on something later. The '
'message wakes the recipient when it fires; it survives restarts.',
parameters: {
'type': 'object',
'properties': {
'text': {
'type': 'string',
'description': 'The message to deliver when the timer fires.',
},
'delay': {
'type': 'string',
'description':
'How long to wait: 90s / 25m / 2h / 1d (combinable, e.g. '
'1h30m).',
},
'to': {
'type': 'string',
'description': 'Recipient mailbox id; defaults to your own mailbox.',
},
},
'required': ['text', 'delay'],
},
tier: ApprovalTier.write,
execute: (args, cancelToken, onUpdate) async {
final text = args['text'] as String? ?? '';
final delay = parseDelay(args['delay'] as String? ?? '');
if (text.trim().isEmpty) {
return ToolExecutionResult.text('error: text is required');
}
if (delay == null) {
return ToolExecutionResult.text(
'error: delay must look like 90s / 25m / 2h / 1d',
);
}
final sender = senderMailbox?.call();
final id = await queue.schedule(
text: text.trim(),
delay: delay,
to: (args['to'] as String?) ?? sender,
from: sender,
);
final due = DateTime.now().add(delay).toIso8601String();
// CROSS-TOOL CONTRACT: the obligations ledger's pending-wait arming
// (AgentCliPersist.recordPendingWait) parses the scheduled id out
// of this line's `scheduled <id> for ` prefix — keep it stable or
// update both sides together (pinned by
// test/cli/agent_cli_persist_pending_wait_test.dart).
return ToolExecutionResult.text(
'scheduled $id for $due — it will arrive as [scheduled] mail'
'${sender == null ? '' : ' in $sender'}',
);
},
);
}