SessionSearchQuery.fromArgs constructor
Parses the tool arguments; throws FormatException with a one-line model-readable reason when a value is invalid.
Implementation
factory SessionSearchQuery.fromArgs(Map<Object?, Object?> args) {
final mode = SessionSearchMode.fromName(args['mode']);
final scope = SessionSearchScope.fromName(args['scope']);
final rawQuery = args['query'];
final query = rawQuery is String ? rawQuery.trim() : '';
if (mode == SessionSearchMode.search && query.isEmpty) {
throw const FormatException(
'query is required (or pass mode: "map" for the archive structure)',
);
}
if (query.length > maxSessionSearchQueryChars) {
throw FormatException(
'query is too long (${query.length} chars — max '
'$maxSessionSearchQueryChars); search for a narrower phrase',
);
}
final regex = args['regex'] == true;
if (regex && query.isNotEmpty) {
try {
RegExp(query);
} on FormatException catch (error) {
throw FormatException('invalid regex: ${error.message}');
}
if (_hasNestedQuantifier(query)) {
throw const FormatException(
'regex has a nested quantifier (a "(a+)+"-shape) — catastrophic '
'backtracking risk; rephrase the pattern without a quantifier '
'directly on a quantified group',
);
}
}
var kinds = const <String>{};
final rawKinds = args['kinds'];
if (rawKinds is List) {
kinds = {
for (final kind in rawKinds)
if (kind is String && kind.trim().isNotEmpty) kind.trim(),
};
}
DateTime? parseStamp(String label, Object? value) {
if (value == null) return null;
final text = value is String ? value.trim() : '$value';
if (text.isEmpty) return null;
final parsed = DateTime.tryParse(text);
if (parsed == null) {
throw FormatException(
'$label must be an ISO-8601 timestamp, got: $text',
);
}
return parsed;
}
final maxHitsRaw = args['maxHits'];
var maxHits = defaultSessionSearchMaxHits;
if (maxHitsRaw is int && maxHitsRaw > 0) {
maxHits = maxHitsRaw.clamp(1, maxSessionSearchMaxHits);
}
final continuationRaw = args['continuation'];
final continuation = continuationRaw is int && continuationRaw > 0
? continuationRaw
: 0;
return SessionSearchQuery(
mode: mode,
scope: scope,
query: query,
kinds: kinds,
before: parseStamp('before', args['before']),
after: parseStamp('after', args['after']),
regex: regex,
maxHits: maxHits,
continuation: continuation,
);
}