diff --git a/flutter-frontend-for-adk/README.md b/flutter-frontend-for-adk/README.md new file mode 100644 index 0000000..41dd54e --- /dev/null +++ b/flutter-frontend-for-adk/README.md @@ -0,0 +1,84 @@ +# Flutter Frontend for ADK Agents (Custom Skill) + +This repository contains a skill intended for use with [Antigravity](https://antigravity.google). It will guide the agent through the discovery, architecture, design, and implementation of a Flutter-based frontend for an Agent Development Kit (ADK) agent written in Python. + +> [!NOTE] +> This repository is a learning resource and an open-source sample. Use it with an experimental project as a way of learning how to use Antigravity to get things done with Flutter. + +--- + +## Capabilities + +The `flutter-frontend-for-adk` skill orchestrates a structured, multi-phase workflow to construct a responsive Flutter client that interfaces with an ADK Python agent: + +1. **Workspace & Agent Discovery:** Maps the agent's purpose, sub-agent hierarchy, API endpoints, event streams, and human-in-the-loop triggers. +2. **Frontend Usage & Behavior:** Defines target platforms (mobile, web, desktop), user experience flows, and screen requirements. +3. **Frontend Architecture:** Establishes directory structure, state management using the `provider` package, and manual JSON serialization models. +4. **Frontend Design & Layout:** Configures visual styles, responsive column layouts, interactive states, and micro-animations. +5. **Scaffolding & Implementation:** Scaffolds the Flutter project, implements the API service (handling REST and Server-Sent Events/SSE streams), and builds the views. +6. **Workspace Integration:** Integrates frontend execution commands into workspace build tools (e.g., Makefiles, script runners) and updates version control settings. + +--- + +## Installation + +To make this skill available to Antigravity, you can install it either globally or locally within a specific workspace. + +### Global Installation + +To make the skill available across all your projects, copy this repository into your global Antigravity customizations directory: + +```bash +mkdir -p ~/.gemini/config/skills/flutter-frontend-for-adk +cp -r . ~/.gemini/config/skills/flutter-frontend-for-adk +``` + +### Local Project Installation + +To make the skill available only within a specific project, copy this repository into the `.agents/skills` directory at the root of that project: + +```bash +mkdir -p /path/to/your/project/.agents/skills/flutter-frontend-for-adk +cp -r . /path/to/your/project/.agents/skills/flutter-frontend-for-adk +``` + +Once copied, Antigravity will automatically discover the skill via its `SKILL.md` definition. + +--- + +## Step-by-Step Usage Example + +This section outlines how to use the skill to replace an existing web frontend with a Flutter client, using the official `deep_search` ADK sample agent as an example. + +### 1. Clone the ADK Samples Repository +Clone the official repository containing the Agent Development Kit samples to your local machine: + +```bash +git clone https://github.com/google/adk-samples.git +cd adk-samples +``` + +### 2. Open the Target Sample +Navigate to the Python version of the `deep_search` sample and open it in your workspace: + +```bash +cd python/deep_search +``` + +Ensure this directory is open in your IDE workspace where Antigravity is active. + +### 3. Remove the Existing Frontend +Prompt Antigravity to remove the React-based frontend that is bundled with the sample. This ensures a clean slate for the new Flutter application. + +You can use a prompt such as: +> "Please remove the React frontend that ships with this sample, along with any mention of it in associated documentation, configuration files, and build scripts." + +Antigravity will delete the React source files, remove web-related dependencies, and clean up workspace references. + +### 4. Generate the Flutter Frontend +Once the workspace is cleaned, instruct Antigravity to use the installed custom skill to build a new Flutter frontend. + +You can use a prompt such as: +> "Please use the `flutter-frontend-for-adk` skill to create a new, Flutter-based frontend for the agent." + +Antigravity will activate the skill and proceed through the six phases described in the [SKILL.md](file:///Users/redbrogdon/source/demos/flutter-frontend-for-adk/SKILL.md) file, prompting you for input and approval at the end of each phase before continuing to the next. diff --git a/flutter-frontend-for-adk/SKILL.md b/flutter-frontend-for-adk/SKILL.md new file mode 100644 index 0000000..5e44462 --- /dev/null +++ b/flutter-frontend-for-adk/SKILL.md @@ -0,0 +1,85 @@ +--- +name: flutter-frontend-for-adk +description: > + Orchestrates the discovery, mapping, design, and implementation of a premium + Flutter-based frontend for an Agent Development Kit (ADK) agent written with Python. +metadata: + author: Antigravity + version: 1.0.0 + requires: + bins: + - flutter + - dart +--- + +# Building a Flutter Frontend for an ADK Agent + +This skill orchestrates the end-to-end workflow of designing, specifying, and implementing a premium Flutter-based frontend for any Agent Development Kit (ADK) agent. + +## Prerequisites & External References + +Before starting, the developer or coding agent should locate ADK framework-level specifications using the following resources instead of restating them in this skill: +1. **Activate ADK Code Skill:** Ensure that `/google-agents-cli-adk-code` is active in the current session. Refer to its reference file `adk-python.md` to understand the core `Event`, `EventActions`, and state management models. +2. **Fetch Official Docs:** Retrieve the official documentation index by calling `curl https://adk.dev/llms.txt` and search for pages related to client serving, networking, or the Python API server. +3. **Inspect Local Code:** To see exact Pydantic definitions and FastAPI endpoint details directly: + * Open and inspect the local server definition inside your python environment at `.venv/lib/python*/site-packages/google/adk/cli/adk_web_server.py`. + * Open and inspect the event schemas at `.venv/lib/python*/site-packages/google/adk/events/event.py` and `event_actions.py`. + +Follow the phases below sequentially. **Do not skip ahead.** You must complete and record the findings of each phase before beginning the next. When creating an implementation plan, add a blocking task for the "Review" step in each phase that has one and wait for the user to perform a review before beginning the next phase. + +--- + +## Phase 1: Workspace & Agent Discovery +Identify the core purpose of the agent in this repository, understand its sub-agent hierarchy, map its API endpoints, event streams, and human-in-the-loop triggers, and record this information. +* **Action:** Read [references/agent_discovery.md](references/agent_discovery.md) for step-by-step discovery instructions. +* **Deliverable:** Create the [AGENT_INTERFACE_NOTES.md](AGENT_INTERFACE_NOTES.md) file in the root of the project. +* **Review:** Summarize (in a paragraph or two) what you just learned for the user. Invite them to inspect `AGENT_DISCOVERY_NOTES.md`, and do not proceed to Phase 2 until they indicate that you should do so. + +--- + +## Phase 2: Frontend Usage & Behavior +Define the high-level behavioral and functional requirements of the frontend: target platforms, screen specifications, feature details, and user experience flows. +* **Action:** Read [references/frontend_usage.md](references/frontend_usage.md) for instructions on defining app behavior. +* **Deliverable:** Create the [FRONTEND_USAGE_NOTES.md](FRONTEND_USAGE_NOTES.md) file in the root of the project. +* **Review:** Summarize (in a paragraph or two) what you just learned for the user. Invite them to inspect `FRONTEND_USAGE_NOTES.md`, and do not proceed to Phase 3 until they indicate that you should do so. + +--- + +## Phase 3: Frontend Architecture +Define the structural patterns of the application, including core folder directory layouts, manual model serializations, and state management setups. +* **Action:** Read [references/frontend_architecture.md](references/frontend_architecture.md) for instructions on defining app architecture. +* **Deliverable:** Create the [FRONTEND_ARCHITECTURE_NOTES.md](FRONTEND_ARCHITECTURE_NOTES.md) file in the root of the project. +* **Review:** Summarize (in a paragraph or two) what you just learned for the user. Invite them to inspect `FRONTEND_ARCHITECTURE_NOTES.md`, and do not proceed to Phase 4 until they indicate that you should do so. + +--- + +## Phase 4: Frontend Design & Layout +Design the visual structure, layout columns, styling guides, states, and micro-animations for the frontend application. +* **Action:** Read [references/frontend_design.md](references/frontend_design.md) for styling and UI blueprints. +* **Deliverable:** Create the [FRONTEND_DESIGN_NOTES.md](FRONTEND_DESIGN_NOTES.md) file in the root of the project. +* **Review:** Summarize (in a paragraph or two) what you just learned for the user. Invite them to inspect `FRONTEND_DESIGN_NOTES.md`, and do not proceed to Phase 5 until they indicate that you should do so. + +---- + +## Phase 5: Scaffolding & Implementation +Create and program the Flutter client application within the workspace, implementing all designs and specifications. +* **Action:** + 1. Read [references/frontend_best_practices.md](references/frontend_best_practices.md) for specific implementation patterns and best practices. + 2. Scaffold a fresh Flutter project inside a folder named `frontend/` at the root of the workspace. Do not specify an org name unless instructed. Create the app to build for only those platforms specified by the user -- if you're not sure, ask. + 3. Add the approved packages (`provider`, `flutter_markdown`, `url_launcher`, and `http`) to `frontend/pubspec.yaml`. + 4. Write the manual serialization models, the `AdkApiService` (implementing standard REST and SSE HTTP streamed response parsers), the `AgentProvider` state notifier, and the responsive views/widgets following `FRONTEND_ARCHITECTURE_NOTES.md` and `FRONTEND_DESIGN_NOTES.md`. + 5. Verify code cleanliness and correctness by running `flutter format`, `flutter analyze`, and `flutter test` inside the `frontend/` directory. +* **Deliverable:** A fully compiled and functional Flutter application located inside the `frontend/` folder. + +--- + +## Phase 6: Workspace Integration & Documentation +Integrate the frontend into the workspace build flows, update version control settings, and document the client setup for developers. +* **Action:** + 1. **Build Configuration:** Examine the codebase to determine how a developer builds and runs the backend agent (e.g. searching for `Makefile` targets, task configurations in `pyproject.toml` like Poe/Taskipy, `package.json` scripts, or custom `run.sh` / `build.sh` scripts). Update these automation tools or files to provide simple commands for developers to build, test, and launch the new Flutter frontend. + 2. **VCS Ignore Setup:** Update the project's version control ignore configurations to make sure they account for the new project and its artifacts. The Flutter frontend should have a nested `.gitignore` file that will account for most things. If the root `.gitignore` file ignores `lib/`, though, make sure to add an exclusion for Flutter's `lib` directory (`!frontend/lib`). + 3. **Documentation Update:** + * If the project has a `README.md` in the root directory, update it to reference the new frontend, detail prerequisites (such as the Flutter SDK version), and outline startup instructions. + * Update the default `frontend/README.md` file to describe the frontend client, its structure, and how to launch both the agent and the client using the build configuration tools updated in step 1. + * Look for any additional pre-existing docs that might need to be updated and do so. +* **Deliverable:** Updated workspace integration configurations, build scripts, and documentation files. diff --git a/flutter-frontend-for-adk/references/agent_discovery.md b/flutter-frontend-for-adk/references/agent_discovery.md new file mode 100644 index 0000000..7423ff0 --- /dev/null +++ b/flutter-frontend-for-adk/references/agent_discovery.md @@ -0,0 +1,94 @@ +# Agent Discovery & Mapping Reference + +This document directs the agent or developer to analyze the target ADK workspace and compile the `AGENT_INTERFACE_NOTES.md` file. + +--- + +## Phase 1: Workspace & Agent Discovery + +Before writing any frontend code, you must analyze the backend agent to understand its capabilities, state variables, and execution patterns. + +### 1. Identify Workspace Entrypoints +* Scan the root directory for documentation (`README.md`, developer specs) to grasp the core purpose of the agent. +* Locate the startup configurations, usually defined in a `Makefile` or `pyproject.toml` (e.g., look for `adk api_server`). + +### 2. Deep Dive Into Backend Agent Logic +Analyze the core agent definition file (typically `app/agent.py` or similar) to map out: +* **The Execution Timeline (Agent Authors):** + * Identify the `root_agent` (the primary coordinator that the server communicates with directly). + * Trace all sub-agents (e.g., in sequential structures, loop structures, or conditional routing trees). + * Note the exact `name` string of every agent. The agent's `name` string will be the value populated in the `author` field of incoming `Event` stream items. Construct a chronological list of these authors representing the pipeline execution flow. +* **State Keys & Custom Callbacks:** + * Identify agent-specific state keys by searching for `output_key="key_name"` parameters on all agent instantiations. + * Search the file for custom callbacks (functions containing parameters of type `ToolContext` or `CallbackContext`). + * Inspect these callback bodies for references to the session's state dictionary (e.g., `callback_context.state["key_name"]` or `tool_context.state["key_name"]`). + * Identify where these callbacks are registered (e.g., `before_agent_callback`, `after_agent_callback`, `after_model_callback`) on specific agents to understand exactly *when* these state variables are updated during the execution sequence. + +### 3. Determine Server Execution Flags +* Examine how the backend API server is run locally. +* Run the command with `--help` (e.g., `uv run adk api_server --help`) or check active configs to determine: + * Port and host configurations (default is `http://127.0.0.1:8000`). + * CORS origin settings (`--allow_origins`). + * Session and artifact persistence backends (e.g., `--session_service_uri`, `--artifact_service_uri`). + +### 4. Record Your Findings +* Create a new file in the root of the project called `AGENT_INTERFACE_NOTES.md`. +* In `AGENT_INTERFACE_NOTES.md` create the following sections: + 1. **Introduction**: a description of the agent, including its name, what its intended purpose is, and a rough description of how it is constructed. + 2. **Agent Entrypoint**: A description of the entrypoint of the agent, including the exact command used to start the agent locally. + 3. **Key Components**: A description of each sub-agent in the agent, including its name, class, and a description of what it does. + +--- + +## Phase 2: ADK API & Event Mapping + +The ADK API server automatically wraps the agent and exposes standard HTTP and streaming endpoints. You must fully understand how communication with the agent works. To do so, you will read and analyze the agent's code, and then record your findings in additional sections in `AGENT_INTERFACE_NOTES.md`. + +### 1. Understand API Endpoints +ADK-based agents expose a REST API that can be used to communicate with the agent. The API is documented in the ADK documentation, but you should also read the agent's code to fully understand how it works. Here are the key endpoints: + +* **Run Agent (Blocking):** `POST /run` + * Executes the agent and returns a complete list of events after completion. +* **Run Agent (SSE Streaming):** `POST /run_sse` + * Establishes a Server-Sent Events stream of events. Used for live chat responses and progress indicators. +* **Session Management:** `GET/POST/DELETE` endpoints under `/apps/{app_name}/users/{user_id}/sessions` to list, create, or delete user sessions. +* **Artifact Loading:** `GET /apps/{app_name}/users/{user_id}/sessions/{session_id}/artifacts/{artifact_name}` to load binary files or documents generated by the agent. + +### 2. Map Payload Schemas +The ADK API uses a specific JSON schema for requests and responses. You should thoroughly understand the schema and record your findings in `AGENT_INTERFACE_NOTES.md`. Here are the key fields: + +* **Run Request (`RunAgentRequest`):** + ```json + { + "app_name": "app", + "user_id": "default_user", + "session_id": "session-uuid", + "new_message": { + "role": "user", + "parts": [{ "text": "Research quantum computing." }] + }, + "streaming": true + } + ``` +* **Response Event:** Each SSE line starting with `data: ` contains an ADK `Event` JSON object. Map out these key fields in Dart: + * `id` (String): Unique identifier of the event. + * `author` (String): The name of the agent or "user" that generated the event. Useful for progress tracking. + * `content` (Object): Contains `parts`, which can hold `text` snippets, `functionCall` parameters (e.g., to `google_search`), `functionResponse` results, or `codeExecutionResult`. + * `actions` (Object): Holds metadata like `stateDelta` (map of state updates) and `artifactDelta` (map of updated files). + +### 3. Understand State, Artifact, & HITL/Resumability Management +The ADK provides global mechanisms for state persistence, file artifact delivery, and execution resumption. You must analyze the agent codebase to extract these specific implementations: + +* **State Keys Schema:** Map each discovered state key to its data structure. Inspect Python helper functions, dataclasses, or Pydantic models to identify if a key stores a `String`, a list of goals, or a complex nested `Map` (e.g., source mapping). +* **Artifact Creation:** Identify if the agent saves file artifacts to GCS or disk using `tool_context.save_artifact("name", part)`. Note their filenames, mime-types, and namespaces (e.g., `user:` vs session-scoped). +* **Human-in-the-Loop (HITL) & Resumability:** Locate exactly how the agent pauses and handles user approval gates: + * *Conversational Gate:* The agent stops execution, asks a question in text chat, and waits for a specific confirmation phrase (e.g., "looks good", "run it") to continue. + * *Formal API Gate:* The agent uses the `request_input` tool, calls `tool_context.request_confirmation(hint)`, or utilizes `require_confirmation` on a `FunctionTool`. + * *Resume Protocol:* If a formal gate is used, document how the client resumes the run (e.g., calling `/run_sse` with the corresponding `invocation_id` or `function_call_event_id` and the user's approval input). + +### 4. Record your Findings +* Add new sections to `AGENT_INTERFACE_NOTES.md` to record what you have learned: + 4. **API Endpoints and Routes**: List the endpoints exposed by the agent, what URL routes are available and what parameters and uses they have. + 5. **Payload Schemas**: List the data schemas used to send messages between the agent and client. Make sure to note which are used for requests vs responses. Record the schemas as JSON. + 6. **State and Artifact Management**: List the state variables and artifacts that the agent uses, and how they are managed. + 7. **Human-in-the-Loop (HITL) & Resumability**: Detail if and where the agent halts for user input or approval. Explain how the client should handle these gates (e.g., via chat text response or by calling a resume endpoint with specific invocation metadata). diff --git a/flutter-frontend-for-adk/references/frontend_architecture.md b/flutter-frontend-for-adk/references/frontend_architecture.md new file mode 100644 index 0000000..7520bc0 --- /dev/null +++ b/flutter-frontend-for-adk/references/frontend_architecture.md @@ -0,0 +1,211 @@ +# Frontend Architecture Reference + +This document directs the agent or developer to define the technical implementation, data model schemas, and folder structures of the frontend, and record them in a root-level `FRONTEND_ARCHITECTURE_NOTES.md` file. + +--- + +## Prerequisites + +> [!IMPORTANT] +> Before defining the frontend architecture, the developer or coding agent **must** verify that both `AGENT_INTERFACE_NOTES.md` and `FRONTEND_USAGE_NOTES.md` exist in the root of the workspace. If either file is missing, **stop immediately** and run Phase 1 (Discovery) and Phase 2 (Usage & Behavior) first. +> +> Refer to these files throughout: +> - Use `AGENT_INTERFACE_NOTES.md` to identify custom state keys, sub-agent execution pathways, and payload JSON models. +> - Use `FRONTEND_USAGE_NOTES.md` to map user features and UI screens to notifier capabilities and concrete widget files. + +--- + +## Instructions for Creating FRONTEND_ARCHITECTURE_NOTES.md + +You must analyze the agent interface (documented in `AGENT_INTERFACE_NOTES.md`) and the frontend behavior (documented in `FRONTEND_USAGE_NOTES.md`) and compile a technical architecture specification called `FRONTEND_ARCHITECTURE_NOTES.md` in the root of the workspace. + +This document must conform to the opinionated architectural constraints defined below, but adapt them to the specific backend agent by providing concrete Dart classes, file names, API payloads, and state properties. + +Create `FRONTEND_ARCHITECTURE_NOTES.md` containing the following sections: + +### 1. Architectural Patterns & Core Guidelines +Document the target architectural patterns, which must follow: +* **Layered separation of concerns:** + * *UI Layer:* Pure Flutter widgets observing state. + * *State Layer:* Single central `ChangeNotifier` managing connection, history lists, stream events, and active states. + * *Service Layer:* Wraps standard cross-platform `package:http` API calls (avoiding native `dart:io` or platform-specific web libraries) to manage connection lifecycles and SSE chunk processing. + * *Model Layer:* Plain Dart classes using manual JSON serialization. +* **SSE streaming parser:** Use a custom connection stream reading raw line-by-line chunks from the HTTP client streamed response. +* **Approved Packages:** List approved packages (`provider`, `flutter_markdown`, `url_launcher`, and `http`). Check with the user before suggesting any additional third-party dependencies. + +### 2. Concrete Directory Scaffold +Create a listing of all specific files and folder structures to be scaffolded under `lib/` matching the target agent's workflow. Give exact filenames instead of placeholders: +* `lib/main.dart` - Entrypoint, theme, and provider initialization. +* `lib/config.dart` - Config values containing the backend API host/port. +* `lib/models/` - List specific model files (e.g. `session.dart`, `event.dart`, and custom models derived from the agent's state structure, such as `research_goal.dart`, `source_citation.dart`). +* `lib/services/` - Specific service files (e.g. `adk_api_service.dart`). +* `lib/providers/` - Specific state notifier files (e.g. `agent_provider.dart`). +* `lib/ui/` - Specific screens and widget files matching the layout specification. + +### 3. Concrete Model Serialization Blueprints +Provide the exact Dart class declarations and manual JSON deserialization factories (`fromJson` / `toJson`) for: +* **Generic Envelope Models:** Standard ADK `Session`, `Event`, and `EventActions` envelopes. +* **Agent-Specific Custom Models:** Inspect `AGENT_INTERFACE_NOTES.md` Section 6 (State & Artifact Management) and map **every** state key listed there to an explicit typed model property in the `Session` model. For example, if the agent uses a `sources` state dictionary to track research sources to cite, define a typed `SourceCitation` class to match. Detail exactly how the `Session.fromJson` factory extracts and deserializes all of these structures. + +### 4. ChangeNotifier State & Action Map +Specify the exact instance variables and methods to be defined in the central `ChangeNotifier` to support the user experience described in `FRONTEND_USAGE_NOTES.md`: +* **State Fields:** List variable types and names (e.g. `List sessions`, `Session? activeSession`, `bool isExecuting`, `String? errorMessage`, etc.). +* **Action Methods:** List specific method names and signatures (e.g. `Future fetchSessionHistory()`, `Future deleteSession(String id)`, `Stream startAgentRun(String prompt)`, `void handleApprovalFeedback(String message)`). + +### 5. Stream Connection & Error Handling Logic +Detail how the service and state provider coordinate during streaming: +* Outline the SSE chunk reading process (decoding byte chunks, splitting lines, yielding decoded data frames). +* Specify how connection interruptions or server errors (e.g. non-200 responses) are propagated to the state provider to set `errorMessage` and transition state safely. + +--- + +## Architectural Guidelines Reference + +To assist you in formulating the concrete blueprints inside `FRONTEND_ARCHITECTURE_NOTES.md`, refer to the following standard code patterns: + +### A. Raw package:http SSE Parser Pattern +The client uses `http.Client` from `package:http` to stream and parse raw SSE chunks: +```dart +import 'dart:convert'; +import 'package:http/http.dart' as http; + +// Core connection logic: +final client = http.Client(); +final request = http.Request('POST', Uri.parse('$baseUrl/run_sse')); +request.headers['Content-Type'] = 'application/json'; +request.headers['Accept'] = 'text/event-stream'; +request.body = jsonEncode(payload); + +final streamedResponse = await client.send(request); +await for (final line in streamedResponse.stream + .transform(utf8.decoder) + .transform(const LineSplitter())) { + final trimmed = line.trim(); + if (trimmed.startsWith('data:')) { + final dataString = trimmed.substring(5).trim(); + // Parse JSON dataString into concrete Event models + } +} +``` + +### B. Standard Session and Event Model Envelopes +ADK serialization payload structures use camelCase properties: +```dart +class Event { + final String id; + final String? invocationId; + final String author; + final String? contentText; + final EventActions actions; + final bool partial; + final bool turnComplete; + + Event({ + required this.id, + this.invocationId, + required this.author, + this.contentText, + required this.actions, + required this.partial, + required this.turnComplete, + }); + + factory Event.fromJson(Map json) { + String? extractedText; + final content = json['content']; + if (content != null && content['parts'] != null) { + final List parts = content['parts']; + if (parts.isNotEmpty && parts.first['text'] != null) { + extractedText = parts.first['text'] as String; + } + } + return Event( + id: json['id'] as String, + invocationId: json['invocationId'] as String?, + author: json['author'] as String? ?? 'system', + contentText: extractedText, + actions: EventActions.fromJson(json['actions'] as Map? ?? {}), + partial: json['partial'] as bool? ?? false, + turnComplete: json['turnComplete'] as bool? ?? false, + ); + } +} + +class EventActions { + final Map stateDelta; + final bool endOfAgent; + + EventActions({ + required this.stateDelta, + required this.endOfAgent, + }); + + factory EventActions.fromJson(Map json) { + return EventActions( + stateDelta: json['stateDelta'] as Map? ?? {}, + endOfAgent: json['endOfAgent'] as bool? ?? false, + ); + } +} +``` + +### C. Standard REST Endpoints Pattern +The client uses standard `package:http` methods for pings, lists, creates, and deletes: +```dart +import 'dart:convert'; +import 'package:http/http.dart' as http; + +class AdkApiService { + final String baseUrl; + final http.Client _client = http.Client(); + + AdkApiService({required this.baseUrl}); + + /// GET /health + Future pingServer() async { + try { + final response = await _client.get(Uri.parse('$baseUrl/health')); + return response.statusCode == 200; + } catch (_) { + return false; + } + } + + /// GET /apps/app/users/default_user/sessions + Future> getSessions() async { + final response = await _client.get( + Uri.parse('$baseUrl/apps/app/users/default_user/sessions'), + ); + if (response.statusCode != 200) { + throw Exception('Failed to fetch sessions: ${response.statusCode}'); + } + final List decodedList = jsonDecode(response.body); + return decodedList + .map((item) => Session.fromJson(item as Map)) + .toList(); + } + + /// POST /apps/app/users/default_user/sessions + Future createSession() async { + final response = await _client.post( + Uri.parse('$baseUrl/apps/app/users/default_user/sessions'), + headers: {'Content-Type': 'application/json'}, + body: jsonEncode({}), // Send empty payload + ); + if (response.statusCode != 200) { + throw Exception('Failed to create session: ${response.statusCode}'); + } + return Session.fromJson(jsonDecode(response.body) as Map); + } + + /// DELETE /apps/app/users/default_user/sessions/{session_id} + Future deleteSession(String sessionId) async { + final response = await _client.delete( + Uri.parse('$baseUrl/apps/app/users/default_user/sessions/$sessionId'), + ); + if (response.statusCode != 200) { + throw Exception('Failed to delete session: ${response.statusCode}'); + } + } +} +``` diff --git a/flutter-frontend-for-adk/references/frontend_best_practices.md b/flutter-frontend-for-adk/references/frontend_best_practices.md new file mode 100644 index 0000000..bceb49e --- /dev/null +++ b/flutter-frontend-for-adk/references/frontend_best_practices.md @@ -0,0 +1,391 @@ +# Frontend Implementation Best Practices + +This document provides concrete code patterns and best practices for assembling the Flutter agent client. These guidelines address common implementation challenges, security configuration, typography, and styling. + +--- + +## 1. macOS and iOS Network Access & Security + +### macOS Sandbox Outbound Connections +When building and testing native macOS Flutter applications, the app runs within a secure sandbox environment by default. If the app needs to fetch assets (like Google Fonts) or communicate with local or remote APIs, it must be granted explicit outbound network permissions. + +#### Implementation Pattern +Locate the entitlements files inside the macOS runner directory: +* `frontend/macos/Runner/DebugProfile.entitlements` +* `frontend/macos/Runner/Release.entitlements` + +Ensure that the key `com.apple.security.network.client` is defined and set to `true` within the `` block: + +```xml + + + + + com.apple.security.app-sandbox + + com.apple.security.network.client + + + +``` + +### iOS App Transport Security (ATS) +Unlike macOS, iOS does not restrict outbound client network calls via sandbox entitlements files. However, Apple's **App Transport Security (ATS)** blocks insecure HTTP connections by default. If the application needs to make plaintext HTTP calls during development (e.g., connecting to a local backend agent server running on `http://127.0.0.1:8000` or `http://localhost:8000`), an exception must be configured. + +#### Implementation Pattern +Locate the `Info.plist` file inside the iOS runner directory: +* `frontend/ios/Runner/Info.plist` + +Add the `NSAppTransportSecurity` dictionary with `NSAllowsLocalNetworking` set to `true` to allow local loopback and local network HTTP connections: + +```xml +NSAppTransportSecurity + + NSAllowsLocalNetworking + + +``` + +--- + +## 2. Rich Markdown Rendering + +Agent response logs contain nested formatting, bold titles, lists, and code blocks. Plain text widgets are insufficient for displaying this nicely. Use `flutter_markdown` with customized typography rules that leverage the Outfit/Inter font system to maintain a premium feel. + +> [!IMPORTANT] +> **Stream-Safe Markdown Formatting:** Make sure that any text field or streaming response that could include markdown formatting is properly formatted and "pretty printed" in real time using `MarkdownBody` instead of standard `Text` widgets, even while the message is incomplete and streaming. + +### Implementation Pattern +Use `MarkdownBody` for rendering single message blocks and wrap it in a customized `MarkdownStyleSheet`: + +```dart +import 'package:flutter/material.dart'; +import 'package:flutter_markdown/flutter_markdown.dart'; +import 'package:google_fonts/google_fonts.dart'; + +Widget buildMarkdownMessage(BuildContext context, String text, Color textColor) { + return MarkdownBody( + data: text, + selectable: true, + styleSheet: MarkdownStyleSheet.fromTheme(Theme.of(context)).copyWith( + p: GoogleFonts.inter( + fontSize: 14, + height: 1.45, + color: textColor, + ), + strong: GoogleFonts.inter( + fontWeight: FontWeight.bold, + color: textColor, + ), + listBullet: GoogleFonts.inter( + fontSize: 14, + color: textColor, + ), + ), + ); +} +``` + +--- + +## 3. Dart Import Ordering & Formatting + +To ensure code cleanliness, maintain structure, and comply with standard style rules, imports must be structured according to the `directives_ordering` rule. + +### 1. Enable Linter Rule +Ensure that the `directives_ordering` rule is enabled in the project's [analysis_options.yaml](file:///Users/redbrogdon/source/flutter-adk/frontend/analysis_options.yaml): + +```yaml +linter: + rules: + directives_ordering: true +``` + +### 2. Automated Import Organizing & Formatting +Instead of manually ordering imports, use the Dart SDK's built-in tools to automate this process. Run the following command inside the `frontend/` directory to automatically sort all imports and resolve auto-fixable lint issues: + +```bash +dart fix --apply +``` + +To format all files according to standard style guidelines, run: + +```bash +dart format . +``` + +--- + +## 4. Sealed Classes & Exhaustive Pattern Matching for Lists + +Lists of messages or UI items from an agent run should be represented using Dart's `sealed` classes. Sealed classes restrict the inheritance hierarchy to the current library, enabling the analyzer to verify that `switch` statements and expressions are exhaustive (handling all possible types without needing a fallback/default clause). + +### Implementation Pattern + +1. **Define the Sealed Model Hierarchy**: + Define a sealed base class representing conversation items, and declare concrete subclasses for each specific data type (e.g., text, tool output, system event, surface widgets): + + ```dart + import 'package:flutter/widgets.dart'; + + sealed class ConversationItem {} + + class TextItem extends ConversationItem { + final String sender; + final String text; + + TextItem({required this.sender, required this.text}); + } + + class ImageItem extends ConversationItem { + final String url; + + ImageItem({required this.url}); + } + ``` + +2. **Render Using Exhaustive Switch Expressions**: + When building lists of items in a `ListView.builder`, map the items using a switch expression. This allows the compiler to enforce compile-time safety; if a new subclass of `ConversationItem` is added later, the compiler will fail to build until it is handled. + + ```dart + Widget buildConversationList(List items) { + return ListView.builder( + itemCount: items.length, + itemBuilder: (context, index) { + final item = items[index]; + + return switch (item) { + TextItem(sender: final sender, text: final text) => + BuildTextMessageBubble(sender: sender, text: text), + ImageItem(url: final url) => + BuildImageMessageBubble(url: url), + }; + }, + ); + } + ``` + +--- + +## 5. Scroll Position Anchoring (Guarding Viewport Yanking) + +When rendering live message feeds, logs, or agent streaming output, the chat window should auto-scroll to the bottom only if the user is already actively looking at the bottom. If the user scrolls up manually to review history, incoming data must not yank their viewport back to the bottom. + +### Implementation Pattern + +1. **Calculate Scroll Position**: + Track if the scroll position is currently at or near the bottom before laying out new items. Use a small threshold (such as `40` pixels) to account for layout variations. +2. **Conditional Animation**: + Trigger the scroll-to-bottom callback only if the active session changed (which warrants a complete view reset) or if the user is already at the bottom and new content is added. + +--- + +## 6. Timer Lifecycle and Resource Cleanup + +If the frontend performs periodic background tasks (such as polling a health endpoint, status checks, or refreshing data), these timers must be managed safely to prevent memory leaks and exceptions when widgets are unmounted from the tree. + +### Implementation Pattern + +1. **Keep a Timer Handle**: + Store the active `Timer` reference inside the state. +2. **Initialize in `initState`**: + Start the periodic loop during the widget initialization phase. +3. **Cancel in `dispose`**: + Cancel the timer in the `dispose` method. Always check if the state is still `mounted` before executing calls from background asynchronous triggers. + +```dart +import 'dart:async'; +import 'package:flutter/material.dart'; + +class StatusMonitor extends StatefulWidget { + const StatusMonitor({super.key}); + + @override + State createState() => _StatusMonitorState(); +} + +class _StatusMonitorState extends State { + Timer? _statusTimer; + + @override + void initState() { + super.initState(); + // Periodically run background check + _statusTimer = Timer.periodic(const Duration(seconds: 10), (timer) { + if (mounted) { + _runHealthCheck(); + } + }); + } + + @override + void dispose() { + // Prevent memory leak by canceling active timers + _statusTimer?.cancel(); + super.dispose(); + } + + void _runHealthCheck() { + // Perform API call + } + + @override + Widget build(BuildContext context) { + return const SizedBox.shrink(); + } +} +``` + +--- + +## 7. Web & Cross-Platform Compatibility (Avoiding `dart:io` Pitfalls) + +To ensure the client runs flawlessly on all target platforms (especially Flutter Web / Chrome), code must be structured to avoid platform-specific libraries that throw runtime exceptions in browsers. + +### Avoid `dart:io` Imports and APIs +* **HttpClient**: Do not import or use `HttpClient` or `Platform` from `dart:io`. Web browsers do not support these native socket-based operations, causing immediate runtime crashes like `Unsupported operation: Platform._version` or `Unsupported operation: HttpClient`. +* **Cross-Platform Alternatives**: Always use standard cross-platform packages: + * Use `package:http` (e.g., `http.Client`) for network operations and SSE streams. + * Use `package:url_launcher` for opening links. + * Use `kIsWeb` from `package:flutter/foundation.dart` for checking if running on Web. + +### Safe Platform Checks +If platform-specific logic is required, check `kIsWeb` first. Never access properties of the `Platform` class from `dart:io` on web targets: + +```dart +import 'package:flutter/foundation.dart' show kIsWeb; +import 'dart:io' show Platform; // WARNING: Do not import/reference if building for web! + +bool get isWebOrDesktop { + if (kIsWeb) return true; + // This is safe because Platform is only accessed when kIsWeb is false + return Platform.isMacOS || Platform.isWindows || Platform.isLinux; +} +``` + +*Best Practice:* Whenever possible, use UI responsiveness thresholds (like `LayoutBuilder` width constraints) to adapt screens, rather than hardcoding platform checks. + +--- + +## 8. Server-Sent Events (SSE) Streaming vs. Completed Message Handling + +When listening to a Server-Sent Events (SSE) stream from the ADK server, the client receives both partial chunk events (representing incremental text generation) and final completed events. To prevent duplicate, fragmented, or partial message bubbles from cluttering the conversation history, follow these guidelines: + +### 1. Separate Stream States +* **Partial Event Chunk (`event.partial == true`):** The text in these events represents active streaming data. Do **NOT** add these events to the permanent session event history list. Instead, accumulate the incoming characters inside temporary state variables (e.g., `activeStreamingResponse` and `activeStreamingAuthor`) in your state manager. +* **Complete Event Chunk (`event.partial == false`):** Once a final event chunk is received, append it to the permanent session event history list and clear the temporary streaming state accumulators. + +### 2. Implementation Pattern +Inside the stream receiver block in your state provider, handle the parsed events like so: + +```dart +await for (final event in eventStream) { + if (event.partial) { + // Accumulate streaming text in temporary state variables + _activeStreamingAuthor = event.author; + _activeStreamingResponse = (_activeStreamingResponse ?? '') + (event.contentText ?? ''); + } else { + // Stream chunk finished: Clear accumulator + _activeStreamingResponse = null; + _activeStreamingAuthor = null; + + // Append the completed event to conversation history list + final updatedEvents = List.from(_activeSession!.events)..add(event); + _activeSession = _activeSession!.copyWith(events: updatedEvents); + + // Apply state deltas if present + if (event.actions.stateDelta.isNotEmpty) { + _mergeStateDelta(event.actions.stateDelta); + } + } + notifyListeners(); +} +``` + +--- + +## 9. Casing Resiliency in JSON Deserialization (ADK Framework Discrepancy) + +The ADK framework-level endpoints return camelCase keys (such as `invocationId`, `turnComplete`, `functionCall`) in `/run_sse` chunked streams, but return snake_case keys (such as `invocation_id`, `turn_complete`, `function_call`) in REST history responses. The parser must check for both casings to support all ADK-based agents. + +### Implementation Pattern + +When parsing fields that differ between streams and history, check both keys: + +```dart +final fc = part['functionCall'] ?? part['function_call']; +final id = json['id'] ?? json['id']; +final invocationId = json['invocationId'] ?? json['invocation_id']; +``` + +--- + +## 10. Generic Tool Log Extraction (Built-in Grounding vs. Explicit Function Calls) + +To ensure that the client can display the tool execution history of any ADK-based agent (including routing decisions, planning operations, or web searches), the frontend should parse all tool invocations generically rather than hardcoding checks for specific tool names (e.g. `google_search`). + +### 1. Define a Generic `ToolCall` Model + +```dart +class ToolCall { + final String name; + final Map arguments; + + ToolCall({required this.name, required this.arguments}); + + String get displayString { + if (name == 'google_search') { + final q = arguments['query'] ?? + arguments['search_query'] ?? + arguments['searchQuery'] ?? + arguments['q'] ?? + ''; + return 'Google Search: "$q"'; + } + // Format other tools nicely, e.g. "plan_generator(request: ...)" + final argsStr = arguments.entries + .map((e) => '${e.key}: ${e.value}') + .join(', '); + return '$name($argsStr)'; + } +} +``` + +### 2. Implementation Pattern + +In the `Event` deserializer, extract both internal Gemini grounding queries and explicit function calls from parts into the `toolCalls` list: + +```dart +final List calls = []; + +// 1. Extract from grounding metadata (Gemini built-in search tool) +final gm = json['groundingMetadata'] ?? json['grounding_metadata']; +if (gm != null && gm is Map) { + final wsq = gm['webSearchQueries'] ?? gm['web_search_queries']; + if (wsq != null && wsq is List) { + for (final q in wsq) { + if (q != null) { + calls.add(ToolCall( + name: 'google_search', + arguments: {'query': q.toString()}, + )); + } + } + } +} + +// 2. Extract from parts (for explicit/custom tool calls) +for (final part in partsList) { + if (part is Map) { + final fc = part['functionCall'] ?? part['function_call']; + if (fc != null && fc is Map) { + final name = fc['name'] as String? ?? 'unknown_tool'; + final args = fc['args'] != null && fc['args'] is Map + ? Map.from(fc['args'] as Map) + : {}; + calls.add(ToolCall(name: name, arguments: args)); + } + } +} +``` + diff --git a/flutter-frontend-for-adk/references/frontend_design.md b/flutter-frontend-for-adk/references/frontend_design.md new file mode 100644 index 0000000..a3321a5 --- /dev/null +++ b/flutter-frontend-for-adk/references/frontend_design.md @@ -0,0 +1,131 @@ +# Frontend Design & Layout Reference + +This document directs the agent or developer to define the visual style, theming, responsive layouts, and interactive behaviors of the frontend, and record them in a root-level `FRONTEND_DESIGN_NOTES.md` file. + +--- + +## Prerequisites + +> [!IMPORTANT] +> Before defining the frontend design, the developer or coding agent **must** verify that `AGENT_INTERFACE_NOTES.md`, `FRONTEND_USAGE_NOTES.md`, and `FRONTEND_ARCHITECTURE_NOTES.md` exist in the root of the workspace. If any of these files are missing, **stop immediately** and complete the earlier phases first. +> +> Refer to these files throughout: +> - Use `AGENT_INTERFACE_NOTES.md` to understand the target agent itself (its core purpose, sub-agent catalog, and execution lifecycle) so that you can design custom visual indicators and timeline transitions that accurately reflect the running backend workflow. +> - Use `FRONTEND_USAGE_NOTES.md` to identify the required screens, features, and conversational sequences that need design specs. +> - Use `FRONTEND_ARCHITECTURE_NOTES.md` to align screen designs and widget layout divisions with the technical file structure (e.g. matching `dashboard_screen.dart` and custom widgets). + +--- + +## Instructions for Creating FRONTEND_DESIGN_NOTES.md + +### Step 1: Collect User Design Preferences +Before writing the design notes, the coding agent **must** present a set of options to the user in the chat and ask for their preferences. Do not assume or decide on these values without user input. Ask the user for their preferences on these topics: + +1. **Visual Theme & Tone** +2. **Primary Accent Color** +3. **Typography & Font Choices** +4. **Animations & Motion Style** + +Suggest some pre-generated options for each one based on the notes in `FRONTEND_USAGE_NOTES.md`, but allow the user to specify something else. + +--- + +### Step 2: Analyze the Agent's Screens & Adapt Layouts +Read `FRONTEND_USAGE_NOTES.md` Section 3 (Screen Specifications) to identify the specific screens, panels, drawers, or views that this specific agent requires. +Do not assume a standard 3-column split view; adapt your visual layout to the specific screens defined by the user's agent. + +--- + +### Step 3: Write FRONTEND_DESIGN_NOTES.md +Create `FRONTEND_DESIGN_NOTES.md` in the root of the project containing the following sections, using the user's responses from Step 1 and the screens analyzed in Step 2: + +#### 1. Global Theme & Visual Style +Specify the exact theme configurations for the application: +* **Color Palette:** Define the HEX values and Flutter color codes (`0xFF...`) for: + * *App Background:* Main scaffold background canvas. + * *Surface Background:* Lighter background for cards, sidebars, and input overlays. + * *Accent Primary:* Vibrant highlight color for primary actions, sliders, or active progress steps (based on the user's accent preference). + * *Accent Glow/Secondary:* Auxiliary highlight color for alerts or success indicators. + * *Text Primary:* Highly legible font color for headings, text inputs, and outputs. + * *Text Secondary:* Muted font color for helper labels and timestamp metadata. + * *Borders / Dividers:* Subtle boundary lines separating layout panes. +* **Typography Hierarchy:** Outline the chosen font families, font sizes, weights, and line-heights (optimized for readability based on the user's font preferences). +* **Visual Enhancements:** Shadow parameters (`BoxShadow` values), border radii (typically `12px` for modern cards, `8px` for inputs), and overlay opacities. +* **Accessibility Design:** Define guidelines for: + * Supporting system text-scaling preferences (preventing text clipping or overflow). + * Ensuring AA/AAA contrast ratios for text on active surfaces. + * Minimum touch/tap target sizes (minimum `48x48` logical pixels for mobile/tablet platforms). + +#### 2. Screen Specifications & Route Navigation +Define the routes, navigation patterns, and panel layouts for the specific screens derived in Step 2: +* **Routing Scheme:** Specify route paths and names (e.g., `/` for Dashboard, `/settings` for settings). +* **Screen Layout Structure:** Explain how the screen real estate is shared among the agent-specific panels on widescreen/desktop layouts. + +#### 3. Screen Breakpoints & Adaptive Layouts +Detail exactly how the app reflows the agent-specific panels across different screen widths: +* **Breakpoint Definitions:** + * *Desktop / Widescreen:* Width `>= 1024px` (All major panels visible side-by-side). + * *Tablet / Medium:* Width `< 1024px` and `>= 600px` (Sidebar collapses into a drawer, panels share remaining split space). + * *Mobile:* Width `< 600px` (Single column; reflows split panels into tabbed navigation pages or bottom sheets). +* **Platform Adaptations:** Design adjustments for target platforms (e.g., scrollbars and mouse-hover states for Desktop/Web, safe area margins and touch gestures for Mobile/iOS/Android). + +#### 4. Interactive Components & Micro-animations +Define states and animations matching the user's motion preference (Step 1) for the custom widgets: +* **Active Processing Indicators:** Pulse animations or loading spinners representing running agent states. +* **Hover & Active Highlights:** Visual feedback (scale, color shifts, opacity changes) when hovering or clicking buttons, cards, and links. +* **Content Transitions:** Smooth opacity fades (`FadeIn`) or sliding animations (`SlideTransition`) for streaming content blocks, plan checklists, or new message feeds. + +--- + +## Design Reference Guidelines + +Use the following code definitions as standard implementation patterns when writing `FRONTEND_DESIGN_NOTES.md`: + +### A. Dark Mode Material ThemeData Definition +```dart +final ThemeData darkTheme = ThemeData( + brightness: Brightness.dark, + scaffoldBackgroundColor: const Color(0xFF0B0F19), + cardColor: const Color(0xFF131926), + primaryColor: const Color(0xFF6366F1), + dividerColor: const Color(0xFF1E293B), + textTheme: const TextTheme( + headlineLarge: TextStyle(fontSize: 28, fontWeight: FontWeight.bold, color: Color(0xFFF8FAFC)), + bodyMedium: TextStyle(fontSize: 15, height: 1.5, color: Color(0xFF94A3B8)), + ), +); +``` + +### B. Responsive Layout Implementation Pattern +Use `LayoutBuilder` to dynamically reflow the UI columns based on screen width: +```dart +Widget build(BuildContext context) { + return Scaffold( + body: LayoutBuilder( + builder: (context, constraints) { + if (constraints.maxWidth >= 1024) { + // Desktop: Show sidebar and panels side-by-side + return Row( + children: [ + const SidebarWidget(width: 260), + Expanded(child: ChatPanelWidget()), + Expanded(child: WorkspacePanelWidget()), + ], + ); + } else if (constraints.maxWidth >= 600) { + // Tablet: Collapsible sidebar, side-by-side panels + return Row( + children: [ + Expanded(child: ChatPanelWidget()), + Expanded(child: WorkspacePanelWidget()), + ], + ); + } else { + // Mobile: Single panel with tab navigation + return MobileTabbedView(); + } + }, + ), + ); +} +``` diff --git a/flutter-frontend-for-adk/references/frontend_usage.md b/flutter-frontend-for-adk/references/frontend_usage.md new file mode 100644 index 0000000..bd58d0e --- /dev/null +++ b/flutter-frontend-for-adk/references/frontend_usage.md @@ -0,0 +1,55 @@ +# Frontend Usage & Behavior Reference + +This document directs the agent or developer to define the functional, behavioral, and feature requirements of the frontend and record them in a root-level `FRONTEND_USAGE_NOTES.md` file. + +--- + +## Prerequisites + +> [!IMPORTANT] +> Before defining the frontend usage and behavior, the developer or coding agent **must** verify that `AGENT_INTERFACE_NOTES.md` exists in the root of the workspace. If this file is missing, **stop immediately** and run Phase 1 (Workspace & Agent Discovery) first to analyze the agent and create it. + +## Instructions for Creating FRONTEND_USAGE_NOTES.md + +You must analyze the agent requirements (documented in `AGENT_INTERFACE_NOTES.md` and `README.md`) and compile a functional specification called `FRONTEND_USAGE_NOTES.md` in the root of the workspace. + +This document must focus strictly on **behavior, screens, and user features**, leaving out internal architecture patterns (like Riverpod or Clean Architecture) and visual design details (like specific color tokens, font files, and padding values). + +Create `FRONTEND_USAGE_NOTES.md` containing the following sections: + +### 1. Introduction & Target Platforms +* **Overview:** Briefly describe what the application does from a user's point of view (e.g., interactive chat assistant, structured report builder, data analyzer). +* **Target Platforms:** Detail which platforms the frontend supports (e.g. Flutter Web, macOS Desktop, iOS, Android). You should ask the user which ones they want the app to build for. + +### 2. User Experience Flow +Describe the high-level step-by-step lifecycle of a user session: +1. **Onboarding:** How the user starts (e.g., entering a topic, prompt, or file upload). +2. **Interactive Gates (if applicable):** How the user reviews and edits intermediate state (e.g. reviewing plans, configuring parameters, or providing approval feedback). +3. **Execution Observation:** What the user sees while the autonomous agent or workflow loop is executing (e.g. progress updates, sub-agent transitions, or active tool invocations). +4. **Output Review:** How the user interacts with the final deliverables (e.g. browsing reports, opening references/sources, exporting files, or viewing charts). + +### 3. Screen Specifications +Detail the functionality and interactive controls of each view in the application: +* **Dashboard Layout:** + * Sidebar containing session lists (creating new sessions, switching between runs, and deleting sessions). + * Connection status indicators (backend API online/offline). +* **Interaction / Chat View:** + * Standard text input and send action. + * Message log showing user vs agent/system messages. + * *Human-in-the-Loop (HITL) Controls:* Custom interactive widgets rendered when the agent requests confirmation or input (e.g. structured checklists, parameter sliders, or yes/no approval prompts). +* **Workspace / Execution View:** + * *Progress Timeline:* A progress indicator displaying the execution status, active sub-agent name (drawn from the event's `author` tag), or current node in the workflow graph. + * *Tool Invocation Logs:* A live activity log showing active tool executions (e.g., web searches, database queries, code interpreter sandboxes) including inputs and status. + * *Rich Output Viewer:* Renderers for the completed deliverables (e.g. Markdown text, comparison tables, code syntax highlighting, or file download buttons). + * *References / Citation Drawer:* Displays source documentation, grounding links, or database record cards when the user clicks inline citations/references in the output. + +### 4. User-Facing Feature Checklist +Create a prioritized list (e.g., MUST HAVE, SHOULD HAVE, COULD HAVE) of functional features: +* Multi-turn chat conversation. +* Interactive state review and approval gate controls. +* Server-Sent Events (SSE) streaming parse (word-by-word message rendering). +* Live execution progress and sub-agent timeline tracking. +* Real-time tool execution logging. +* Markdown or rich output rendering (supporting tables, links, and code). +* Clickable citation links with source detail views. +* Session history storage (listing, switching, and deleting runs).