feat(agent): add voice input capability and standardize tool naming
- Add voice recording with transcribe endpoint (ASR) for multimodal input - Android: add RECORD_AUDIO and INTERNET permissions - Refactor tool naming: frontend tools use 'front.' prefix, backend tools use 'back.' - Migrate calendar tools: create_calendar_event -> back.mutate/list/delete events - Add calendar_event_list.v1 and calendar_operation.v1 UI card types - Update all Flutter and Python tests to match new tool naming conventions - Add record package dependency for voice recording
This commit is contained in:
@@ -12,11 +12,11 @@ void main() {
|
||||
});
|
||||
|
||||
group('getTool', () {
|
||||
test('returns tool definition for create_calendar_event', () {
|
||||
final tool = ToolRegistry.getTool('create_calendar_event');
|
||||
test('returns tool definition for front.navigate_to_route', () {
|
||||
final tool = ToolRegistry.getTool('front.navigate_to_route');
|
||||
|
||||
expect(tool, isNotNull);
|
||||
expect(tool!.name, 'create_calendar_event');
|
||||
expect(tool!.name, 'front.navigate_to_route');
|
||||
expect(tool.description, isNotEmpty);
|
||||
});
|
||||
|
||||
@@ -26,26 +26,16 @@ void main() {
|
||||
});
|
||||
|
||||
group('validateArgs', () {
|
||||
test('returns error for empty args (missing title)', () {
|
||||
final result = ToolRegistry.validateArgs('create_calendar_event', {});
|
||||
test('returns error for empty args (missing target)', () {
|
||||
final result = ToolRegistry.validateArgs('front.navigate_to_route', {});
|
||||
|
||||
expect(result.ok, false);
|
||||
expect(result.error, contains('title'));
|
||||
expect(result.error, contains('target'));
|
||||
});
|
||||
|
||||
test('returns error when missing startAt', () {
|
||||
final result = ToolRegistry.validateArgs('create_calendar_event', {
|
||||
'title': 'Test Event',
|
||||
});
|
||||
|
||||
expect(result.ok, false);
|
||||
expect(result.error, contains('startAt'));
|
||||
});
|
||||
|
||||
test('returns ok: true for valid args with title and startAt', () {
|
||||
final result = ToolRegistry.validateArgs('create_calendar_event', {
|
||||
'title': 'x',
|
||||
'startAt': 'x',
|
||||
test('returns ok: true for valid args', () {
|
||||
final result = ToolRegistry.validateArgs('front.navigate_to_route', {
|
||||
'target': '/settings',
|
||||
});
|
||||
|
||||
expect(result.ok, true);
|
||||
@@ -61,17 +51,6 @@ void main() {
|
||||
});
|
||||
|
||||
group('execute', () {
|
||||
test('returns eventId on success', () async {
|
||||
final result = await ToolRegistry.execute('create_calendar_event', {
|
||||
'title': 'Test Meeting',
|
||||
'startAt': '2026-03-01T10:00:00Z',
|
||||
});
|
||||
|
||||
expect(result['eventId'], isNotNull);
|
||||
expect(result['ok'], true);
|
||||
expect(result['title'], 'Test Meeting');
|
||||
});
|
||||
|
||||
test('throws ToolNotFoundException for unknown tool', () async {
|
||||
expect(
|
||||
() => ToolRegistry.execute('unknown_tool', {}),
|
||||
@@ -79,22 +58,8 @@ void main() {
|
||||
);
|
||||
});
|
||||
|
||||
test('includes optional fields in result', () async {
|
||||
final result = await ToolRegistry.execute('create_calendar_event', {
|
||||
'title': 'Test',
|
||||
'startAt': '2026-03-01T10:00:00Z',
|
||||
'description': 'Description',
|
||||
'location': 'Room A',
|
||||
'endAt': '2026-03-01T11:00:00Z',
|
||||
});
|
||||
|
||||
expect(result['description'], 'Description');
|
||||
expect(result['location'], 'Room A');
|
||||
expect(result['endAt'], '2026-03-01T11:00:00Z');
|
||||
});
|
||||
|
||||
test('navigate_to_route rejects disallowed target', () async {
|
||||
final result = await ToolRegistry.execute('navigate_to_route', {
|
||||
test('front.navigate_to_route rejects disallowed target', () async {
|
||||
final result = await ToolRegistry.execute('front.navigate_to_route', {
|
||||
'target': '/admin',
|
||||
});
|
||||
|
||||
@@ -102,23 +67,26 @@ void main() {
|
||||
expect(result['error'], contains('not allowed'));
|
||||
});
|
||||
|
||||
test('navigate_to_route executes allowed target when navigator is bound', () async {
|
||||
String? navigatedTo;
|
||||
bool replaced = false;
|
||||
RouteNavigationTool.instance.bindNavigator((target, {replace = false}) {
|
||||
navigatedTo = target;
|
||||
replaced = replace;
|
||||
});
|
||||
test(
|
||||
'front.navigate_to_route executes allowed target when navigator is bound',
|
||||
() async {
|
||||
String? navigatedTo;
|
||||
bool replaced = false;
|
||||
RouteNavigationTool.instance.bindNavigator((target, {replace = false}) {
|
||||
navigatedTo = target;
|
||||
replaced = replace;
|
||||
});
|
||||
|
||||
final result = await ToolRegistry.execute('navigate_to_route', {
|
||||
'target': '/settings',
|
||||
'replace': true,
|
||||
});
|
||||
final result = await ToolRegistry.execute('front.navigate_to_route', {
|
||||
'target': '/settings',
|
||||
'replace': true,
|
||||
});
|
||||
|
||||
expect(result['ok'], true);
|
||||
expect(navigatedTo, '/settings');
|
||||
expect(replaced, true);
|
||||
});
|
||||
expect(result['ok'], true);
|
||||
expect(navigatedTo, '/settings');
|
||||
expect(replaced, true);
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
group('getAllTools', () {
|
||||
@@ -126,7 +94,8 @@ void main() {
|
||||
final tools = ToolRegistry.getAllTools();
|
||||
|
||||
expect(tools, isNotEmpty);
|
||||
expect(tools.any((t) => t.name == 'create_calendar_event'), true);
|
||||
expect(tools.any((t) => t.name == 'front.navigate_to_route'), true);
|
||||
expect(tools.any((t) => t.name == 'create_calendar_event'), false);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user