validateInput method
Validate input before execution and permission checks.
Implementation
@override
ValidationResult validateInput(Map<String, dynamic> input) {
final to = input['to'] as String?;
if (to == null || to.trim().isEmpty) {
return const ValidationResult.invalid('to must not be empty');
}
// Parse address for validation
final addr = parseAddress(to);
// Check for empty address targets
if ((addr.scheme == 'bridge' || addr.scheme == 'uds') &&
addr.target.trim().isEmpty) {
return const ValidationResult.invalid('address target must not be empty');
}
// Reject @ notation — there is only one team per session
if (to.contains('@')) {
return const ValidationResult.invalid(
'to must be a bare teammate name or "*" -- there is only one '
'team per session',
);
}
final message = input['message'];
// Plain text message validation
if (message is String) {
// Bridge cross-session: structured messages not allowed
if (addr.scheme == 'bridge') {
// Structured messages cannot be sent cross-session
return const ValidationResult.valid();
}
// UDS cross-session: summary not required
if (addr.scheme == 'uds') {
return const ValidationResult.valid();
}
// Local plain text: summary is required
final summary = input['summary'] as String?;
if (summary == null || summary.trim().isEmpty) {
return const ValidationResult.invalid(
'summary is required when message is a string',
);
}
return const ValidationResult.valid();
}
// Structured message validation
if (message is Map) {
// Structured messages cannot be broadcast
if (to == '*') {
return const ValidationResult.invalid(
'structured messages cannot be broadcast (to: "*")',
);
}
// Structured messages cannot be sent cross-session
if (addr.scheme != 'other') {
return const ValidationResult.invalid(
'structured messages cannot be sent cross-session -- '
'only plain text',
);
}
final type = message['type'] as String?;
// shutdown_response must be sent to team-lead
if (type == 'shutdown_response' && to != teamLeadName) {
return ValidationResult.invalid(
'shutdown_response must be sent to "$teamLeadName"',
);
}
// Rejecting a shutdown requires a reason
if (type == 'shutdown_response') {
final approve = message['approve'] as bool? ?? false;
if (!approve) {
final reason = message['reason'] as String?;
if (reason == null || reason.trim().isEmpty) {
return const ValidationResult.invalid(
'reason is required when rejecting a shutdown request',
);
}
}
}
return const ValidationResult.valid();
}
return const ValidationResult.invalid(
'message must be a string or structured message object',
);
}