StuckToolConfig.fromYaml constructor
StuckToolConfig.fromYaml(
- Object? node
Implementation
factory StuckToolConfig.fromYaml(Object? node) {
if (node == null) return const StuckToolConfig();
if (node is! Map) {
throw ConfigException('agent.stuckTool must be a map, got: $node');
}
int seconds(String key, int fallback, {bool positive = false}) {
final value = node[key];
if (value == null) return fallback;
if (value is! int) {
throw ConfigException('"agent.stuckTool.$key" must be an integer');
}
// A heartbeat cadence of zero would spin the event loop
// (Timer.periodic(Duration.zero)); a grace/floor of zero is a legal
// "immediately" knob (gh-1054 review).
if (positive ? value <= 0 : value < 0) {
throw ConfigException(
'"agent.stuckTool.$key" must be '
'${positive ? '> 0' : '>= 0'}',
);
}
return value;
}
var enabled = true;
var followUp = StuckFollowUpMode.autonomous;
var floorSeconds = defaultStuckFloorSeconds;
num factor = defaultStuckDeclaredTimeoutFactor;
var heartbeatSeconds = defaultStuckHeartbeatSeconds;
var cancelGraceSeconds = defaultStuckCancelGraceSeconds;
var excludeTools = defaultStuckExcludedTools;
for (final key in node.keys) {
switch (key) {
case 'enabled':
final value = node[key];
if (value is! bool) {
throw ConfigException(
'"agent.stuckTool.enabled" must be a boolean',
);
}
enabled = value;
case 'followUp':
final value = node[key];
if (value is! String) {
throw ConfigException(
'"agent.stuckTool.followUp" must be a string '
'(autonomous | advisory)',
);
}
followUp = switch (value) {
'autonomous' => StuckFollowUpMode.autonomous,
'advisory' => StuckFollowUpMode.advisory,
_ => throw ConfigException(
'"agent.stuckTool.followUp" must be "autonomous" or '
'"advisory", got: $value',
),
};
case 'floorSeconds':
floorSeconds = seconds('floorSeconds', defaultStuckFloorSeconds);
case 'declaredTimeoutFactor':
final value = node[key];
if (value is! num || value <= 0) {
throw ConfigException(
'"agent.stuckTool.declaredTimeoutFactor" must be a positive '
'number',
);
}
factor = value;
case 'heartbeatSeconds':
heartbeatSeconds = seconds(
'heartbeatSeconds',
defaultStuckHeartbeatSeconds,
positive: true,
);
case 'cancelGraceSeconds':
cancelGraceSeconds = seconds(
'cancelGraceSeconds',
defaultStuckCancelGraceSeconds,
);
case 'excludeTools':
final value = node[key];
if (value is! List || value.any((entry) => entry is! String)) {
throw ConfigException(
'"agent.stuckTool.excludeTools" must be a list of tool names',
);
}
excludeTools = List<String>.from(value);
default:
throw ConfigException('unknown "agent.stuckTool" key: $key');
}
}
return StuckToolConfig(
enabled: enabled,
followUp: followUp,
floor: Duration(seconds: floorSeconds),
declaredTimeoutFactor: factor,
heartbeatInterval: Duration(seconds: heartbeatSeconds),
cancelGrace: Duration(seconds: cancelGraceSeconds),
excludeTools: excludeTools,
);
}