diff --git a/.gitignore b/.gitignore index 52a5a7cf36..b1d9e315a4 100644 --- a/.gitignore +++ b/.gitignore @@ -148,4 +148,5 @@ package-lock.json *.tsbuildinfo .wireit/ .angular/ -*.ts \ No newline at end of file +*.ts +uv.lock \ No newline at end of file diff --git a/examples/agent/a2ui_agent/README.md b/examples/agent/a2ui_agent/README.md new file mode 100644 index 0000000000..b8b3e9195a --- /dev/null +++ b/examples/agent/a2ui_agent/README.md @@ -0,0 +1,110 @@ +# A2UI in AgentScope + +[A2UI (Agent-to-Agent UI)](https://github.com/google/A2UI) is a protocol for agents to send +streaming, interactive user interfaces to clients. It enables LLMs to generate platform-agnostic, +declarative UI definitions that clients can render progressively using native widget sets. + +In this example, we demonstrate how to integrate A2UI into a ReAct agent in AgentScope. This +implementation is based on the official A2UI agent samples, adapted to use AgentScope's agent +framework. + +Specifically, we have: + +1. **Reimplemented the agent with AgentScope**: The agent part of the official A2UI samples has + been reimplemented using AgentScope's `ReActAgent`, providing a more familiar and integrated + development experience for AgentScope users. + +2. **Progressive schema and template exposure via skills**: To help the agent learn and generate + A2UI-compliant interfaces, we use AgentScope's skill system to progressively expose the A2UI + schema and UI templates. The agent can dynamically load these resources through the + `A2UI_response_generator` skill, enabling it to understand component definitions and learn from + example UI structures. + +## Note on External Dependencies + +The following directories in this example contain content sourced from the [Google A2UI repository](https://github.com/google/A2UI): + +- **`samples/client/`**: A2UI client sample applications + +**NPM Package Status**: As of now, the A2UI client libraries (`@a2ui/lit` and `@a2ui/angular`) are **not yet published to NPM**. According to the [official A2UI client setup guide](https://a2ui.org/guides/client-setup/#renderers): "The Lit client library is not yet published to NPM. Check back in the coming days." + +Therefore, these dependencies are currently included in this example repository using local file paths (e.g., `"@a2ui/lit": "file:../../../../renderers/lit"` in `package.json` files). This mirrors the approach used in the [official A2UI repository](https://github.com/google/A2UI), where the renderers and samples also use local file paths to reference each other. Additionally, the `copy-spec` task in `renderers/lit/package.json` copies files from the local `specification/` directory during the build process. + +**Future Plans**: Once those libraries are published to NPM, we plan to gradually migrate to using the official NPM packages and remove these locally included directories. + +## Quick Start + +Download the a2ui and agentscope package to the same directory + +```bash +git clone https://github.com/google/A2UI.git +git clone -b main https://github.com/agentscope-ai/agentscope.git +# copy the renders and specification directory to AgentScope/examples/agent/a2ui_agent +copy -r a2ui/renderers AgentScope/examples/agent/a2ui_agent +copy -r a2ui/specification AgentScope/examples/agent/a2ui_agent +``` + + +Then, navigate to the client directory and run the restaurant finder demo: + +```bash +cd AgentScope/examples/agent/a2ui_agent/samples/client/lit +npm run demo:restaurant +``` + +This command will: +- Install dependencies and build the A2UI renderer +- Start the A2A server (AgentScope agent) for the restaurant finder +- Launch the client application in your browser + +> Note: +> - The example is built with DashScope chat model. Make sure to set your `DASHSCOPE_API_KEY` +> environment variable before running the demo. +> - If you are using Qwen series models, we recommend using `qwen3-max` for better performance in +> generating A2UI-compliant JSON responses. +> - Generating UI JSON responses may take some time, typically 1-2 minutes, as the agent needs to +> process the schema, examples, and generate complex UI structures. +> - The demo uses the standard A2UI catalog. Custom catalog and inline catalog support are under +> development. + +## Roadmap + +AgentScope's main focus going forward will be on improving **How Agents Work** with A2UI. The +workflow we're working towards is: + +``` +User Input → Agent Logic → LLM → A2UI JSON +``` + +Our optimization efforts will focus on: + +- **Agent Logic**: Improving how agents process inputs and orchestrate the generation of A2UI JSON + messages + + +- **Handle user interactions from the client**: Enabling agents to properly process and respond to + user interactions from the client (such as button clicks, form submissions), treating them as new + user input to create a continuous interactive loop + +**Current approach**: The skill-based method we've implemented in this example is our first step +towards this goal. By using AgentScope's skill system to progressively expose the A2UI schema and +templates, agents can learn to generate compliant UI structures. Future improvements will focus on +streamlining this process and making it more intuitive for developers to build A2UI-capable agents. + +**Next steps for Agent Logic improvement** + +- **Agent skills improvements**: + - Support flexible schema addition: Enable developers to easily add and customize schemas without + modifying core skill code + - Separate schema and examples into dedicated folders: Organize schema definitions and example + templates into distinct directories for better maintainability and clearer structure + +- **Context management in Memory for A2UI long context**: + - Currently, A2UI messages are extremely long, which makes multi-turn interactions inefficient + and degrades the quality of agent responses. We plan to implement better context management + strategies to handle these long-form messages and improve the quality of multi-turn conversations. + +- **Keep up with A2UI protocol updates**: + - We will follow A2UI protocol updates and make corresponding adjustments. For example, we plan to + support streaming UI JSON introduced in A2UI v0.9. + diff --git a/examples/agent/a2ui_agent/samples/agent/general_agent/__main__.py b/examples/agent/a2ui_agent/samples/agent/general_agent/__main__.py new file mode 100644 index 0000000000..771acd37af --- /dev/null +++ b/examples/agent/a2ui_agent/samples/agent/general_agent/__main__.py @@ -0,0 +1,35 @@ +# -*- coding: utf-8 -*- +"""Main entry point for running the restaurant finder A2UI server.""" +import logging +import os +import uvicorn +from starlette.staticfiles import StaticFiles +from setup_a2ui_server import app + +if __name__ == "__main__": + # Get the directory where this script is located + script_dir = os.path.dirname(os.path.abspath(__file__)) + images_dir = os.path.join(script_dir, "images") + + # Mount static files if images directory exists + if os.path.exists(images_dir): + app.mount("/static", StaticFiles(directory=images_dir), name="static") + logging.info( + "Mounted static files from %s at /static", + images_dir, + ) + else: + logging.warning( + "Images directory not found at %s, " + "static files will not be served", + images_dir, + ) + + # Run the app manually with uvicorn + uvicorn.run( + app, + host="0.0.0.0", + port=10002, + log_level="info", + access_log=True, + ) diff --git a/examples/agent/a2ui_agent/samples/agent/general_agent/a2ui_utils.py b/examples/agent/a2ui_agent/samples/agent/general_agent/a2ui_utils.py new file mode 100644 index 0000000000..b0935bb1c5 --- /dev/null +++ b/examples/agent/a2ui_agent/samples/agent/general_agent/a2ui_utils.py @@ -0,0 +1,318 @@ +# -*- coding: utf-8 -*- +"""Utility functions for A2UI agent integration.""" +import json +from typing import Any +from pydantic import BaseModel +from pydantic import Field + +from a2a.types import ( + DataPart, + TextPart, + Message, + Part, +) +from a2ui.a2ui_extension import ( + A2UI_MIME_TYPE, + MIME_TYPE_KEY, + A2UI_EXTENSION_URI, +) + +from agentscope._logging import logger + + +class A2UIResponse(BaseModel): + """Response model for A2UI formatted output.""" + + response_with_a2ui: str = Field( + description="The response with A2UI JSON", + ) + + +def check_a2ui_extension(*args: Any) -> bool: + """Check if a2ui extension is requested in the request context. + + Args: + *args: Variable arguments that may contain ServerCallContext as the + first element. + + Returns: + True if a2ui extension is requested and activated, False otherwise. + """ + # Extract context from args (ServerCallContext is typically the first + # element) + if not args or len(args) == 0: + logger.warning("check_a2ui_extension: No context provided in args") + return False + + context = args[0] + + # Check if context has requested_extensions attribute + if not hasattr(context, "requested_extensions"): + logger.warning( + "check_a2ui_extension: Context does not have " + "requested_extensions attribute", + ) + return False + + # Check if A2UI extension is requested + if A2UI_EXTENSION_URI in context.requested_extensions: + # Activate the extension if add_activated_extension method exists + if hasattr(context, "add_activated_extension"): + context.add_activated_extension(A2UI_EXTENSION_URI) + logger.info("A2UI extension activated: %s", A2UI_EXTENSION_URI) + else: + logger.warning( + "check_a2ui_extension: Context does not have " + "add_activated_extension method", + ) + return True + + return False + + +def transfer_ui_event_to_query(ui_event_part: dict) -> str: + """Transfer UI event to a query string. + + Args: + ui_event_part: A dictionary containing UI event information with + actionName and context. + + Returns: + A formatted query string based on the UI event action. + """ + action = ui_event_part.get("actionName") + ctx = ui_event_part.get("context", {}) + + if action in ["book_restaurant", "select_item"]: + restaurant_name = ctx.get("restaurantName", "Unknown Restaurant") + address = ctx.get("address", "Address not provided") + image_url = ctx.get("imageUrl", "") + query = ( + f"USER_WANTS_TO_BOOK: {restaurant_name}, " + f"Address: {address}, ImageURL: {image_url}" + ) + elif action == "submit_booking": + restaurant_name = ctx.get("restaurantName", "Unknown Restaurant") + party_size = ctx.get("partySize", "Unknown Size") + reservation_time = ctx.get("reservationTime", "Unknown Time") + dietary_reqs = ctx.get("dietary", "None") + image_url = ctx.get("imageUrl", "") + query = ( + f"User submitted a booking for {restaurant_name} " + f"for {party_size} people at {reservation_time} " + f"with dietary requirements: {dietary_reqs}. " + f"The image URL is {image_url}" + ) + else: + query = f"User submitted an event: {action} with data: {ctx}" + + return query + + +def pre_process_request_with_ui_event(message: Message) -> Any: + """Pre-process the request. + + Args: + message: The agent request object. + + Returns: + The pre-processed request. + """ + + if message and message.parts: + logger.info( + "--- AGENT_EXECUTOR: Processing %s message parts ---", + len(message.parts), + ) + for i, part in enumerate(message.parts): + if isinstance(part.root, DataPart): + if "userAction" in part.root.data: + logger.info( + " Part %s: Found a2ui UI ClientEvent payload: %s", + i, + json.dumps(part.root.data["userAction"], indent=4), + ) + ui_event_part = part.root.data["userAction"] + message.parts[i] = Part( + root=TextPart( + text=transfer_ui_event_to_query(ui_event_part), + ), + ) + return message + + +def _find_json_end(json_string: str) -> int: + """Find the end position of a JSON array or object. + + Finds the end by matching brackets/braces. + + Args: + json_string: The JSON string to search. + + Returns: + The end position (index + 1) of the JSON structure. + """ + if json_string.startswith("["): + # Find matching closing bracket + bracket_count = 0 + for i, char in enumerate(json_string): + if char == "[": + bracket_count += 1 + elif char == "]": + bracket_count -= 1 + if bracket_count == 0: + return i + 1 + elif json_string.startswith("{"): + # Find matching closing brace + brace_count = 0 + for i, char in enumerate(json_string): + if char == "{": + brace_count += 1 + elif char == "}": + brace_count -= 1 + if brace_count == 0: + return i + 1 + return len(json_string) + + +def extract_ui_json_from_text(content_str: str) -> tuple[str, None]: + """Extract the UI JSON from the text. + + Args: + text: The text to extract the UI JSON from. + + Returns: + The UI JSON. + """ + text_content, json_string = content_str.split("---a2ui_JSON---", 1) + json_data = None + if json_string.strip(): + try: + # Clean JSON string (remove markdown code blocks if present) + json_string_cleaned = ( + json_string.strip().lstrip("```json").rstrip("```").strip() + ) + + # Find the end of JSON array/object by matching brackets/braces + json_end = _find_json_end(json_string_cleaned) + json_string_final = json_string_cleaned[:json_end].strip() + json_data = json.loads(json_string_final) + except json.JSONDecodeError as e: + logger.error("Failed to parse UI JSON: %s", e) + # On error, keep the JSON as text content + return content_str, None + return text_content, json_data + + +def check_a2ui_json_in_message( + part: Part, + is_final: bool, +) -> tuple[bool, str | None]: + """Check if the message contains A2UI JSON. + + Args: + message: The message to check. + + Returns: + A tuple containing a boolean indicating if A2UI JSON is found and + the A2UI JSON string if found. + """ + # for the case when ReActAgent max iters is reached, the message will be + # the last complete message, with text message. + if ( + isinstance(part.root, TextPart) + and "---a2ui_JSON---" in part.root.text + and is_final + ): + logger.info( + "--- Found A2UI JSON in the message: %s ---", + part.root.text, + ) + return True, part.root.text + + # for the case when ReActAgent max iters is not reached, if it contains + # tool use block with name "generate_response" and type "tool_use", and + # the response_with_a2ui contains "---a2ui_JSON---", then return True, + # response_with_a2ui. + if ( + isinstance(part.root, DataPart) + and part.root.data.get("name") == "generate_response" + and part.root.data.get("type") == "tool_use" + and not is_final + ): + input_data = part.root.data.get("input") + if input_data and isinstance(input_data, dict): + response_with_a2ui = input_data.get("response_with_a2ui") + if response_with_a2ui and "---a2ui_JSON---" in response_with_a2ui: + return True, response_with_a2ui + return False, None + + +def post_process_a2a_message_for_ui( + message: Message, + is_final: bool, +) -> Message: + """Post-process the transferred A2A message. + + Args: + message: The transferred A2A message. + + Returns: + The post-processed A2A message. + """ + new_parts = [] + for part in message.parts: + has_a2ui_json, a2ui_json_string = check_a2ui_json_in_message( + part, + is_final=is_final, + ) + logger.info( + "message: %s --- has_a2ui_json: %s ---", + message, + has_a2ui_json, + ) + if has_a2ui_json and a2ui_json_string is not None: + text_content, json_data = extract_ui_json_from_text( + a2ui_json_string, + ) + if json_data: + # Replace the part with a TextPart and multiple DataParts + # with the same metadata for a2ui + try: + new_parts.append( + Part( + root=TextPart( + text=text_content, + ), + ), + ) + + for item in json_data: + new_parts.append( + Part( + root=DataPart( + data=item, + metadata={MIME_TYPE_KEY: A2UI_MIME_TYPE}, + ), + ), + ) + + except Exception as e: + logger.error( + "Error processing a2ui JSON parts: %s", + e, + exc_info=True, + ) + raise + else: + # Keep the original part if it doesn't contain the marker + if isinstance(part.root, DataPart): + if ( + part.root.data.get("name") == "generate_response" + and part.root.data.get("type") == "tool_result" + ): + continue + new_parts.append(part) + + message.parts = new_parts + return message diff --git a/examples/agent/a2ui_agent/samples/agent/general_agent/agent_card.py b/examples/agent/a2ui_agent/samples/agent/general_agent/agent_card.py new file mode 100644 index 0000000000..7e4bdf44ce --- /dev/null +++ b/examples/agent/a2ui_agent/samples/agent/general_agent/agent_card.py @@ -0,0 +1,39 @@ +# -*- coding: utf-8 -*- +"""The agent card definition for the A2A agent.""" +from a2a.types import AgentCard, AgentCapabilities, AgentSkill +from a2ui.a2ui_extension import get_a2ui_agent_extension + +agent_card = AgentCard( + name="Friday", + description="A simple ReAct agent that handles input queries", + url="http://localhost:10002", + version="1.0.0", + capabilities=AgentCapabilities( + push_notifications=False, + state_transition_history=True, + streaming=True, + extensions=[get_a2ui_agent_extension()], + ), + default_input_modes=["text/plain"], + default_output_modes=["text/plain"], + skills=[ + AgentSkill( + name="execute_python_code", + id="execute_python_code", + description="Execute Python code snippets.", + tags=["code_execution"], + ), + AgentSkill( + name="execute_shell_command", + id="execute_shell_command", + description="Execute shell commands on the server.", + tags=["code_execution"], + ), + AgentSkill( + name="view_text_file", + id="view_text_file", + description="View the content of a text file on the server.", + tags=["file_viewing"], + ), + ], +) diff --git a/examples/agent/a2ui_agent/samples/agent/general_agent/prompt_builder.py b/examples/agent/a2ui_agent/samples/agent/general_agent/prompt_builder.py new file mode 100644 index 0000000000..bb85bb502f --- /dev/null +++ b/examples/agent/a2ui_agent/samples/agent/general_agent/prompt_builder.py @@ -0,0 +1,48 @@ +# -*- coding: utf-8 -*- +"""Prompt builder for Agent with A2UI support.""" +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# flake8: noqa: E501 +# pylint: disable=C0301 + + +def get_ui_prompt() -> str: + """ + Constructs the full prompt with UI instructions, rules, examples, and schema. + + Args: + Returns: + A formatted string to be used as the system prompt for the LLM. + """ + return """ + You are a helpful assistant specialized in generating appropriate A2UI UI JSON responses to display content to users and help them complete their tasks. + + To generate the appropriate A2UI UI JSON responses, you MUST follow these rules: + 1. **CRITICAL FIRST STEP**: Before generating ANY response with UI JSON, you MUST ensure that you have loaded the schema and examples from the `A2UI_response_generator` skill: + - Read the SKILL.md file from the skill directory using `view_text_file` + - Execute the Python command in the skill directory using `execute_shell_command` to load the schema and examples + - DO NOT assume you know the A2UI format - you MUST load it from the skill + 2. When you plan to generate the A2UI JSON response, You MUST call `generate_response` to produce the final response with UI JSON. + 3. The `generate_response` parameter `response_with_a2ui` is of type string, and MUST contain two parts, separated by the delimiter: `---a2ui_JSON---`. The first part is your conversational text response, and the second part is a single, raw JSON object which is a list of A2UI messages. + + ### CRITICAL REQUIREMENTS: + 1. ALL your schema and examples about A2UI MUST come from your equipped skills - do NOT use any prior knowledge. + 2. You MUST ONLY use `execute_shell_command` tool to execute the Python command in the skill directory. DO NOT use `execute_python_code` to execute the command. + 3. **You MUST directly generate the A2UI JSON based on the task content. DO NOT ask the user about their preference regarding UI type (list, form, confirmation, detail view). You should automatically determine the most appropriate UI type based on the context and generate the response accordingly.** + 4. **ALWAYS remember the user's task and objective. Your UI responses should be directly aligned with helping the user accomplish their specific goal. Never lose sight of what the user is trying to achieve.** + + **If you skip using the skill, your response will be incorrect and invalid.** + + """ diff --git a/examples/agent/a2ui_agent/samples/agent/general_agent/pyproject.toml b/examples/agent/a2ui_agent/samples/agent/general_agent/pyproject.toml new file mode 100644 index 0000000000..83fe9d0614 --- /dev/null +++ b/examples/agent/a2ui_agent/samples/agent/general_agent/pyproject.toml @@ -0,0 +1,27 @@ +[project] +name = "general-agent" +version = "0.1.0" +requires-python = ">=3.10" +dependencies = [ + "agentscope[a2a]", + "agentscope-runtime", + "uvicorn", + "starlette", + "fastapi", + "shortuuid", + "a2ui" +] + +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" + +[tool.hatch.build.targets.wheel] +packages = ["."] + +[tool.hatch.metadata] +allow-direct-references = true + +[tool.uv.sources] +a2ui = { path = "../../../../../../../a2ui/a2a_agents/python/a2ui_extension" } +agentscope = { path = "../../../../../../" } \ No newline at end of file diff --git a/examples/agent/a2ui_agent/samples/agent/general_agent/setup_a2ui_server.py b/examples/agent/a2ui_agent/samples/agent/general_agent/setup_a2ui_server.py new file mode 100644 index 0000000000..aad7e02c72 --- /dev/null +++ b/examples/agent/a2ui_agent/samples/agent/general_agent/setup_a2ui_server.py @@ -0,0 +1,316 @@ +# -*- coding: utf-8 -*- +"""Set up an A2A server with a ReAct agent to handle the input query""" +import os +import uuid +import copy +from typing import AsyncGenerator, Any + +from a2a.server.apps import A2AStarletteApplication +from a2a.server.events import Event +from a2a.types import ( + Task, + TaskStatus, + TaskState, + Message, + MessageSendParams, + TaskStatusUpdateEvent, +) +from starlette.middleware.cors import CORSMiddleware + +from agent_card import agent_card +from prompt_builder import get_ui_prompt +from a2ui_utils import ( + pre_process_request_with_ui_event, + post_process_a2a_message_for_ui, + A2UIResponse, +) +from agentscope._logging import logger +from agentscope.agent import ReActAgent +from agentscope.formatter import DashScopeChatFormatter, A2AChatFormatter +from agentscope.model import DashScopeChatModel +from agentscope.pipeline import stream_printing_messages +from agentscope.session import JSONSession +from agentscope.tool import ( + Toolkit, + view_text_file, + execute_python_code, + execute_shell_command, +) +from agentscope.message import Msg + + +def get_final_structured_output(message: Msg) -> None | str: + """Get the final structured output from the message.""" + if isinstance(message.content, list): + for block in message.content: + if ( + isinstance(block, dict) + and block.get("type") == "tool_use" + and block.get("name") == "generate_response" + ): + input_data = block.get("input") + if input_data is not None and isinstance(input_data, dict): + return input_data.get("response_with_a2ui") + return None + + +class SimpleStreamHandler: + """A simple request handler that handles the input query by an + ReAct agent.""" + + async def _prepare_final_message( + self, + formatter: A2AChatFormatter, + final_msg_text: str | None, + last_complete_msg: Msg | None, + ) -> Message: + """Prepare the final message for response. + + Args: + formatter: The A2AChatFormatter instance. + final_msg_text: The structured output text if available. + last_complete_msg: The last complete message if available. + + Returns: + The prepared final message. + """ + logger.info( + "--- Processing final response, final_msg_text: %s ---", + final_msg_text is not None, + ) + + if final_msg_text is not None: + logger.info("--- Using structured output for final message ---") + final_a2a_message = await formatter.format( + [Msg(name="Friday", content=final_msg_text, role="assistant")], + ) + else: + logger.info( + "--- Using last complete message for final message ---", + ) + final_a2a_message = await formatter.format( + [copy.deepcopy(last_complete_msg)], + ) + + logger.info( + "--- Post-processing message for UI: %s ---", + final_a2a_message, + ) + final_a2a_message = post_process_a2a_message_for_ui( + final_a2a_message, + is_final=True, + ) + return final_a2a_message + + async def on_message_send( + self, # pylint: disable=unused-argument + params: MessageSendParams, + *args: Any, + **kwargs: Any, + ) -> Task: + """Handles non-streaming message_send requests by collecting + events from the stream and returning the final Task. + + Args: + params (`MessageSendParams`): + The parameters for sending the message. + + Returns: + The final Task object. + """ + logger.info("--- params: %s ---", params) + logger.info("args: %s ---", args) + logger.info("kwargs: %s ---", kwargs) + # Collect all events from the stream + final_event = None + task_id = params.message.task_id or uuid.uuid4().hex + context_id = params.message.context_id or "default-context" + + async for event in self.on_message_send_stream( + params, + *args, + **kwargs, + ): + if event.final: + final_event = event + break + + # Ensure we always return a valid Task + if final_event is None: + # If no final event was found, create one with completed state + logger.warning( + "No final event found in stream, " + "creating default completed event", + ) + final_event = TaskStatusUpdateEvent( + task_id=task_id, + context_id=context_id, + status=TaskStatus(state=TaskState.failed), + final=True, + ) + + # Convert TaskStatusUpdateEvent to Task + # A2A protocol expects on_message_send to return a Task, + # not TaskStatusUpdateEvent + return Task( + id=final_event.task_id, + context_id=final_event.context_id, + status=final_event.status, + artifacts=[], + ) + + async def on_message_send_stream( + self, # pylint: disable=unused-argument + params: MessageSendParams, + *args: Any, + **kwargs: Any, + ) -> AsyncGenerator[Event, None]: + """Handles the message_send method by the agent + + Args: + params (`MessageSendParams`): + The parameters for sending the message. + + Returns: + `AsyncGenerator[Event, None]`: + An asynchronous generator that yields task status update + events. + """ + + task_id = params.message.task_id or uuid.uuid4().hex + context_id = params.message.context_id or "default-context" + # ============ Agent Logic ============ + toolkit = Toolkit() + toolkit.register_tool_function(execute_python_code) + toolkit.register_tool_function(execute_shell_command) + toolkit.register_tool_function(view_text_file) + # Get the skill path relative to this file + # From restaurant_finder/ to restaurant_finder/skills/ + # A2UI_response_generator + skill_path = os.path.abspath( + os.path.join( + os.path.dirname(__file__), + "skills", + "A2UI_response_generator", + ), + ) + toolkit.register_agent_skill(skill_path) + + # Create the agent instance + agent = ReActAgent( + name="Friday", + sys_prompt=get_ui_prompt(), + model=DashScopeChatModel( + model_name="qwen-max", + api_key=os.getenv("DASHSCOPE_API_KEY"), + ), + formatter=DashScopeChatFormatter(), + toolkit=toolkit, + max_iters=10, + ) + logger.info("Agent system prompt: %s", agent.sys_prompt) + + session = JSONSession(save_dir="./sessions") + session_id = params.message.task_id or "test-a2ui-agent" + await session.load_session_state( + session_id=session_id, + agent=agent, + ) + + # pre-process the A2A message with UI event, + # and then convert to AgentScope Msg objects + formatter = A2AChatFormatter() + as_msg = await formatter.format_a2a_message( + name="Friday", + message=pre_process_request_with_ui_event( + params.message, + ), + ) + + yield TaskStatusUpdateEvent( + task_id=task_id, + context_id=context_id, + status=TaskStatus(state=TaskState.working), + final=False, + ) + + # Collect all messages from the stream + # The 'last' flag indicates the last chunk of a streaming message, + # not the last message from the agent + final_msg_text = None + last_complete_msg = None # Track the last complete message + message_count = 0 + try: + async for msg, last in stream_printing_messages( + agents=[agent], + coroutine_task=agent(as_msg, structured_model=A2UIResponse), + ): + message_count += 1 + if last: + # Print message content preview (first 100 characters) + log_text = f"----{msg}" + logger.debug(log_text[0 : min(len(log_text), 500)]) + # Track the last complete message + # (only keep reference, no expensive ops) + last_complete_msg = copy.deepcopy(msg) + if isinstance(msg.content, list): + structured_output = get_final_structured_output(msg) + if structured_output: + final_msg_text = structured_output + break + except Exception as e: + logger.error( + "--- Error in message stream: %s ---", + e, + exc_info=True, + ) + raise + finally: + logger.info( + "--- Message stream collection completed. " + "Total messages: %s, " + "Last message: %s ---", + message_count, + last_complete_msg, + ) + + # Save session state (move before final message processing + # to avoid blocking yield) + await session.save_session_state( + session_id=session_id, + agent=agent, + ) + + final_a2a_message = await self._prepare_final_message( + formatter, + final_msg_text, + last_complete_msg, + ) + + logger.info("--- Yielding final TaskStatusUpdateEvent ---") + yield TaskStatusUpdateEvent( + task_id=task_id, + context_id=context_id, + status=TaskStatus( + state=TaskState.input_required, + message=final_a2a_message, + ), + final=True, + ) + + +handler = SimpleStreamHandler() +app_instance = A2AStarletteApplication( + agent_card, + handler, +) +app = app_instance.build() + +# Add CORS middleware to handle OPTIONS requests +app.add_middleware( + CORSMiddleware, + allow_origins=["*"], # Allow all origins for development + allow_credentials=False, # Cannot use "*" with credentials=True + allow_methods=["*"], # Allow all HTTP methods including OPTIONS + allow_headers=["*"], # Allow all headers +) diff --git a/examples/agent/a2ui_agent/samples/agent/general_agent/skills/A2UI_response_generator/SKILL.md b/examples/agent/a2ui_agent/samples/agent/general_agent/skills/A2UI_response_generator/SKILL.md new file mode 100644 index 0000000000..dd1137b1c3 --- /dev/null +++ b/examples/agent/a2ui_agent/samples/agent/general_agent/skills/A2UI_response_generator/SKILL.md @@ -0,0 +1,147 @@ +--- +name: A2UI_response_generator +description: A skill that can retrieve A2UI UI JSON schematics and UI templates that best show the response. This skill is essential and must be used before generating A2UI (Agent to UI) JSON responses. +--- + +# A2UI response generation Skill + +## Overview + +This skill is **essential and must be used before generating A2UI (Agent to UI) JSON responses**. It enables agents to retrieve A2UI UI JSON schematics and UI templates that best show the response, allowing for the generation of rich, interactive UI responses using the A2UI protocol. + +Instead of loading the entire A2UI schema and all examples at once, this skill allows agents to **retrieve** only the relevant UI templates and schematics based on the response content. The A2UI protocol defines a JSON-based format for dynamically constructing and updating user interfaces. By breaking down the examples into modular templates, agents can: + +1. Retrieve the appropriate A2UI UI JSON schematics for validation and structure reference +2. Select UI templates that best match and display the response content +3. Reduce prompt token usage by loading only necessary templates +4. Easily extend with new UI templates for different domains + +### File Structure + +``` +A2UI_response_generator/ +├── SKILL.md # This file - main skill documentation +├── view_a2ui_schema.py # Tool to view the complete A2UI schema (schema included in file) +├── view_a2ui_examples.py # Tool to view UI template examples (templates included in file) +├── __init__.py # Package initialization +├── schema/ # A2UI schema definitions +│ ├── __init__.py +│ └── base_schema.py # Base A2UI schema +└── UI_templete_examples/ # UI template examples + ├── __init__.py + ├── booking_form.py # Booking form template + ├── contact_form.py # Contact form template + ├── email_compose_form.py # Email compose form template + ├── error_message.py # Error message template + ├── info_message.py # Info message template + ├── item_detail_card_with_image.py # Item detail card with image template + ├── profile_view.py # Profile view template + ├── search_filter_form.py # Search filter form template + ├── simple_column_list_without_image.py # Simple list template + ├── single_column_list.py # Single column list template + ├── success_confirmation_with_image.py # Success confirmation template + └── two_column_list.py # Two column list template +``` + +## Quick Start + +When it is required to generate UI JSON, follow these steps: + +Important: Please use the `execute_shell_command` tool to execute Python command. + +### Step 1: Load the A2UI Schema + +Run the following script to load the complete A2UI schema. + +Currently available `schema_category` is `BASE_SCHEMA`. + +**Use the `execute_shell_command` tool to run (make sure you are in the skill directory):** +```bash +python -m view_a2ui_schema --schema_category BASE_SCHEMA +``` + +**Usage**: `python -m view_a2ui_schema --schema_category [schema_category]` - Loads the A2UI schema definition for validating A2UI JSON response structure. Currently only `BASE_SCHEMA` is available. + +About detailed usage, please refer to the `./view_a2ui_schema.py` script (located in the same folder as this SKILL.md file). + +### Step 2: Select UI Template Examples + +Select appropriate UI template examples based on your response content. + +**IMPORTANT**: You MUST use the **exact template names** listed in the "Available UI template examples" table below. Do NOT use generic category names like 'list', 'form', 'confirmation', or 'detail'. You MUST use the specific template name (e.g., `SINGLE_COLUMN_LIST_WITH_IMAGE`, `BOOKING_FORM_WITH_IMAGE`, etc.). + +**Use the `execute_shell_command` tool to run (make sure you are in the skill directory):** +```bash +python -m view_a2ui_examples --template_name SINGLE_COLUMN_LIST_WITH_IMAGE +``` + +**Usage**: `python -m view_a2ui_examples --template_name [template_name]` - Loads a UI template example for reference when generating A2UI responses. Accepts a single template name from the available templates table below. + +**Available UI template examples** (when `schema_category` is `BASE_SCHEME`, you MUST use these exact names, case-sensitive): + +| Template Name | Use Case | Selection Guide | Image Support | +| --- | --- | --- | --- | +| `SINGLE_COLUMN_LIST_WITH_IMAGE` | Vertical list with detailed cards (for ≤5 items) | Use for **list display** with ≤5 items | ✅ With image | +| `TWO_COLUMN_LIST_WITH_IMAGE` | Grid layout with cards (for >5 items) | Use for **list display** with >5 items | ✅ With image | +| `SIMPLE_LIST` | Compact list without images | Use for **compact lists** without images | ❌ Without image | +| `SELECTION_CARD` | Multiple choice questions | Use for **multiple choice questions** | ❌ Without image | +| `MULTIPLE_SELECTION_CARDS` | Multiple selection cards in a list | Use for **multiple selection cards** displayed together | ❌ Without image | +| `BOOKING_FORM_WITH_IMAGE` | Reservation, booking, registration | Use for **booking/reservation forms** | ✅ With image | +| `SEARCH_FILTER_FORM_WITH_IMAGE` | Search forms with filters | Use for **search forms with filters** | ❌ Without image | +| `CONTACT_FORM_WITH_IMAGE` | Contact or feedback forms | Use for **contact/feedback forms** | ❌ Without image | +| `EMAIL_COMPOSE_FORM_WITH_IMAGE` | Email composition forms | Use for **email composition forms** | ❌ Without image | +| `SUCCESS_CONFIRMATION_WITH_IMAGE` | Success message after action | Use for **success confirmations** | ✅ With image | +| `ERROR_MESSAGE` | Error or warning display | Use for **error messages** | ❌ Without image | +| `INFO_MESSAGE` | Informational messages | Use for **info messages** | ❌ Without image | +| `ITEM_DETAIL_CARD` | Detailed view of single item | Use for **item detail views** | ✅ With image | +| `ITEM_DETAIL_CARD_WITH_IMAGE` | Detailed view of single item with image | Use for **item detail views** with images | ✅ With image | +| `PROFILE_VIEW` | User or entity profile display | Use for **profile views** | ✅ With image | + +**Remember**: Always use the exact template names from the table above. Never use generic terms like 'list' or 'form' - they are NOT valid template names. + +About detailed usage, please refer to the `./view_a2ui_examples.py` script (located in the same folder as this SKILL.md file). + +### Step 3: Generate the A2UI Response + +After loading the schema and examples, construct your A2UI response using the **generate_response** tool. The `response_with_a2ui` parameter must be a string containing two parts separated by the delimiter `---a2ui_JSON---`: + +First Part: **Conversational text response**: Your natural language reply to the user +Second Part. **A2UI JSON messages**: A raw JSON array of A2UI message objects that MUST validate against the A2UI schema + +**Format:** +``` +[Your conversational response here] + +---a2ui_JSON--- +[ + { "beginRendering": { ... } }, + { "surfaceUpdate": { ... } }, + { "dataModelUpdate": { ... } } +] +``` + +**Important**: The JSON portion must be valid JSON and conform to the A2UI schema loaded in Step 1. + + + +## Domain-Specific Extensions + +To add support for a new domain (e.g., flight booking, e-commerce), add new templates to `view_a2ui_examples.py`: + +1. Define a new template constant in `view_a2ui_examples.py` (e.g., `FLIGHT_BOOKING_FORM_EXAMPLE`) +2. Add the template to the `TEMPLATE_MAP` dictionary in `view_a2ui_examples.py` +3. Update this SKILL.md to include the new templates in the available templates list + + +## Troubleshooting + +If you encounter any issues running the scripts, make sure: +you use the tool `execute_shell_command` run the python command. + +1. You are in the correct skill directory (check the skill description for the actual path) +2. The script files (`view_a2ui_schema.py` and `view_a2ui_examples.py`) exist in the skill directory +3. You have the required Python dependencies installed + +For detailed usage of each script, please refer to: +- `./view_a2ui_schema.py` - View the A2UI schema +- `./view_a2ui_examples.py` - View A2UI template examples diff --git a/examples/agent/a2ui_agent/samples/agent/general_agent/skills/A2UI_response_generator/UI_templete_examples/__init__.py b/examples/agent/a2ui_agent/samples/agent/general_agent/skills/A2UI_response_generator/UI_templete_examples/__init__.py new file mode 100644 index 0000000000..9e2c3bf0bf --- /dev/null +++ b/examples/agent/a2ui_agent/samples/agent/general_agent/skills/A2UI_response_generator/UI_templete_examples/__init__.py @@ -0,0 +1,38 @@ +# -*- coding: utf-8 -*- +# flake8: noqa: E501 +"""A2UI template examples for generating UI responses.""" +from .booking_form import BOOKING_FORM_WITH_IMAGE +from .contact_form import CONTACT_FORM_EXAMPLE +from .email_compose_form import EMAIL_COMPOSE_FORM_EXAMPLE +from .error_message import ERROR_MESSAGE_EXAMPLE +from .info_message import INFO_MESSAGE_EXAMPLE +from .item_detail_card_with_image import ITEM_DETAIL_CARD_EXAMPLE_WITH_IMAGE +from .profile_view import PROFILE_VIEW_WITH_IMAGE_EXAMPLE +from .search_filter_form import SEARCH_FILTER_FORM_EXAMPLE +from .simple_column_list_without_image import SIMPLE_LIST_EXAMPLE +from .single_column_list import SINGLE_COLUMN_LIST_WITH_IMAGE_EXAMPLE +from .success_confirmation_with_image import ( + SUCCESS_CONFIRMATION_WITH_IMAGE_EXAMPLE, +) +from .two_column_list import TWO_COLUMN_LIST_WITH_IMAGE_EXAMPLE +from .selection_card import ( + SELECTION_CARD_EXAMPLE, + MULTIPLE_SELECTION_CARDS_EXAMPLE, +) + +__all__ = [ + "BOOKING_FORM_WITH_IMAGE", + "CONTACT_FORM_EXAMPLE", + "EMAIL_COMPOSE_FORM_EXAMPLE", + "ERROR_MESSAGE_EXAMPLE", + "INFO_MESSAGE_EXAMPLE", + "ITEM_DETAIL_CARD_EXAMPLE_WITH_IMAGE", + "PROFILE_VIEW_WITH_IMAGE_EXAMPLE", + "SEARCH_FILTER_FORM_EXAMPLE", + "SIMPLE_LIST_EXAMPLE", + "SINGLE_COLUMN_LIST_WITH_IMAGE_EXAMPLE", + "SUCCESS_CONFIRMATION_WITH_IMAGE_EXAMPLE", + "TWO_COLUMN_LIST_WITH_IMAGE_EXAMPLE", + "SELECTION_CARD_EXAMPLE", + "MULTIPLE_SELECTION_CARDS_EXAMPLE", +] diff --git a/examples/agent/a2ui_agent/samples/agent/general_agent/skills/A2UI_response_generator/UI_templete_examples/booking_form.py b/examples/agent/a2ui_agent/samples/agent/general_agent/skills/A2UI_response_generator/UI_templete_examples/booking_form.py new file mode 100644 index 0000000000..9637e6b833 --- /dev/null +++ b/examples/agent/a2ui_agent/samples/agent/general_agent/skills/A2UI_response_generator/UI_templete_examples/booking_form.py @@ -0,0 +1,40 @@ +# -*- coding: utf-8 -*- +# flake8: noqa: E501 +"""A2UI template example for booking form.""" + +BOOKING_FORM_WITH_IMAGE = """ +---BEGIN BOOKING_FORM_WITH_IMAGE_EXAMPLE--- +Use this template for booking, reservation, or registration forms. + +[ + {{ "beginRendering": {{ "surfaceId": "booking-form", "root": "form-column", "styles": {{ "primaryColor": "#FF5722", "font": "Roboto" }} }} }}, + {{ "surfaceUpdate": {{ + "surfaceId": "booking-form", + "components": [ + {{ "id": "form-column", "component": {{ "Column": {{ "children": {{ "explicitList": ["form-title", "form-image", "form-address", "party-size-field", "datetime-field", "notes-field", "submit-button"] }} }} }} }}, + {{ "id": "form-title", "component": {{ "Text": {{ "usageHint": "h2", "text": {{ "path": "title" }} }} }} }}, + {{ "id": "form-image", "component": {{ "Image": {{ "url": {{ "path": "imageUrl" }}, "usageHint": "mediumFeature" }} }} }}, + {{ "id": "form-address", "component": {{ "Text": {{ "text": {{ "path": "address" }} }} }} }}, + {{ "id": "party-size-field", "component": {{ "TextField": {{ "label": {{ "literalString": "Party Size" }}, "text": {{ "path": "partySize" }}, "type": "number" }} }} }}, + {{ "id": "datetime-field", "component": {{ "DateTimeInput": {{ "label": {{ "literalString": "Date & Time" }}, "value": {{ "path": "reservationTime" }}, "enableDate": true, "enableTime": true }} }} }}, + {{ "id": "notes-field", "component": {{ "TextField": {{ "label": {{ "literalString": "Special Requests" }}, "text": {{ "path": "notes" }}, "multiline": true }} }} }}, + {{ "id": "submit-button", "component": {{ "Button": {{ "child": "submit-text", "primary": true, "action": {{ "name": "submit_booking", "context": [ {{ "key": "itemName", "value": {{ "path": "itemName" }} }}, {{ "key": "partySize", "value": {{ "path": "partySize" }} }}, {{ "key": "reservationTime", "value": {{ "path": "reservationTime" }} }}, {{ "key": "notes", "value": {{ "path": "notes" }} }} ] }} }} }} }}, + {{ "id": "submit-text", "component": {{ "Text": {{ "text": {{ "literalString": "Submit Reservation" }} }} }} }} + ] + }} }}, + {{ "dataModelUpdate": {{ + "surfaceId": "booking-form", + "path": "/", + "contents": [ + {{ "key": "title", "valueString": "Book [Item Name]" }}, + {{ "key": "address", "valueString": "[Address]" }}, + {{ "key": "itemName", "valueString": "[Item Name]" }}, + {{ "key": "imageUrl", "valueString": "[Image URL]" }}, + {{ "key": "partySize", "valueString": "2" }}, + {{ "key": "reservationTime", "valueString": "" }}, + {{ "key": "notes", "valueString": "" }} + ] + }} }} +] +---END BOOKING_FORM_WITH_IMAGE_EXAMPLE--- +""" diff --git a/examples/agent/a2ui_agent/samples/agent/general_agent/skills/A2UI_response_generator/UI_templete_examples/contact_form.py b/examples/agent/a2ui_agent/samples/agent/general_agent/skills/A2UI_response_generator/UI_templete_examples/contact_form.py new file mode 100644 index 0000000000..848aad1d83 --- /dev/null +++ b/examples/agent/a2ui_agent/samples/agent/general_agent/skills/A2UI_response_generator/UI_templete_examples/contact_form.py @@ -0,0 +1,36 @@ +# -*- coding: utf-8 -*- +# flake8: noqa: E501 +"""A2UI template example for contact form.""" + +CONTACT_FORM_EXAMPLE = """ +---BEGIN CONTACT_FORM_EXAMPLE--- +Use this template for contact or feedback forms. + +[ + {{ "beginRendering": {{ "surfaceId": "contact-form", "root": "contact-column", "styles": {{ "primaryColor": "#4CAF50", "font": "Roboto" }} }} }}, + {{ "surfaceUpdate": {{ + "surfaceId": "contact-form", + "components": [ + {{ "id": "contact-column", "component": {{ "Column": {{ "children": {{ "explicitList": ["contact-title", "name-field", "email-field", "subject-field", "message-field", "send-button"] }} }} }} }}, + {{ "id": "contact-title", "component": {{ "Text": {{ "usageHint": "h2", "text": {{ "literalString": "Contact Us" }} }} }} }}, + {{ "id": "name-field", "component": {{ "TextField": {{ "label": {{ "literalString": "Your Name" }}, "text": {{ "path": "name" }} }} }} }}, + {{ "id": "email-field", "component": {{ "TextField": {{ "label": {{ "literalString": "Email Address" }}, "text": {{ "path": "email" }}, "type": "email" }} }} }}, + {{ "id": "subject-field", "component": {{ "TextField": {{ "label": {{ "literalString": "Subject" }}, "text": {{ "path": "subject" }} }} }} }}, + {{ "id": "message-field", "component": {{ "TextField": {{ "label": {{ "literalString": "Message" }}, "text": {{ "path": "message" }}, "multiline": true }} }} }}, + {{ "id": "send-button", "component": {{ "Button": {{ "child": "send-text", "primary": true, "action": {{ "name": "send_message", "context": [ {{ "key": "name", "value": {{ "path": "name" }} }}, {{ "key": "email", "value": {{ "path": "email" }} }}, {{ "key": "subject", "value": {{ "path": "subject" }} }}, {{ "key": "message", "value": {{ "path": "message" }} }} ] }} }} }} }}, + {{ "id": "send-text", "component": {{ "Text": {{ "text": {{ "literalString": "Send Message" }} }} }} }} + ] + }} }}, + {{ "dataModelUpdate": {{ + "surfaceId": "contact-form", + "path": "/", + "contents": [ + {{ "key": "name", "valueString": "" }}, + {{ "key": "email", "valueString": "" }}, + {{ "key": "subject", "valueString": "" }}, + {{ "key": "message", "valueString": "" }} + ] + }} }} +] +---END CONTACT_FORM_EXAMPLE--- +""" diff --git a/examples/agent/a2ui_agent/samples/agent/general_agent/skills/A2UI_response_generator/UI_templete_examples/email_compose_form.py b/examples/agent/a2ui_agent/samples/agent/general_agent/skills/A2UI_response_generator/UI_templete_examples/email_compose_form.py new file mode 100644 index 0000000000..76047eb9c2 --- /dev/null +++ b/examples/agent/a2ui_agent/samples/agent/general_agent/skills/A2UI_response_generator/UI_templete_examples/email_compose_form.py @@ -0,0 +1,44 @@ +# -*- coding: utf-8 -*- +# flake8: noqa: E501 +"""A2UI template example for email compose form.""" + +EMAIL_COMPOSE_FORM_EXAMPLE = """ +---BEGIN EMAIL_COMPOSE_FORM_EXAMPLE--- +Use this template for composing and sending emails. + +[ + {{ "beginRendering": {{ "surfaceId": "email-compose", "root": "email-column", "styles": {{ "primaryColor": "#1976D2", "font": "Roboto" }} }} }}, + {{ "surfaceUpdate": {{ + "surfaceId": "email-compose", + "components": [ + {{ "id": "email-column", "component": {{ "Column": {{ "children": {{ "explicitList": ["email-title", "to-field", "cc-field", "bcc-field", "subject-field", "body-field", "attach-row", "send-button-row"] }} }} }} }}, + {{ "id": "email-title", "component": {{ "Text": {{ "usageHint": "h2", "text": {{ "literalString": "Compose Email" }} }} }} }}, + {{ "id": "to-field", "component": {{ "TextField": {{ "label": {{ "literalString": "To" }}, "text": {{ "path": "to" }}, "type": "email" }} }} }}, + {{ "id": "cc-field", "component": {{ "TextField": {{ "label": {{ "literalString": "Cc" }}, "text": {{ "path": "cc" }}, "type": "email" }} }} }}, + {{ "id": "bcc-field", "component": {{ "TextField": {{ "label": {{ "literalString": "Bcc" }}, "text": {{ "path": "bcc" }}, "type": "email" }} }} }}, + {{ "id": "subject-field", "component": {{ "TextField": {{ "label": {{ "literalString": "Subject" }}, "text": {{ "path": "subject" }} }} }} }}, + {{ "id": "body-field", "component": {{ "TextField": {{ "label": {{ "literalString": "Message" }}, "text": {{ "path": "body" }}, "multiline": true }} }} }}, + {{ "id": "attach-row", "component": {{ "Row": {{ "children": {{ "explicitList": ["attach-icon", "attach-text"] }} }} }} }}, + {{ "id": "attach-icon", "component": {{ "Icon": {{ "name": {{ "literalString": "attachFile" }} }} }} }}, + {{ "id": "attach-text", "weight": 1, "component": {{ "Text": {{ "text": {{ "literalString": "Attach File" }}, "usageHint": "caption" }} }} }}, + {{ "id": "send-button-row", "component": {{ "Row": {{ "children": {{ "explicitList": ["send-button", "draft-button"] }}, "distribution": "end" }} }} }}, + {{ "id": "send-button", "component": {{ "Button": {{ "child": "send-text", "primary": true, "action": {{ "name": "send_email", "context": [ {{ "key": "to", "value": {{ "path": "to" }} }}, {{ "key": "cc", "value": {{ "path": "cc" }} }}, {{ "key": "bcc", "value": {{ "path": "bcc" }} }}, {{ "key": "subject", "value": {{ "path": "subject" }} }}, {{ "key": "body", "value": {{ "path": "body" }} }} ] }} }} }}, + {{ "id": "send-text", "component": {{ "Text": {{ "text": {{ "literalString": "Send" }} }} }} }}, + {{ "id": "draft-button", "component": {{ "Button": {{ "child": "draft-text", "action": {{ "name": "save_draft", "context": [ {{ "key": "to", "value": {{ "path": "to" }} }}, {{ "key": "cc", "value": {{ "path": "cc" }} }}, {{ "key": "bcc", "value": {{ "path": "bcc" }} }}, {{ "key": "subject", "value": {{ "path": "subject" }} }}, {{ "key": "body", "value": {{ "path": "body" }} }} ] }} }} }}, + {{ "id": "draft-text", "component": {{ "Text": {{ "text": {{ "literalString": "Save Draft" }} }} }} }} + ] + }} }}, + {{ "dataModelUpdate": {{ + "surfaceId": "email-compose", + "path": "/", + "contents": [ + {{ "key": "to", "valueString": "" }}, + {{ "key": "cc", "valueString": "" }}, + {{ "key": "bcc", "valueString": "" }}, + {{ "key": "subject", "valueString": "" }}, + {{ "key": "body", "valueString": "" }} + ] + }} }} +] +---END EMAIL_COMPOSE_FORM_EXAMPLE--- +""" diff --git a/examples/agent/a2ui_agent/samples/agent/general_agent/skills/A2UI_response_generator/UI_templete_examples/error_message.py b/examples/agent/a2ui_agent/samples/agent/general_agent/skills/A2UI_response_generator/UI_templete_examples/error_message.py new file mode 100644 index 0000000000..bf671a9003 --- /dev/null +++ b/examples/agent/a2ui_agent/samples/agent/general_agent/skills/A2UI_response_generator/UI_templete_examples/error_message.py @@ -0,0 +1,58 @@ +# -*- coding: utf-8 -*- +# flake8: noqa: E501 +"""A2UI template example for error message.""" + +ERROR_MESSAGE_EXAMPLE = """ +---BEGIN ERROR_MESSAGE_EXAMPLE--- +Use this template to display error or warning messages. + +[ + {{ "beginRendering": {{ + "surfaceId": "error-message", + "root": "error-card", + "styles": {{ "primaryColor": "#F44336", "font": "Roboto" }} + }} }}, + {{ "surfaceUpdate": {{ + "surfaceId": "error-message", + "components": [ + {{ "id": "error-card", "component": {{ + "Card": {{ "child": "error-column" }} + }} }}, + {{ "id": "error-column", "component": {{ "Column": {{ + "children": {{ "explicitList": [ + "error-icon", "error-title", "error-message", "retry-button" + ] }} + }} }} }}, + {{ "id": "error-icon", "component": {{ "Icon": {{ + "name": {{ "literalString": "error" }} + }} }} }}, + {{ "id": "error-title", "component": {{ + "Text": {{ "usageHint": "h3", "text": {{ "path": "title" }} }} + }} }}, + {{ "id": "error-message", "component": {{ + "Text": {{ "text": {{ "path": "message" }} }} + }} }}, + {{ "id": "retry-button", "component": {{ + "Button": {{ + "child": "retry-text", + "primary": true, + "action": {{ "name": "retry", "context": [] }} + }} + }} }}, + {{ "id": "retry-text", "component": {{ + "Text": {{ "text": {{ "literalString": "Try Again" }} }} + }} }} + ] + }} }}, + {{ "dataModelUpdate": {{ + "surfaceId": "error-message", + "path": "/", + "contents": [ + {{ "key": "title", "valueString": "Something went wrong" }}, + {{ "key": "message", + "valueString": "[Error description and suggested action]" }} + ] + }} }} +] +---END ERROR_MESSAGE_EXAMPLE--- +""" diff --git a/examples/agent/a2ui_agent/samples/agent/general_agent/skills/A2UI_response_generator/UI_templete_examples/info_message.py b/examples/agent/a2ui_agent/samples/agent/general_agent/skills/A2UI_response_generator/UI_templete_examples/info_message.py new file mode 100644 index 0000000000..6d96300f19 --- /dev/null +++ b/examples/agent/a2ui_agent/samples/agent/general_agent/skills/A2UI_response_generator/UI_templete_examples/info_message.py @@ -0,0 +1,57 @@ +# -*- coding: utf-8 -*- +# flake8: noqa: E501 +"""A2UI template example for info message.""" + +INFO_MESSAGE_EXAMPLE = """ +---BEGIN INFO_MESSAGE_EXAMPLE--- +Use this template to display informational messages. + +[ + {{ "beginRendering": {{ + "surfaceId": "info-message", + "root": "info-card", + "styles": {{ "primaryColor": "#2196F3", "font": "Roboto" }} + }} }}, + {{ "surfaceUpdate": {{ + "surfaceId": "info-message", + "components": [ + {{ "id": "info-card", "component": {{ "Card": {{ "child": "info-column" }} }} }}, + {{ "id": "info-column", "component": {{ + "Column": {{ + "children": {{ + "explicitList": [ + "info-icon", "info-title", "info-message", "dismiss-button" + ] + }} + }} + }} }}, + {{ "id": "info-icon", "component": {{ "Icon": {{ "name": {{ "literalString": "info" }} }} }} }}, + {{ "id": "info-title", "component": {{ + "Text": {{ "usageHint": "h3", "text": {{ "path": "title" }} }} + }} }}, + {{ "id": "info-message", "component": {{ + "Text": {{ "text": {{ "path": "message" }} }} + }} }}, + {{ "id": "dismiss-button", "component": {{ + "Button": {{ + "child": "dismiss-text", + "action": {{ "name": "dismiss", "context": [] }} + }} + }} }}, + {{ "id": "dismiss-text", "component": {{ + "Text": {{ "text": {{ "literalString": "Got it" }} }} + }} }} + ] + }} }}, + {{ "dataModelUpdate": {{ + "surfaceId": "info-message", + "path": "/", + "contents": [ + {{ "key": "title", "valueString": "[Info Title]" }}, + {{ "key": "message", + "valueString": "[Informational message]" }} + ] + }} }} +] +---END INFO_MESSAGE_EXAMPLE--- +""" diff --git a/examples/agent/a2ui_agent/samples/agent/general_agent/skills/A2UI_response_generator/UI_templete_examples/item_detail_card_with_image.py b/examples/agent/a2ui_agent/samples/agent/general_agent/skills/A2UI_response_generator/UI_templete_examples/item_detail_card_with_image.py new file mode 100644 index 0000000000..852b07e7f0 --- /dev/null +++ b/examples/agent/a2ui_agent/samples/agent/general_agent/skills/A2UI_response_generator/UI_templete_examples/item_detail_card_with_image.py @@ -0,0 +1,59 @@ +# -*- coding: utf-8 -*- +# flake8: noqa: E501 +"""A2UI template example for item detail card with image.""" + +# Detail examples +ITEM_DETAIL_CARD_EXAMPLE_WITH_IMAGE = """ +---BEGIN ITEM_DETAIL_CARD_EXAMPLE_WITH_IMAGE--- +Use this template to display detailed information about a single item. + +[ + {{ "beginRendering": {{ "surfaceId": "item-detail", "root": "detail-column", "styles": {{ "primaryColor": "#673AB7", "font": "Roboto" }} }} }}, + {{ "surfaceUpdate": {{ + "surfaceId": "item-detail", + "components": [ + {{ "id": "detail-column", "component": {{ "Column": {{ "children": {{ "explicitList": ["header-image", "detail-card"] }} }} }} }}, + {{ "id": "header-image", "component": {{ "Image": {{ "url": {{ "path": "imageUrl" }}, "usageHint": "header" }} }} }}, + {{ "id": "detail-card", "component": {{ "Card": {{ "child": "card-content" }} }} }}, + {{ "id": "card-content", "component": {{ "Column": {{ "children": {{ "explicitList": ["item-title", "item-subtitle", "divider1", "description-section", "divider2", "info-section", "action-row"] }} }} }} }}, + {{ "id": "item-title", "component": {{ "Text": {{ "usageHint": "h1", "text": {{ "path": "name" }} }} }} }}, + {{ "id": "item-subtitle", "component": {{ "Text": {{ "usageHint": "caption", "text": {{ "path": "subtitle" }} }} }} }}, + {{ "id": "divider1", "component": {{ "Divider": {{}} }} }}, + {{ "id": "description-section", "component": {{ "Column": {{ "children": {{ "explicitList": ["description-title", "description-text"] }} }} }} }}, + {{ "id": "description-title", "component": {{ "Text": {{ "usageHint": "h4", "text": {{ "literalString": "Description" }} }} }} }}, + {{ "id": "description-text", "component": {{ "Text": {{ "text": {{ "path": "description" }} }} }} }}, + {{ "id": "divider2", "component": {{ "Divider": {{}} }} }}, + {{ "id": "info-section", "component": {{ "Column": {{ "children": {{ "explicitList": ["info-row-1", "info-row-2", "info-row-3"] }} }} }} }}, + {{ "id": "info-row-1", "component": {{ "Row": {{ "children": {{ "explicitList": ["info-icon-1", "info-text-1"] }} }} }} }}, + {{ "id": "info-icon-1", "component": {{ "Icon": {{ "name": {{ "literalString": "locationOn" }} }} }} }}, + {{ "id": "info-text-1", "weight": 1, "component": {{ "Text": {{ "text": {{ "path": "location" }} }} }} }}, + {{ "id": "info-row-2", "component": {{ "Row": {{ "children": {{ "explicitList": ["info-icon-2", "info-text-2"] }} }} }} }}, + {{ "id": "info-icon-2", "component": {{ "Icon": {{ "name": {{ "literalString": "phone" }} }} }} }}, + {{ "id": "info-text-2", "weight": 1, "component": {{ "Text": {{ "text": {{ "path": "phone" }} }} }} }}, + {{ "id": "info-row-3", "component": {{ "Row": {{ "children": {{ "explicitList": ["info-icon-3", "info-text-3"] }} }} }} }}, + {{ "id": "info-icon-3", "component": {{ "Icon": {{ "name": {{ "literalString": "star" }} }} }} }}, + {{ "id": "info-text-3", "weight": 1, "component": {{ "Text": {{ "text": {{ "path": "rating" }} }} }} }}, + {{ "id": "action-row", "component": {{ "Row": {{ "children": {{ "explicitList": ["share-button", "primary-action-button"] }} }} }} }}, + {{ "id": "share-button", "weight": 1, "component": {{ "Button": {{ "child": "share-text", "action": {{ "name": "share", "context": [ {{ "key": "itemId", "value": {{ "path": "id" }} }} ] }} }} }} }}, + {{ "id": "share-text", "component": {{ "Text": {{ "text": {{ "literalString": "Share" }} }} }} }}, + {{ "id": "primary-action-button", "weight": 1, "component": {{ "Button": {{ "child": "action-text", "primary": true, "action": {{ "name": "select_item", "context": [ {{ "key": "itemId", "value": {{ "path": "id" }} }}, {{ "key": "itemName", "value": {{ "path": "name" }} }} ] }} }} }} }}, + {{ "id": "action-text", "component": {{ "Text": {{ "text": {{ "literalString": "Book Now" }} }} }} }} + ] + }} }}, + {{ "dataModelUpdate": {{ + "surfaceId": "item-detail", + "path": "/", + "contents": [ + {{ "key": "id", "valueString": "[Item ID]" }}, + {{ "key": "name", "valueString": "[Item Name]" }}, + {{ "key": "subtitle", "valueString": "[Category or Type]" }}, + {{ "key": "imageUrl", "valueString": "[Header Image URL]" }}, + {{ "key": "description", "valueString": "[Detailed description of the item]" }}, + {{ "key": "location", "valueString": "[Address or Location]" }}, + {{ "key": "phone", "valueString": "[Phone Number]" }}, + {{ "key": "rating", "valueString": "[Rating] stars" }} + ] + }} }} +] +---END ITEM_DETAIL_CARD_EXAMPLE_WITH_IMAGE--- +""" diff --git a/examples/agent/a2ui_agent/samples/agent/general_agent/skills/A2UI_response_generator/UI_templete_examples/profile_view.py b/examples/agent/a2ui_agent/samples/agent/general_agent/skills/A2UI_response_generator/UI_templete_examples/profile_view.py new file mode 100644 index 0000000000..e80aeaa026 --- /dev/null +++ b/examples/agent/a2ui_agent/samples/agent/general_agent/skills/A2UI_response_generator/UI_templete_examples/profile_view.py @@ -0,0 +1,66 @@ +# -*- coding: utf-8 -*- +# flake8: noqa: E501 +"""A2UI template example for profile view.""" + +PROFILE_VIEW_WITH_IMAGE_EXAMPLE = """ +---BEGIN PROFILE_VIEW_WITH_IMAGE_EXAMPLE--- +Use this template to display user or entity profile information. + +[ + {{ "beginRendering": {{ "surfaceId": "profile", "root": "profile-column", "styles": {{ "primaryColor": "#009688", "font": "Roboto" }} }} }}, + {{ "surfaceUpdate": {{ + "surfaceId": "profile", + "components": [ + {{ "id": "profile-column", "component": {{ "Column": {{ "children": {{ "explicitList": ["profile-header", "profile-card"] }} }} }} }}, + {{ "id": "profile-header", "component": {{ "Row": {{ "children": {{ "explicitList": ["avatar-image", "header-info"] }} }} }} }}, + {{ "id": "avatar-image", "component": {{ "Image": {{ "url": {{ "path": "avatarUrl" }}, "usageHint": "avatar" }} }} }}, + {{ "id": "header-info", "weight": 1, "component": {{ "Column": {{ "children": {{ "explicitList": ["profile-name", "profile-title"] }} }} }} }}, + {{ "id": "profile-name", "component": {{ "Text": {{ "usageHint": "h2", "text": {{ "path": "name" }} }} }} }}, + {{ "id": "profile-title", "component": {{ "Text": {{ "usageHint": "caption", "text": {{ "path": "title" }} }} }} }}, + {{ "id": "profile-card", "component": {{ "Card": {{ "child": "profile-details" }} }} }}, + {{ "id": "profile-details", "component": {{ "Column": {{ "children": {{ "explicitList": ["bio-section", "divider1", "contact-section", "divider2", "stats-section"] }} }} }} }}, + {{ "id": "bio-section", "component": {{ "Column": {{ "children": {{ "explicitList": ["bio-title", "bio-text"] }} }} }} }}, + {{ "id": "bio-title", "component": {{ "Text": {{ "usageHint": "h4", "text": {{ "literalString": "About" }} }} }} }}, + {{ "id": "bio-text", "component": {{ "Text": {{ "text": {{ "path": "bio" }} }} }} }}, + {{ "id": "divider1", "component": {{ "Divider": {{}} }} }}, + {{ "id": "contact-section", "component": {{ "Column": {{ "children": {{ "explicitList": ["email-row", "phone-row"] }} }} }} }}, + {{ "id": "email-row", "component": {{ "Row": {{ "children": {{ "explicitList": ["email-icon", "email-text"] }} }} }} }}, + {{ "id": "email-icon", "component": {{ "Icon": {{ "name": {{ "literalString": "mail" }} }} }} }}, + {{ "id": "email-text", "weight": 1, "component": {{ "Text": {{ "text": {{ "path": "email" }} }} }} }}, + {{ "id": "phone-row", "component": {{ "Row": {{ "children": {{ "explicitList": ["phone-icon", "phone-text"] }} }} }} }}, + {{ "id": "phone-icon", "component": {{ "Icon": {{ "name": {{ "literalString": "phone" }} }} }} }}, + {{ "id": "phone-text", "weight": 1, "component": {{ "Text": {{ "text": {{ "path": "phone" }} }} }} }}, + {{ "id": "divider2", "component": {{ "Divider": {{}} }} }}, + {{ "id": "stats-section", "component": {{ "Row": {{ "children": {{ "explicitList": ["stat-1", "stat-2", "stat-3"] }} }} }} }}, + {{ "id": "stat-1", "weight": 1, "component": {{ "Column": {{ "children": {{ "explicitList": ["stat-1-value", "stat-1-label"] }} }} }} }}, + {{ "id": "stat-1-value", "component": {{ "Text": {{ "usageHint": "h3", "text": {{ "path": "stat1Value" }} }} }} }}, + {{ "id": "stat-1-label", "component": {{ "Text": {{ "usageHint": "caption", "text": {{ "path": "stat1Label" }} }} }} }}, + {{ "id": "stat-2", "weight": 1, "component": {{ "Column": {{ "children": {{ "explicitList": ["stat-2-value", "stat-2-label"] }} }} }} }}, + {{ "id": "stat-2-value", "component": {{ "Text": {{ "usageHint": "h3", "text": {{ "path": "stat2Value" }} }} }} }}, + {{ "id": "stat-2-label", "component": {{ "Text": {{ "usageHint": "caption", "text": {{ "path": "stat2Label" }} }} }} }}, + {{ "id": "stat-3", "weight": 1, "component": {{ "Column": {{ "children": {{ "explicitList": ["stat-3-value", "stat-3-label"] }} }} }} }}, + {{ "id": "stat-3-value", "component": {{ "Text": {{ "usageHint": "h3", "text": {{ "path": "stat3Value" }} }} }} }}, + {{ "id": "stat-3-label", "component": {{ "Text": {{ "usageHint": "caption", "text": {{ "path": "stat3Label" }} }} }} }} + ] + }} }}, + {{ "dataModelUpdate": {{ + "surfaceId": "profile", + "path": "/", + "contents": [ + {{ "key": "name", "valueString": "[User Name]" }}, + {{ "key": "title", "valueString": "[Job Title or Role]" }}, + {{ "key": "avatarUrl", "valueString": "[Avatar Image URL]" }}, + {{ "key": "bio", "valueString": "[User biography or description]" }}, + {{ "key": "email", "valueString": "[Email Address]" }}, + {{ "key": "phone", "valueString": "[Phone Number]" }}, + {{ "key": "stat1Value", "valueString": "[Value]" }}, + {{ "key": "stat1Label", "valueString": "[Label]" }}, + {{ "key": "stat2Value", "valueString": "[Value]" }}, + {{ "key": "stat2Label", "valueString": "[Label]" }}, + {{ "key": "stat3Value", "valueString": "[Value]" }}, + {{ "key": "stat3Label", "valueString": "[Label]" }} + ] + }} }} +] +---END PROFILE_VIEW_WITH_IMAGE_EXAMPLE--- +""" diff --git a/examples/agent/a2ui_agent/samples/agent/general_agent/skills/A2UI_response_generator/UI_templete_examples/search_filter_form.py b/examples/agent/a2ui_agent/samples/agent/general_agent/skills/A2UI_response_generator/UI_templete_examples/search_filter_form.py new file mode 100644 index 0000000000..7c044ec806 --- /dev/null +++ b/examples/agent/a2ui_agent/samples/agent/general_agent/skills/A2UI_response_generator/UI_templete_examples/search_filter_form.py @@ -0,0 +1,43 @@ +# -*- coding: utf-8 -*- +# flake8: noqa: E501 +"""A2UI template example for search filter form.""" + +SEARCH_FILTER_FORM_EXAMPLE = """ +---BEGIN SEARCH_FILTER_FORM_EXAMPLE--- +Use this template for search forms with filters. + +[ + {{ "beginRendering": {{ "surfaceId": "search-form", "root": "search-column", "styles": {{ "primaryColor": "#2196F3", "font": "Roboto" }} }} }}, + {{ "surfaceUpdate": {{ + "surfaceId": "search-form", + "components": [ + {{ "id": "search-column", "component": {{ "Column": {{ "children": {{ "explicitList": ["search-title", "search-input-row", "filter-section", "search-button"] }} }} }} }}, + {{ "id": "search-title", "component": {{ "Text": {{ "usageHint": "h2", "text": {{ "literalString": "Search" }} }} }} }}, + {{ "id": "search-input-row", "component": {{ "Row": {{ "children": {{ "explicitList": ["search-icon", "search-field"] }} }} }} }}, + {{ "id": "search-icon", "component": {{ "Icon": {{ "name": {{ "literalString": "search" }} }} }} }}, + {{ "id": "search-field", "weight": 1, "component": {{ "TextField": {{ "label": {{ "literalString": "Search" }}, "text": {{ "path": "searchQuery" }}, "hint": {{ "literalString": "Enter keywords..." }} }} }} }}, + {{ "id": "filter-section", "component": {{ "Column": {{ "children": {{ "explicitList": ["filter-title", "location-field", "category-field", "price-range-row"] }} }} }} }}, + {{ "id": "filter-title", "component": {{ "Text": {{ "usageHint": "h4", "text": {{ "literalString": "Filters" }} }} }} }}, + {{ "id": "location-field", "component": {{ "TextField": {{ "label": {{ "literalString": "Location" }}, "text": {{ "path": "location" }} }} }} }}, + {{ "id": "category-field", "component": {{ "TextField": {{ "label": {{ "literalString": "Category" }}, "text": {{ "path": "category" }} }} }} }}, + {{ "id": "price-range-row", "component": {{ "Row": {{ "children": {{ "explicitList": ["min-price-field", "max-price-field"] }} }} }} }}, + {{ "id": "min-price-field", "weight": 1, "component": {{ "TextField": {{ "label": {{ "literalString": "Min Price" }}, "text": {{ "path": "minPrice" }}, "type": "number" }} }} }}, + {{ "id": "max-price-field", "weight": 1, "component": {{ "TextField": {{ "label": {{ "literalString": "Max Price" }}, "text": {{ "path": "maxPrice" }}, "type": "number" }} }} }}, + {{ "id": "search-button", "component": {{ "Button": {{ "child": "search-button-text", "primary": true, "action": {{ "name": "perform_search", "context": [ {{ "key": "query", "value": {{ "path": "searchQuery" }} }}, {{ "key": "location", "value": {{ "path": "location" }} }}, {{ "key": "category", "value": {{ "path": "category" }} }}, {{ "key": "minPrice", "value": {{ "path": "minPrice" }} }}, {{ "key": "maxPrice", "value": {{ "path": "maxPrice" }} }} ] }} }} }} }}, + {{ "id": "search-button-text", "component": {{ "Text": {{ "text": {{ "literalString": "Search" }} }} }} }} + ] + }} }}, + {{ "dataModelUpdate": {{ + "surfaceId": "search-form", + "path": "/", + "contents": [ + {{ "key": "searchQuery", "valueString": "" }}, + {{ "key": "location", "valueString": "" }}, + {{ "key": "category", "valueString": "" }}, + {{ "key": "minPrice", "valueString": "" }}, + {{ "key": "maxPrice", "valueString": "" }} + ] + }} }} +] +---END SEARCH_FILTER_FORM_EXAMPLE--- +""" diff --git a/examples/agent/a2ui_agent/samples/agent/general_agent/skills/A2UI_response_generator/UI_templete_examples/selection_card.py b/examples/agent/a2ui_agent/samples/agent/general_agent/skills/A2UI_response_generator/UI_templete_examples/selection_card.py new file mode 100644 index 0000000000..195e5dd4dd --- /dev/null +++ b/examples/agent/a2ui_agent/samples/agent/general_agent/skills/A2UI_response_generator/UI_templete_examples/selection_card.py @@ -0,0 +1,151 @@ +# -*- coding: utf-8 -*- +# flake8: noqa: E501 +"""A2UI template example for selection card.""" + +SELECTION_CARD_EXAMPLE = """ +---BEGIN SELECTION_CARD_EXAMPLE--- +Use this template to display a multiple choice question card with options. + +[ + {{ "beginRendering": {{ "surfaceId": "quiz-card", "root": "quiz-card-root", "styles": {{ "primaryColor": "#673AB7", "font": "Roboto" }} }} }}, + {{ "surfaceUpdate": {{ + "surfaceId": "quiz-card", + "components": [ + {{ "id": "quiz-card-root", "component": {{ "Card": {{ "child": "quiz-card-content" }} }} }}, + {{ "id": "quiz-card-content", "component": {{ "Column": {{ "children": {{ "explicitList": ["quiz-title", "quiz-question", "quiz-options", "quiz-submit-button"] }} }} }} }}, + {{ "id": "quiz-title", "component": {{ "Text": {{ "usageHint": "h1", "text": {{ "path": "title" }} }} }} }}, + {{ "id": "quiz-question", "component": {{ "Text": {{ "usageHint": "h2", "text": {{ "path": "question" }} }} }} }}, + {{ "id": "quiz-options", "component": {{ "Column": {{ "children": {{ "template": {{ "componentId": "option-template", "dataBinding": "/options" }} }} }} }} }}, + {{ "id": "option-template", "component": {{ "Button": {{ "child": "option-text", "action": {{ "name": "select_option", "context": [ {{ "key": "questionId", "value": {{ "path": "/questionId" }} }}, {{ "key": "optionId", "value": {{ "path": "id" }} }}, {{ "key": "optionText", "value": {{ "path": "text" }} }} ] }} }} }}, + {{ "id": "option-text", "component": {{ "Text": {{ "text": {{ "path": "text" }} }} }} }}, + {{ "id": "quiz-submit-button", "component": {{ "Button": {{ "child": "submit-text", "primary": true, "action": {{ "name": "submit_answer", "context": [ {{ "key": "questionId", "value": {{ "path": "questionId" }} }}, {{ "key": "selectedOption", "value": {{ "path": "selectedOption" }} }} ] }} }} }} }}, + {{ "id": "submit-text", "component": {{ "Text": {{ "text": {{ "literalString": "Submit Answer" }} }} }} }} + ] + }} }}, + {{ "dataModelUpdate": {{ + "surfaceId": "quiz-card", + "path": "/", + "contents": [ + {{ "key": "title", "valueString": "[Quiz Title]" }}, + {{ "key": "question", "valueString": "[Question Content]" }}, + {{ "key": "questionId", "valueString": "[Question ID]" }}, + {{ "key": "selectedOption", "valueString": "" }}, + {{ "key": "options", "valueMap": [ + {{ "key": "option1", "valueMap": [ + {{ "key": "id", "valueString": "A" }}, + {{ "key": "text", "valueString": "[Option A]" }} + ] }}, + {{ "key": "option2", "valueMap": [ + {{ "key": "id", "valueString": "B" }}, + {{ "key": "text", "valueString": "[Option B]" }} + ] }}, + {{ "key": "option3", "valueMap": [ + {{ "key": "id", "valueString": "C" }}, + {{ "key": "text", "valueString": "[Option C]" }} + ] }}, + {{ "key": "option4", "valueMap": [ + {{ "key": "id", "valueString": "D" }}, + {{ "key": "text", "valueString": "[Option D]" }} + ] }} + ] }} + ] + }} }} +] +---END SELECTION_CARD_EXAMPLE--- +""" + +MULTIPLE_SELECTION_CARDS_EXAMPLE = """ +---BEGIN MULTIPLE_SELECTION_CARDS_EXAMPLE--- +Use this template to display multiple selection cards in a vertical list. Each card represents a separate question with its own options. + +[ + {{ "beginRendering": {{ "surfaceId": "multi-quiz", "root": "root-column", "styles": {{ "primaryColor": "#673AB7", "font": "Roboto" }} }} }}, + {{ "surfaceUpdate": {{ + "surfaceId": "multi-quiz", + "components": [ + {{ "id": "root-column", "component": {{ "Column": {{ "children": {{ "explicitList": ["page-title", "quiz-list"] }} }} }} }}, + {{ "id": "page-title", "component": {{ "Text": {{ "usageHint": "h1", "text": {{ "path": "pageTitle" }} }} }} }}, + {{ "id": "quiz-list", "component": {{ "List": {{ "direction": "vertical", "children": {{ "template": {{ "componentId": "quiz-card-template", "dataBinding": "/questions" }} }} }} }} }}, + {{ "id": "quiz-card-template", "component": {{ "Card": {{ "child": "quiz-card-content" }} }} }}, + {{ "id": "quiz-card-content", "component": {{ "Column": {{ "children": {{ "explicitList": ["quiz-title", "quiz-question", "quiz-options", "quiz-submit-button"] }} }} }} }}, + {{ "id": "quiz-title", "component": {{ "Text": {{ "usageHint": "h2", "text": {{ "path": "title" }} }} }} }}, + {{ "id": "quiz-question", "component": {{ "Text": {{ "usageHint": "h3", "text": {{ "path": "question" }} }} }} }}, + {{ "id": "quiz-options", "component": {{ "Column": {{ "children": {{ "template": {{ "componentId": "option-template", "dataBinding": "options" }} }} }} }} }}, + {{ "id": "option-template", "component": {{ "Button": {{ "child": "option-text", "action": {{ "name": "select_option", "context": [ {{ "key": "questionId", "value": {{ "path": "/questionId" }} }}, {{ "key": "optionId", "value": {{ "path": "id" }} }}, {{ "key": "optionText", "value": {{ "path": "text" }} }} ] }} }} }}, + {{ "id": "option-text", "component": {{ "Text": {{ "text": {{ "path": "text" }} }} }} }}, + {{ "id": "quiz-submit-button", "component": {{ "Button": {{ "child": "submit-text", "primary": true, "action": {{ "name": "submit_answer", "context": [ {{ "key": "questionId", "value": {{ "path": "questionId" }} }}, {{ "key": "selectedOption", "value": {{ "path": "selectedOption" }} }} ] }} }} }}, + {{ "id": "submit-text", "component": {{ "Text": {{ "text": {{ "literalString": "Submit Answer" }} }} }} }} + ] + }} }}, + {{ "dataModelUpdate": {{ + "surfaceId": "multi-quiz", + "path": "/", + "contents": [ + {{ "key": "pageTitle", "valueString": "[Page Title]" }}, + {{ "key": "questions", "valueMap": [ + {{ "key": "question1", "valueMap": [ + {{ "key": "questionId", "valueString": "q1" }}, + {{ "key": "title", "valueString": "[Question 1 Title]" }}, + {{ "key": "question", "valueString": "[Question 1 Content]" }}, + {{ "key": "selectedOption", "valueString": "" }}, + {{ "key": "options", "valueMap": [ + {{ "key": "option1", "valueMap": [ + {{ "key": "id", "valueString": "A" }}, + {{ "key": "text", "valueString": "[Option A]" }} + ] }}, + {{ "key": "option2", "valueMap": [ + {{ "key": "id", "valueString": "B" }}, + {{ "key": "text", "valueString": "[Option B]" }} + ] }}, + {{ "key": "option3", "valueMap": [ + {{ "key": "id", "valueString": "C" }}, + {{ "key": "text", "valueString": "[Option C]" }} + ] }} + ] }} + ] }}, + {{ "key": "question2", "valueMap": [ + {{ "key": "questionId", "valueString": "q2" }}, + {{ "key": "title", "valueString": "[Question 2 Title]" }}, + {{ "key": "question", "valueString": "[Question 2 Content]" }}, + {{ "key": "selectedOption", "valueString": "" }}, + {{ "key": "options", "valueMap": [ + {{ "key": "option1", "valueMap": [ + {{ "key": "id", "valueString": "A" }}, + {{ "key": "text", "valueString": "[Option A]" }} + ] }}, + {{ "key": "option2", "valueMap": [ + {{ "key": "id", "valueString": "B" }}, + {{ "key": "text", "valueString": "[Option B]" }} + ] }}, + {{ "key": "option3", "valueMap": [ + {{ "key": "id", "valueString": "C" }}, + {{ "key": "text", "valueString": "[Option C]" }} + ] }}, + {{ "key": "option4", "valueMap": [ + {{ "key": "id", "valueString": "D" }}, + {{ "key": "text", "valueString": "[Option D]" }} + ] }} + ] }} + ] }}, + {{ "key": "question3", "valueMap": [ + {{ "key": "questionId", "valueString": "q3" }}, + {{ "key": "title", "valueString": "[Question 3 Title]" }}, + {{ "key": "question", "valueString": "[Question 3 Content]" }}, + {{ "key": "selectedOption", "valueString": "" }}, + {{ "key": "options", "valueMap": [ + {{ "key": "option1", "valueMap": [ + {{ "key": "id", "valueString": "A" }}, + {{ "key": "text", "valueString": "[Option A]" }} + ] }}, + {{ "key": "option2", "valueMap": [ + {{ "key": "id", "valueString": "B" }}, + {{ "key": "text", "valueString": "[Option B]" }} + ] }} + ] }} + ] }} + ] }} + ] + }} }} +] +---END MULTIPLE_SELECTION_CARDS_EXAMPLE--- +""" diff --git a/examples/agent/a2ui_agent/samples/agent/general_agent/skills/A2UI_response_generator/UI_templete_examples/simple_column_list_without_image.py b/examples/agent/a2ui_agent/samples/agent/general_agent/skills/A2UI_response_generator/UI_templete_examples/simple_column_list_without_image.py new file mode 100644 index 0000000000..306f813591 --- /dev/null +++ b/examples/agent/a2ui_agent/samples/agent/general_agent/skills/A2UI_response_generator/UI_templete_examples/simple_column_list_without_image.py @@ -0,0 +1,41 @@ +# -*- coding: utf-8 -*- +# flake8: noqa: E501 +"""A2UI template example for simple column list without images.""" + +SIMPLE_LIST_EXAMPLE = """ +---BEGIN SIMPLE_LIST_EXAMPLE--- +Use this template for compact lists without images. + +[ + {{ "beginRendering": {{ "surfaceId": "default", "root": "root-column", "styles": {{ "primaryColor": "#2196F3", "font": "Roboto" }} }} }}, + {{ "surfaceUpdate": {{ + "surfaceId": "default", + "components": [ + {{ "id": "root-column", "component": {{ "Column": {{ "children": {{ "explicitList": ["title-heading", "item-list"] }} }} }} }}, + {{ "id": "title-heading", "component": {{ "Text": {{ "usageHint": "h1", "text": {{ "path": "title" }} }} }} }}, + {{ "id": "item-list", "component": {{ "List": {{ "direction": "vertical", "children": {{ "template": {{ "componentId": "list-item-template", "dataBinding": "/items" }} }} }} }} }}, + {{ "id": "list-item-template", "component": {{ "Row": {{ "children": {{ "explicitList": ["item-icon", "item-content", "item-action"] }} }} }} }}, + {{ "id": "item-icon", "component": {{ "Icon": {{ "name": {{ "path": "icon" }} }} }} }}, + {{ "id": "item-content", "weight": 1, "component": {{ "Column": {{ "children": {{ "explicitList": ["item-title", "item-subtitle"] }} }} }} }}, + {{ "id": "item-title", "component": {{ "Text": {{ "usageHint": "h4", "text": {{ "path": "title" }} }} }} }}, + {{ "id": "item-subtitle", "component": {{ "Text": {{ "usageHint": "caption", "text": {{ "path": "subtitle" }} }} }} }}, + {{ "id": "item-action", "component": {{ "Icon": {{ "name": {{ "literalString": "arrowForward" }} }} }} }} + ] + }} }}, + {{ "dataModelUpdate": {{ + "surfaceId": "default", + "path": "/", + "contents": [ + {{ "key": "title", "valueString": "[List Title]" }}, + {{ "key": "items", "valueMap": [ + {{ "key": "item1", "valueMap": [ + {{ "key": "icon", "valueString": "folder" }}, + {{ "key": "title", "valueString": "[Item Title]" }}, + {{ "key": "subtitle", "valueString": "[Item Subtitle]" }} + ] }} + ] }} + ] + }} }} +] +---END SIMPLE_LIST_EXAMPLE--- +""" diff --git a/examples/agent/a2ui_agent/samples/agent/general_agent/skills/A2UI_response_generator/UI_templete_examples/single_column_list.py b/examples/agent/a2ui_agent/samples/agent/general_agent/skills/A2UI_response_generator/UI_templete_examples/single_column_list.py new file mode 100644 index 0000000000..eb19c20c5d --- /dev/null +++ b/examples/agent/a2ui_agent/samples/agent/general_agent/skills/A2UI_response_generator/UI_templete_examples/single_column_list.py @@ -0,0 +1,49 @@ +# -*- coding: utf-8 -*- +# flake8: noqa: E501 +"""A2UI template example for single column list with images.""" + +# List examples +SINGLE_COLUMN_LIST_WITH_IMAGE_EXAMPLE = """ +---BEGIN SINGLE_COLUMN_LIST_WITH_IMAGE_EXAMPLE--- +Use this template when displaying a list of 5 or fewer items with detailed information. + +[ + {{ "beginRendering": {{ "surfaceId": "default", "root": "root-column", "styles": {{ "primaryColor": "#FF0000", "font": "Roboto" }} }} }}, + {{ "surfaceUpdate": {{ + "surfaceId": "default", + "components": [ + {{ "id": "root-column", "component": {{ "Column": {{ "children": {{ "explicitList": ["title-heading", "item-list"] }} }} }} }}, + {{ "id": "title-heading", "component": {{ "Text": {{ "usageHint": "h1", "text": {{ "path": "title" }} }} }} }}, + {{ "id": "item-list", "component": {{ "List": {{ "direction": "vertical", "children": {{ "template": {{ "componentId": "item-card-template", "dataBinding": "/items" }} }} }} }} }}, + {{ "id": "item-card-template", "component": {{ "Card": {{ "child": "card-layout" }} }} }}, + {{ "id": "card-layout", "component": {{ "Row": {{ "children": {{ "explicitList": ["template-image", "card-details"] }} }} }} }}, + {{ "id": "template-image", "weight": 1, "component": {{ "Image": {{ "url": {{ "path": "imageUrl" }} }} }} }}, + {{ "id": "card-details", "weight": 2, "component": {{ "Column": {{ "children": {{ "explicitList": ["template-name", "template-rating", "template-detail", "template-link", "template-action-button"] }} }} }} }}, + {{ "id": "template-name", "component": {{ "Text": {{ "usageHint": "h3", "text": {{ "path": "name" }} }} }} }}, + {{ "id": "template-rating", "component": {{ "Text": {{ "text": {{ "path": "rating" }} }} }} }}, + {{ "id": "template-detail", "component": {{ "Text": {{ "text": {{ "path": "detail" }} }} }} }}, + {{ "id": "template-link", "component": {{ "Text": {{ "text": {{ "path": "infoLink" }} }} }} }}, + {{ "id": "template-action-button", "component": {{ "Button": {{ "child": "action-button-text", "primary": true, "action": {{ "name": "select_item", "context": [ {{ "key": "itemName", "value": {{ "path": "name" }} }}, {{ "key": "itemId", "value": {{ "path": "id" }} }} ] }} }} }} }}, + {{ "id": "action-button-text", "component": {{ "Text": {{ "text": {{ "literalString": "Select" }} }} }} }} + ] + }} }}, + {{ "dataModelUpdate": {{ + "surfaceId": "default", + "path": "/", + "contents": [ + {{ "key": "title", "valueString": "[Your List Title]" }}, + {{ "key": "items", "valueMap": [ + {{ "key": "item1", "valueMap": [ + {{ "key": "id", "valueString": "1" }}, + {{ "key": "name", "valueString": "[Item Name]" }}, + {{ "key": "rating", "valueString": "[Rating]" }}, + {{ "key": "detail", "valueString": "[Detail Description]" }}, + {{ "key": "infoLink", "valueString": "[URL]" }}, + {{ "key": "imageUrl", "valueString": "[Image URL]" }} + ] }} + ] }} + ] + }} }} +] +---END SINGLE_COLUMN_LIST_WITH_IMAGE_EXAMPLE--- +""" diff --git a/examples/agent/a2ui_agent/samples/agent/general_agent/skills/A2UI_response_generator/UI_templete_examples/success_confirmation_with_image.py b/examples/agent/a2ui_agent/samples/agent/general_agent/skills/A2UI_response_generator/UI_templete_examples/success_confirmation_with_image.py new file mode 100644 index 0000000000..e4edc34a89 --- /dev/null +++ b/examples/agent/a2ui_agent/samples/agent/general_agent/skills/A2UI_response_generator/UI_templete_examples/success_confirmation_with_image.py @@ -0,0 +1,53 @@ +# -*- coding: utf-8 -*- +# flake8: noqa: E501 +"""A2UI template example for success confirmation with image.""" +# Confirmation examples +SUCCESS_CONFIRMATION_WITH_IMAGE_EXAMPLE = """ +---BEGIN SUCCESS_CONFIRMATION_WITH_IMAGE_EXAMPLE--- +Use this template to display success confirmations after an action. + +[ + {{ "beginRendering": {{ "surfaceId": "confirmation", "root": "confirmation-card", "styles": {{ "primaryColor": "#4CAF50", "font": "Roboto" }} }} }}, + {{ "surfaceUpdate": {{ + "surfaceId": "confirmation", + "components": [ + {{ "id": "confirmation-card", "component": {{ "Card": {{ "child": "confirmation-column" }} }} }}, + {{ "id": "confirmation-column", "component": {{ "Column": {{ "children": {{ "explicitList": ["success-icon", "confirm-title", "confirm-image", "divider1", "confirm-details", "divider2", "confirm-message", "action-button"] }} }} }} }}, + {{ "id": "success-icon", "component": {{ "Icon": {{ + "name": {{ "literalString": "check" }} + }} }} }}, + {{ "id": "confirm-title", "component": {{ "Text": {{ + "usageHint": "h2", "text": {{ "path": "title" }} + }} }} }}, + {{ "id": "confirm-image", "component": {{ "Image": {{ + "url": {{ "path": "imageUrl" }}, "usageHint": "mediumFeature" + }} }} }}, + {{ "id": "confirm-details", "component": {{ "Text": {{ + "text": {{ "path": "details" }} + }} }} }}, + {{ "id": "confirm-message", "component": {{ "Text": {{ + "usageHint": "h5", "text": {{ "path": "message" }} + }} }} }}, + {{ "id": "divider1", "component": {{ "Divider": {{}} }} }}, + {{ "id": "divider2", "component": {{ "Divider": {{}} }} }}, + {{ "id": "action-button", "component": {{ "Button": {{ + "child": "action-text", "action": {{ "name": "dismiss", "context": [] }} + }} }} }}, + {{ "id": "action-text", "component": {{ "Text": {{ + "text": {{ "literalString": "Done" }} + }} }} }} + ] + }} }}, + {{ "dataModelUpdate": {{ + "surfaceId": "confirmation", + "path": "/", + "contents": [ + {{ "key": "title", "valueString": "[Confirmation Title]" }}, + {{ "key": "details", "valueString": "[Booking/Action Details]" }}, + {{ "key": "message", "valueString": "We look forward to seeing you!" }}, + {{ "key": "imageUrl", "valueString": "[Image URL]" }} + ] + }} }} +] +---END SUCCESS_CONFIRMATION_WITH_IMAGE_EXAMPLE--- +""" diff --git a/examples/agent/a2ui_agent/samples/agent/general_agent/skills/A2UI_response_generator/UI_templete_examples/two_column_list.py b/examples/agent/a2ui_agent/samples/agent/general_agent/skills/A2UI_response_generator/UI_templete_examples/two_column_list.py new file mode 100644 index 0000000000..a2c2b9d286 --- /dev/null +++ b/examples/agent/a2ui_agent/samples/agent/general_agent/skills/A2UI_response_generator/UI_templete_examples/two_column_list.py @@ -0,0 +1,57 @@ +# -*- coding: utf-8 -*- +# flake8: noqa: E501 +"""A2UI template example for two column grid list with images.""" + +TWO_COLUMN_LIST_WITH_IMAGE_EXAMPLE = """ +---BEGIN TWO_COLUMN_LIST_WITH_IMAGE_EXAMPLE--- +Use this template when displaying more than 5 items in a grid layout. + +[ + {{ "beginRendering": {{ "surfaceId": "default", "root": "root-column", "styles": {{ "primaryColor": "#FF0000", "font": "Roboto" }} }} }}, + {{ "surfaceUpdate": {{ + "surfaceId": "default", + "components": [ + {{ "id": "root-column", "component": {{ "Column": {{ "children": {{ "explicitList": ["title-heading", "item-row-1", "item-row-2"] }} }} }} }}, + {{ "id": "title-heading", "component": {{ "Text": {{ "usageHint": "h1", "text": {{ "path": "title" }} }} }} }}, + {{ "id": "item-row-1", "component": {{ "Row": {{ "children": {{ "explicitList": ["item-card-1", "item-card-2"] }} }} }} }}, + {{ "id": "item-row-2", "component": {{ "Row": {{ "children": {{ "explicitList": ["item-card-3", "item-card-4"] }} }} }} }}, + {{ "id": "item-card-1", "weight": 1, "component": {{ "Card": {{ "child": "card-layout-1" }} }} }}, + {{ "id": "card-layout-1", "component": {{ "Column": {{ "children": {{ "explicitList": ["template-image-1", "card-details-1"] }} }} }} }}, + {{ "id": "template-image-1", "component": {{ "Image": {{ "url": {{ "path": "/items/0/imageUrl" }}, "width": "100%" }} }} }}, + {{ "id": "card-details-1", "component": {{ "Column": {{ "children": {{ "explicitList": ["template-name-1", "template-rating-1", "template-action-button-1"] }} }} }} }}, + {{ "id": "template-name-1", "component": {{ "Text": {{ "usageHint": "h3", "text": {{ "path": "/items/0/name" }} }} }} }}, + {{ "id": "template-rating-1", "component": {{ "Text": {{ "text": {{ "path": "/items/0/rating" }} }} }} }}, + {{ "id": "template-action-button-1", "component": {{ "Button": {{ "child": "action-text-1", "action": {{ "name": "select_item", "context": [ {{ "key": "itemName", "value": {{ "path": "/items/0/name" }} }} ] }} }} }} }}, + {{ "id": "action-text-1", "component": {{ "Text": {{ "text": {{ "literalString": "Select" }} }} }} }}, + {{ "id": "item-card-2", "weight": 1, "component": {{ "Card": {{ "child": "card-layout-2" }} }} }}, + {{ "id": "card-layout-2", "component": {{ "Column": {{ "children": {{ "explicitList": ["template-image-2", "card-details-2"] }} }} }} }}, + {{ "id": "template-image-2", "component": {{ "Image": {{ "url": {{ "path": "/items/1/imageUrl" }}, "width": "100%" }} }} }}, + {{ "id": "card-details-2", "component": {{ "Column": {{ "children": {{ "explicitList": ["template-name-2", "template-rating-2", "template-action-button-2"] }} }} }} }}, + {{ "id": "template-name-2", "component": {{ "Text": {{ "usageHint": "h3", "text": {{ "path": "/items/1/name" }} }} }} }}, + {{ "id": "template-rating-2", "component": {{ "Text": {{ "text": {{ "path": "/items/1/rating" }} }} }} }}, + {{ "id": "template-action-button-2", "component": {{ "Button": {{ "child": "action-text-2", "action": {{ "name": "select_item", "context": [ {{ "key": "itemName", "value": {{ "path": "/items/1/name" }} }} ] }} }} }} }}, + {{ "id": "action-text-2", "component": {{ "Text": {{ "text": {{ "literalString": "Select" }} }} }} }} + ] + }} }}, + {{ "dataModelUpdate": {{ + "surfaceId": "default", + "path": "/", + "contents": [ + {{ "key": "title", "valueString": "[Your Grid Title]" }}, + {{ "key": "items", "valueMap": [ + {{ "key": "0", "valueMap": [ + {{ "key": "name", "valueString": "[Item 1 Name]" }}, + {{ "key": "rating", "valueString": "[Rating]" }}, + {{ "key": "imageUrl", "valueString": "[Image URL]" }} + ] }}, + {{ "key": "1", "valueMap": [ + {{ "key": "name", "valueString": "[Item 2 Name]" }}, + {{ "key": "rating", "valueString": "[Rating]" }}, + {{ "key": "imageUrl", "valueString": "[Image URL]" }} + ] }} + ] }} + ] + }} }} +] +---END TWO_COLUMN_LIST_WITH_IMAGE_EXAMPLE--- +""" diff --git a/examples/agent/a2ui_agent/samples/agent/general_agent/skills/A2UI_response_generator/__init__.py b/examples/agent/a2ui_agent/samples/agent/general_agent/skills/A2UI_response_generator/__init__.py new file mode 100644 index 0000000000..8e2a436b7d --- /dev/null +++ b/examples/agent/a2ui_agent/samples/agent/general_agent/skills/A2UI_response_generator/__init__.py @@ -0,0 +1,9 @@ +# -*- coding: utf-8 -*- +"""A2UI response generator skill package.""" +from .view_a2ui_schema import view_a2ui_schema +from .view_a2ui_examples import view_a2ui_examples + +__all__ = [ + "view_a2ui_schema", + "view_a2ui_examples", +] diff --git a/examples/agent/a2ui_agent/samples/agent/general_agent/skills/A2UI_response_generator/schema/__init__.py b/examples/agent/a2ui_agent/samples/agent/general_agent/skills/A2UI_response_generator/schema/__init__.py new file mode 100644 index 0000000000..b02af82a40 --- /dev/null +++ b/examples/agent/a2ui_agent/samples/agent/general_agent/skills/A2UI_response_generator/schema/__init__.py @@ -0,0 +1,7 @@ +# -*- coding: utf-8 -*- +"""A2UI schema module for validating A2UI protocol messages.""" +from .base_schema import A2UI_SCHEMA + +__all__ = [ + "A2UI_SCHEMA", +] diff --git a/examples/agent/a2ui_agent/samples/agent/general_agent/skills/A2UI_response_generator/schema/base_schema.py b/examples/agent/a2ui_agent/samples/agent/general_agent/skills/A2UI_response_generator/schema/base_schema.py new file mode 100644 index 0000000000..b86b1dbeb1 --- /dev/null +++ b/examples/agent/a2ui_agent/samples/agent/general_agent/skills/A2UI_response_generator/schema/base_schema.py @@ -0,0 +1,776 @@ +# -*- coding: utf-8 -*- +# flake8: noqa: E501 +"""A2UI base schema definition for validating A2UI protocol messages.""" +# The complete A2UI schema (copied from Google A2UI repository) +A2UI_SCHEMA = r""" +{ + "title": "A2UI Message Schema", + "description": "Describes a JSON payload for an A2UI (Agent to UI) message, which is used to dynamically construct and update user interfaces. A message MUST contain exactly ONE of the action properties: 'beginRendering', 'surfaceUpdate', 'dataModelUpdate', or 'deleteSurface'.", + "type": "object", + "properties": { + "beginRendering": { + "type": "object", + "description": "Signals the client to begin rendering a surface " + "with a root component and specific styles.", + "properties": { + "surfaceId": { + "type": "string", + "description": "The unique identifier for the UI surface to be rendered." + }, + "root": { + "type": "string", + "description": "The ID of the root component to render." + }, + "styles": { + "type": "object", + "description": "Styling information for the UI.", + "properties": { + "font": { + "type": "string", + "description": "The primary font for the UI." + }, + "primaryColor": { + "type": "string", + "description": "The primary UI color as a hexadecimal code (e.g., '#00BFFF').", + "pattern": "^#[0-9a-fA-F]{6}$" + } + } + } + }, + "required": ["root", "surfaceId"] + }, + "surfaceUpdate": { + "type": "object", + "description": "Updates a surface with a new set of components.", + "properties": { + "surfaceId": { + "type": "string", + "description": "The unique identifier for the UI surface to be updated. If you are adding a new surface this *must* be a new, unique identified that has never been used for any existing surfaces shown." + }, + "components": { + "type": "array", + "description": "A list containing all UI components for the surface.", + "minItems": 1, + "items": { + "type": "object", + "description": "Represents a *single* component in a UI widget tree. This component could be one of many supported types.", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for this component." + }, + "weight": { + "type": "number", + "description": "The relative weight of this component within a Row or Column. This corresponds to the CSS 'flex-grow' property. Note: this may ONLY be set when the component is a direct descendant of a Row or Column." + }, + "component": { + "type": "object", + "description": "A wrapper object that MUST contain exactly one key, which is the name of the component type (e.g., 'Heading'). The value is an object containing the properties for that specific component.", + "properties": { + "Text": { + "type": "object", + "properties": { + "text": { + "type": "object", + "description": "The text content to display. This can be a literal string or a reference to a value in the data model ('path', e.g., '/doc/title'). While simple Markdown formatting is supported (i.e. without HTML, images, or links), utilizing dedicated UI components is generally preferred for a richer and more structured presentation.", + "properties": { + "literalString": { + "type": "string" + }, + "path": { + "type": "string" + } + } + }, + "usageHint": { + "type": "string", + "description": "A hint for the base text style. One of:\n- `h1`: Largest heading.\n- `h2`: Second largest heading.\n- `h3`: Third largest heading.\n- `h4`: Fourth largest heading.\n- `h5`: Fifth largest heading.\n- `caption`: Small text for captions.\n- `body`: Standard body text.", + "enum": [ + "h1", + "h2", + "h3", + "h4", + "h5", + "caption", + "body" + ] + } + }, + "required": ["text"] + }, + "Image": { + "type": "object", + "properties": { + "url": { + "type": "object", + "description": "The URL of the image to display. This can be a literal string ('literal') or a reference to a value in the data model ('path', e.g. '/thumbnail/url').", + "properties": { + "literalString": { + "type": "string" + }, + "path": { + "type": "string" + } + } + }, + "fit": { + "type": "string", + "description": "Specifies how the image should be resized to fit its container. This corresponds to the CSS 'object-fit' property.", + "enum": [ + "contain", + "cover", + "fill", + "none", + "scale-down" + ] + }, + "usageHint": { + "type": "string", + "description": "A hint for the image size and style. One of:\n- `icon`: Small square icon.\n- `avatar`: Circular avatar image.\n- `smallFeature`: Small feature image.\n- `mediumFeature`: Medium feature image.\n- `largeFeature`: Large feature image.\n- `header`: Full-width, full bleed, header image.", + "enum": [ + "icon", + "avatar", + "smallFeature", + "mediumFeature", + "largeFeature", + "header" + ] + } + }, + "required": ["url"] + }, + "Icon": { + "type": "object", + "properties": { + "name": { + "type": "object", + "description": "The name of the icon to display. This can be a literal string or a reference to a value in the data model ('path', e.g. '/form/submit').", + "properties": { + "literalString": { + "type": "string", + "enum": [ + "accountCircle", + "add", + "arrowBack", + "arrowForward", + "attachFile", + "calendarToday", + "call", + "camera", + "check", + "close", + "delete", + "download", + "edit", + "event", + "error", + "favorite", + "favoriteOff", + "folder", + "help", + "home", + "info", + "locationOn", + "lock", + "lockOpen", + "mail", + "menu", + "moreVert", + "moreHoriz", + "notificationsOff", + "notifications", + "payment", + "person", + "phone", + "photo", + "print", + "refresh", + "search", + "send", + "settings", + "share", + "shoppingCart", + "star", + "starHalf", + "starOff", + "upload", + "visibility", + "visibilityOff", + "warning" + ] + }, + "path": { + "type": "string" + } + } + } + }, + "required": ["name"] + }, + "Video": { + "type": "object", + "properties": { + "url": { + "type": "object", + "description": "The URL of the video to display. This can be a literal string or a reference to a value in the data model ('path', e.g. '/video/url').", + "properties": { + "literalString": { + "type": "string" + }, + "path": { + "type": "string" + } + } + } + }, + "required": ["url"] + }, + "AudioPlayer": { + "type": "object", + "properties": { + "url": { + "type": "object", + "description": "The URL of the audio to be played. This can be a literal string ('literal') or a reference to a value in the data model ('path', e.g. '/song/url').", + "properties": { + "literalString": { + "type": "string" + }, + "path": { + "type": "string" + } + } + }, + "description": { + "type": "object", + "description": "A description of the audio, such as a title or summary. This can be a literal string or a reference to a value in the data model ('path', e.g. '/song/title').", + "properties": { + "literalString": { + "type": "string" + }, + "path": { + "type": "string" + } + } + } + }, + "required": ["url"] + }, + "Row": { + "type": "object", + "properties": { + "children": { + "type": "object", + "description": "Defines the children. Use 'explicitList' for a fixed set of children, or 'template' to generate children from a data list.", + "properties": { + "explicitList": { + "type": "array", + "items": { + "type": "string" + } + }, + "template": { + "type": "object", + "description": "A template for generating a dynamic list of children from a data model list. `componentId` is the component to use as a template, and `dataBinding` is the path to the map of components in the data model. Values in the map will define the list of children.", + "properties": { + "componentId": { + "type": "string" + }, + "dataBinding": { + "type": "string" + } + }, + "required": ["componentId", "dataBinding"] + } + } + }, + "distribution": { + "type": "string", + "description": "Defines the arrangement of children along the main axis (horizontally). This corresponds to the CSS 'justify-content' property.", + "enum": [ + "center", + "end", + "spaceAround", + "spaceBetween", + "spaceEvenly", + "start" + ] + }, + "alignment": { + "type": "string", + "description": "Defines the alignment of children along the cross axis (vertically). This corresponds to the CSS 'align-items' property.", + "enum": ["start", "center", "end", "stretch"] + } + }, + "required": ["children"] + }, + "Column": { + "type": "object", + "properties": { + "children": { + "type": "object", + "description": "Defines the children. Use 'explicitList' for a fixed set of children, or 'template' to generate children from a data list.", + "properties": { + "explicitList": { + "type": "array", + "items": { + "type": "string" + } + }, + "template": { + "type": "object", + "description": "A template for generating a dynamic list of children from a data model list. `componentId` is the component to use as a template, and `dataBinding` is the path to the map of components in the data model. Values in the map will define the list of children.", + "properties": { + "componentId": { + "type": "string" + }, + "dataBinding": { + "type": "string" + } + }, + "required": ["componentId", "dataBinding"] + } + } + }, + "distribution": { + "type": "string", + "description": "Defines the arrangement of children along the main axis (vertically). This corresponds to the CSS 'justify-content' property.", + "enum": [ + "start", + "center", + "end", + "spaceBetween", + "spaceAround", + "spaceEvenly" + ] + }, + "alignment": { + "type": "string", + "description": "Defines the alignment of children along the cross axis (horizontally). This corresponds to the CSS 'align-items' property.", + "enum": ["center", "end", "start", "stretch"] + } + }, + "required": ["children"] + }, + "List": { + "type": "object", + "properties": { + "children": { + "type": "object", + "description": "Defines the children. Use 'explicitList' for a fixed set of children, or 'template' to generate children from a data list.", + "properties": { + "explicitList": { + "type": "array", + "items": { + "type": "string" + } + }, + "template": { + "type": "object", + "description": "A template for generating a dynamic list of children from a data model list. `componentId` is the component to use as a template, and `dataBinding` is the path to the map of components in the data model. Values in the map will define the list of children.", + "properties": { + "componentId": { + "type": "string" + }, + "dataBinding": { + "type": "string" + } + }, + "required": ["componentId", "dataBinding"] + } + } + }, + "direction": { + "type": "string", + "description": "The direction in which the list items are laid out.", + "enum": ["vertical", "horizontal"] + }, + "alignment": { + "type": "string", + "description": "Defines the alignment of children along the cross axis.", + "enum": ["start", "center", "end", "stretch"] + } + }, + "required": ["children"] + }, + "Card": { + "type": "object", + "properties": { + "child": { + "type": "string", + "description": "The ID of the component to be rendered inside the card." + } + }, + "required": ["child"] + }, + "Tabs": { + "type": "object", + "properties": { + "tabItems": { + "type": "array", + "description": "An array of objects, where each object defines a tab with a title and a child component.", + "items": { + "type": "object", + "properties": { + "title": { + "type": "object", + "description": "The tab title. Defines the value as either a literal value or a path to data model value (e.g. '/options/title').", + "properties": { + "literalString": { + "type": "string" + }, + "path": { + "type": "string" + } + } + }, + "child": { + "type": "string" + } + }, + "required": ["title", "child"] + } + } + }, + "required": ["tabItems"] + }, + "Divider": { + "type": "object", + "properties": { + "axis": { + "type": "string", + "description": "The orientation of the divider.", + "enum": ["horizontal", "vertical"] + } + } + }, + "Modal": { + "type": "object", + "properties": { + "entryPointChild": { + "type": "string", + "description": "The ID of the component that opens the modal when interacted with (e.g., a button)." + }, + "contentChild": { + "type": "string", + "description": "The ID of the component to be displayed inside the modal." + } + }, + "required": ["entryPointChild", "contentChild"] + }, + "Button": { + "type": "object", + "properties": { + "child": { + "type": "string", + "description": "The ID of the component to display in the button, typically a Text component." + }, + "primary": { + "type": "boolean", + "description": "Indicates if this button should be styled as the primary action." + }, + "action": { + "type": "object", + "description": "The client-side action to be dispatched when the button is clicked. It includes the action's name and an optional context payload.", + "properties": { + "name": { + "type": "string" + }, + "context": { + "type": "array", + "items": { + "type": "object", + "properties": { + "key": { + "type": "string" + }, + "value": { + "type": "object", + "description": "Defines the value to be included in the context as either a literal value or a path to a data model value (e.g. '/user/name').", + "properties": { + "path": { + "type": "string" + }, + "literalString": { + "type": "string" + }, + "literalNumber": { + "type": "number" + }, + "literalBoolean": { + "type": "boolean" + } + } + } + }, + "required": ["key", "value"] + } + } + }, + "required": ["name"] + } + }, + "required": ["child", "action"] + }, + "CheckBox": { + "type": "object", + "properties": { + "label": { + "type": "object", + "description": "The text to display next to the checkbox. Defines the value as either a literal value or a path to data model ('path', e.g. '/option/label').", + "properties": { + "literalString": { + "type": "string" + }, + "path": { + "type": "string" + } + } + }, + "value": { + "type": "object", + "description": "The current state of the checkbox (true for checked, false for unchecked). This can be a literal boolean ('literalBoolean') or a reference to a value in the data model ('path', e.g. '/filter/open').", + "properties": { + "literalBoolean": { + "type": "boolean" + }, + "path": { + "type": "string" + } + } + } + }, + "required": ["label", "value"] + }, + "TextField": { + "type": "object", + "properties": { + "label": { + "type": "object", + "description": "The text label for the input field. This can be a literal string or a reference to a value in the data model ('path, e.g. '/user/name').", + "properties": { + "literalString": { + "type": "string" + }, + "path": { + "type": "string" + } + } + }, + "text": { + "type": "object", + "description": "The value of the text field. This can be a literal string or a reference to a value in the data model ('path', e.g. '/user/name').", + "properties": { + "literalString": { + "type": "string" + }, + "path": { + "type": "string" + } + } + }, + "textFieldType": { + "type": "string", + "description": "The type of input field to display.", + "enum": [ + "date", + "longText", + "number", + "shortText", + "obscured" + ] + }, + "validationRegexp": { + "type": "string", + "description": "A regular expression used for client-side validation of the input." + } + }, + "required": ["label"] + }, + "DateTimeInput": { + "type": "object", + "properties": { + "value": { + "type": "object", + "description": "The selected date and/or time value in ISO 8601 format. This can be a literal string ('literalString') or a reference to a value in the data model ('path', e.g. '/user/dob').", + "properties": { + "literalString": { + "type": "string" + }, + "path": { + "type": "string" + } + } + }, + "enableDate": { + "type": "boolean", + "description": "If true, allows the user to select a date." + }, + "enableTime": { + "type": "boolean", + "description": "If true, allows the user to select a time." + } + }, + "required": ["value"] + }, + "MultipleChoice": { + "type": "object", + "properties": { + "selections": { + "type": "object", + "description": "The currently selected values for the component. This can be a literal array of strings or a path to an array in the data model('path', e.g. '/hotel/options').", + "properties": { + "literalArray": { + "type": "array", + "items": { + "type": "string" + } + }, + "path": { + "type": "string" + } + } + }, + "options": { + "type": "array", + "description": "An array of available options for the user to choose from.", + "items": { + "type": "object", + "properties": { + "label": { + "type": "object", + "description": "The text to display for this option. This can be a literal string or a reference to a value in the data model (e.g. '/option/label').", + "properties": { + "literalString": { + "type": "string" + }, + "path": { + "type": "string" + } + } + }, + "value": { + "type": "string", + "description": "The value to be associated with this option when selected." + } + }, + "required": ["label", "value"] + } + }, + "maxAllowedSelections": { + "type": "integer", + "description": "The maximum number of options that the user is allowed to select." + } + }, + "required": ["selections", "options"] + }, + "Slider": { + "type": "object", + "properties": { + "value": { + "type": "object", + "description": "The current value of the slider. This can be a literal number ('literalNumber') or a reference to a value in the data model ('path', e.g. '/restaurant/cost').", + "properties": { + "literalNumber": { + "type": "number" + }, + "path": { + "type": "string" + } + } + }, + "minValue": { + "type": "number", + "description": "The minimum value of the slider." + }, + "maxValue": { + "type": "number", + "description": "The maximum value of the slider." + } + }, + "required": ["value"] + } + } + } + }, + "required": ["id", "component"] + } + } + }, + "required": ["surfaceId", "components"] + }, + "dataModelUpdate": { + "type": "object", + "description": "Updates the data model for a surface.", + "properties": { + "surfaceId": { + "type": "string", + "description": "The unique identifier for the UI surface this data model update applies to." + }, + "path": { + "type": "string", + "description": "An optional path to a location within the data model (e.g., '/user/name'). If omitted, or set to '/', the entire data model will be replaced." + }, + "contents": { + "type": "array", + "description": "An array of data entries. Each entry must contain a 'key' and exactly one corresponding typed 'value*' property.", + "items": { + "type": "object", + "description": "A single data entry. Exactly one 'value*' property should be provided alongside the key.", + "properties": { + "key": { + "type": "string", + "description": "The key for this data entry." + }, + "valueString": { + "type": "string" + }, + "valueNumber": { + "type": "number" + }, + "valueBoolean": { + "type": "boolean" + }, + "valueMap": { + "description": "Represents a map as an adjacency list.", + "type": "array", + "items": { + "type": "object", + "description": "One entry in the map. Exactly one 'value*' property should be provided alongside the key.", + "properties": { + "key": { + "type": "string" + }, + "valueString": { + "type": "string" + }, + "valueNumber": { + "type": "number" + }, + "valueBoolean": { + "type": "boolean" + } + }, + "required": ["key"] + } + } + }, + "required": ["key"] + } + } + }, + "required": ["contents", "surfaceId"] + }, + "deleteSurface": { + "type": "object", + "description": "Signals the client to delete the surface identified by 'surfaceId'.", + "properties": { + "surfaceId": { + "type": "string", + "description": "The unique identifier for the UI surface to be deleted." + } + }, + "required": ["surfaceId"] + } + } +} +""" diff --git a/examples/agent/a2ui_agent/samples/agent/general_agent/skills/A2UI_response_generator/view_a2ui_examples.py b/examples/agent/a2ui_agent/samples/agent/general_agent/skills/A2UI_response_generator/view_a2ui_examples.py new file mode 100644 index 0000000000..b0497c7c35 --- /dev/null +++ b/examples/agent/a2ui_agent/samples/agent/general_agent/skills/A2UI_response_generator/view_a2ui_examples.py @@ -0,0 +1,125 @@ +# -*- coding: utf-8 -*- +# flake8: noqa: E501 +""" +A2UI Example Viewer - Tool for viewing A2UI UI template examples. + +This script provides a way to retrieve A2UI UI template examples. + +Usage: + # Load specific template name + python view_a2ui_examples.py --template_name SINGLE_COLUMN_LIST_WITH_IMAGE +""" +from UI_templete_examples import ( + SINGLE_COLUMN_LIST_WITH_IMAGE_EXAMPLE, + TWO_COLUMN_LIST_WITH_IMAGE_EXAMPLE, + SIMPLE_LIST_EXAMPLE, + BOOKING_FORM_WITH_IMAGE, + SEARCH_FILTER_FORM_EXAMPLE, + CONTACT_FORM_EXAMPLE, + EMAIL_COMPOSE_FORM_EXAMPLE, + SUCCESS_CONFIRMATION_WITH_IMAGE_EXAMPLE, + ERROR_MESSAGE_EXAMPLE, + INFO_MESSAGE_EXAMPLE, + ITEM_DETAIL_CARD_EXAMPLE_WITH_IMAGE, + PROFILE_VIEW_WITH_IMAGE_EXAMPLE, + SELECTION_CARD_EXAMPLE, + MULTIPLE_SELECTION_CARDS_EXAMPLE, +) + +# Template name to example mapping +TEMPLATE_MAP = { + "SINGLE_COLUMN_LIST_WITH_IMAGE": SINGLE_COLUMN_LIST_WITH_IMAGE_EXAMPLE, + "TWO_COLUMN_LIST_WITH_IMAGE": TWO_COLUMN_LIST_WITH_IMAGE_EXAMPLE, + "SIMPLE_LIST": SIMPLE_LIST_EXAMPLE, + "BOOKING_FORM_WITH_IMAGE": BOOKING_FORM_WITH_IMAGE, + "SEARCH_FILTER_FORM_WITH_IMAGE": SEARCH_FILTER_FORM_EXAMPLE, + "CONTACT_FORM_WITH_IMAGE": CONTACT_FORM_EXAMPLE, + "EMAIL_COMPOSE_FORM_WITH_IMAGE": EMAIL_COMPOSE_FORM_EXAMPLE, + "SUCCESS_CONFIRMATION_WITH_IMAGE": SUCCESS_CONFIRMATION_WITH_IMAGE_EXAMPLE, + "ERROR_MESSAGE": ERROR_MESSAGE_EXAMPLE, + "INFO_MESSAGE": INFO_MESSAGE_EXAMPLE, + "ITEM_DETAIL_CARD": ITEM_DETAIL_CARD_EXAMPLE_WITH_IMAGE, + "ITEM_DETAIL_CARD_WITH_IMAGE": ITEM_DETAIL_CARD_EXAMPLE_WITH_IMAGE, + "PROFILE_VIEW": PROFILE_VIEW_WITH_IMAGE_EXAMPLE, + "SELECTION_CARD": SELECTION_CARD_EXAMPLE, + "MULTIPLE_SELECTION_CARDS": MULTIPLE_SELECTION_CARDS_EXAMPLE, +} + + +def view_a2ui_examples(template_name: str) -> str: + """ + View A2UI UI template examples for generating UI responses. + + Args: + template_name: Specific template name to load. Options: + - SINGLE_COLUMN_LIST_WITH_IMAGE, TWO_COLUMN_LIST_WITH_IMAGE, + SIMPLE_LIST,SELECTION_CARD, + MULTIPLE_SELECTION_CARDS, + BOOKING_FORM_WITH_IMAGE, SEARCH_FILTER_FORM_WITH_IMAGE, + CONTACT_FORM_WITH_IMAGE, + EMAIL_COMPOSE_FORM_WITH_IMAGE,SUCCESS_CONFIRMATION_WITH_IMAGE, + ERROR_MESSAGE, INFO_MESSAGE, + ITEM_DETAIL_CARD, + ITEM_DETAIL_CARD_WITH_IMAGE, + PROFILE_VIEW + + Returns: + The requested template example. + + Examples: + # Load specific template + >>> view_a2ui_examples(template_name="BOOKING_FORM_WITH_IMAGE") + >>> view_a2ui_examples(template_name="SINGLE_COLUMN_LIST_WITH_IMAGE") + """ + if not template_name: + raise ValueError("template_name is required and cannot be empty") + + if template_name not in TEMPLATE_MAP: + raise ValueError(f"Unknown template name: {template_name}") + + example = TEMPLATE_MAP[template_name] + + return f""" +## A2UI Template: {template_name} + +{example} + +--- +Adapt this template to your specific data and styling requirements. +""" + + +# Tool metadata for AgentScope registration +TOOL_METADATA = { + "name": "view_a2ui_examples", + "description": "View A2UI UI template examples for generating UI responses.", + "parameters": { + "type": "object", + "properties": { + "template_name": { + "type": "string", + "description": "Specific template name to load", + }, + }, + "required": ["template_name"], + }, +} + + +if __name__ == "__main__": + import argparse + + parser = argparse.ArgumentParser( + description="View A2UI UI template examples for generating UI responses.", + ) + parser.add_argument( + "--template_name", + type=str, + required=True, + help="Specific template name to load", + ) + + args = parser.parse_args() + + res = view_a2ui_examples(template_name=args.template_name) + print(res) diff --git a/examples/agent/a2ui_agent/samples/agent/general_agent/skills/A2UI_response_generator/view_a2ui_schema.py b/examples/agent/a2ui_agent/samples/agent/general_agent/skills/A2UI_response_generator/view_a2ui_schema.py new file mode 100644 index 0000000000..bcb46d0de1 --- /dev/null +++ b/examples/agent/a2ui_agent/samples/agent/general_agent/skills/A2UI_response_generator/view_a2ui_schema.py @@ -0,0 +1,72 @@ +# -*- coding: utf-8 -*- +# flake8: noqa: E501 +""" +A2UI Schema Viewer - Tool for viewing A2UI schema. + +This script provides a way to retrieve the complete A2UI schema for +generating UI responses. + +Usage: + python view_a2ui_schema.py +""" + +from schema import A2UI_SCHEMA + + +def view_a2ui_schema(schema_category: str = "BASE_SCHEMA") -> str: + """ + View the complete A2UI schema for generating UI responses. + + This tool returns the complete A2UI JSON schema that defines all + available UI components and message types. + + Args: + schema_category: The category of the schema to view. Can be "BASE_SCHEMA". + + Returns: + The complete A2UI schema as a string. + """ + if schema_category == "BASE_SCHEMA": + return f""" +## A2UI JSON Schema + +The following is the complete A2UI schema for generating UI responses: + +{A2UI_SCHEMA} + +--- +Use this schema to construct valid A2UI JSON responses. +""" + else: + raise ValueError(f"Invalid schema category: {schema_category}") + + +# Tool metadata for AgentScope registration +TOOL_METADATA = { + "name": "view_a2ui_schema", + "description": "View the complete A2UI schema for generating UI responses.", + "parameters": { + "type": "object", + "properties": {}, + }, +} + + +if __name__ == "__main__": + import argparse + + parser = argparse.ArgumentParser( + description="View the complete A2UI schema for generating UI responses.", + ) + parser.add_argument( + "--schema_category", + type=str, + required=True, + help="The category of the schema to view. Can be 'BASE_SCHEMA'.", + choices=["BASE_SCHEMA"], + default="BASE_SCHEMA", + ) + args = parser.parse_args() + + res = view_a2ui_schema(schema_category=args.schema_category) + print(res) diff --git a/examples/agent/a2ui_agent/samples/client/a2a_client.py b/examples/agent/a2ui_agent/samples/client/a2a_client.py new file mode 100644 index 0000000000..f3d5bdc531 --- /dev/null +++ b/examples/agent/a2ui_agent/samples/client/a2a_client.py @@ -0,0 +1,169 @@ +# -*- coding: utf-8 -*- +"""A2A client example demonstrating agent card fetching and message sending.""" +import logging + +from typing import Any +from uuid import uuid4 + +import httpx + +from a2a.client import A2ACardResolver, A2AClient +from a2a.types import ( + AgentCard, + MessageSendParams, + SendMessageRequest, +) +from a2a.utils.constants import ( + AGENT_CARD_WELL_KNOWN_PATH, + EXTENDED_AGENT_CARD_PATH, +) + + +async def main() -> None: + """Main function demonstrating A2A client usage.""" + # Configure logging to show INFO level messages + logging.basicConfig(level=logging.INFO) + logger = logging.getLogger(__name__) # Get a logger instance + + # --8<-- [start:A2ACardResolver] + + base_url = "http://localhost:10002" + + async with httpx.AsyncClient( + timeout=httpx.Timeout(timeout=300.0), + ) as httpx_client: + # Initialize A2ACardResolver + resolver = A2ACardResolver( + httpx_client=httpx_client, + base_url=base_url, + # agent_card_path uses default, + # extended_agent_card_path also uses default + ) + # --8<-- [end:A2ACardResolver] + + # Fetch Public Agent Card and Initialize Client + final_agent_card_to_use: AgentCard | None = None + + try: + logger.info( + "Attempting to fetch public agent card from: %s%s", + base_url, + AGENT_CARD_WELL_KNOWN_PATH, + ) + _public_card = ( + await resolver.get_agent_card() + ) # Fetches from default public path + logger.info("Successfully fetched public agent card:") + logger.info( + _public_card.model_dump_json(indent=2, exclude_none=True), + ) + final_agent_card_to_use = _public_card + logger.info( + "\nUsing PUBLIC agent card for client initialization " + "(default).", + ) + + if _public_card.supports_authenticated_extended_card: + try: + logger.info( + "\nPublic card supports authenticated extended card. " + "Attempting to fetch from: %s%s", + base_url, + EXTENDED_AGENT_CARD_PATH, + ) + auth_headers_dict = { + "Authorization": ( + "Bearer dummy-token-for-extended-card" + ), + } + _extended_card = await resolver.get_agent_card( + relative_card_path=EXTENDED_AGENT_CARD_PATH, + http_kwargs={"headers": auth_headers_dict}, + ) + logger.info( + "Successfully fetched authenticated extended " + "agent card:", + ) + logger.info( + _extended_card.model_dump_json( + indent=2, + exclude_none=True, + ), + ) + final_agent_card_to_use = ( + _extended_card # Update to use the extended card + ) + logger.info( + "\nUsing AUTHENTICATED EXTENDED agent card " + "for client initialization.", + ) + except Exception as e_extended: + logger.warning( + "Failed to fetch extended agent card: %s. " + "Will proceed with public card.", + e_extended, + exc_info=True, + ) + elif ( + _public_card + ): # supports_authenticated_extended_card is False or None + logger.info( + "\nPublic card does not indicate support for an " + "extended card. Using public card.", + ) + + except Exception as e: + logger.error( + "Critical error fetching public agent card: %s", + e, + exc_info=True, + ) + raise RuntimeError( + "Failed to fetch the public agent card. Cannot continue.", + ) from e + + # --8<-- [start:send_message] + client = A2AClient( + httpx_client=httpx_client, + agent_card=final_agent_card_to_use, + ) + logger.info("A2AClient initialized.") + + send_message_payload: dict[str, Any] = { + "message": { + "role": "user", + "parts": [ + { + "kind": "text", + "text": "find top 5 chinese restaurants in new york", + }, + ], + "messageId": uuid4().hex, + }, + } + request = SendMessageRequest( + id=str(uuid4()), + params=MessageSendParams(**send_message_payload), + ) + + response = await client.send_message(request) + print(response.model_dump(mode="json", exclude_none=True)) + # --8<-- [end:send_message] + + # # --8<-- [start:send_message_streaming] + + # streaming_request = SendStreamingMessageRequest( + # id=str(uuid4()), params=MessageSendParams(**send_message_payload) + # ) + + # stream_response = client.send_message_streaming(streaming_request) + + # async for chunk in stream_response: + # print(chunk.model_dump(mode='json', exclude_none=True)) + # --8<-- [end:send_message_streaming] + + +if __name__ == "__main__": + import asyncio + + asyncio.run(main()) diff --git a/examples/agent/a2ui_agent/samples/client/lit/contact/README.md b/examples/agent/a2ui_agent/samples/client/lit/contact/README.md new file mode 100644 index 0000000000..9cea3880bd --- /dev/null +++ b/examples/agent/a2ui_agent/samples/client/lit/contact/README.md @@ -0,0 +1,30 @@ +# A2UI Generator + +This is a UI to generate and visualize A2UI responses. + +## Prerequisites + +1. [nodejs](https://nodejs.org/en) + +## Running + +This sample depends on the Lit renderer. Before running this sample, you need to build the renderer. + +1. **Build the renderer:** + ```bash + cd ../../../renderers/lit + npm install + npm run build + ``` + +2. **Run this sample:** + ```bash + cd - # back to the sample directory + npm install + ``` + +3. **Run the servers:** + - Run the [A2A server](../../../agent/adk/contact_lookup/) + - Run the dev server: `npm run dev` + +After starting the dev server, you can open http://localhost:5173/ to view the sample. \ No newline at end of file diff --git a/examples/agent/a2ui_agent/samples/client/lit/contact/client.ts b/examples/agent/a2ui_agent/samples/client/lit/contact/client.ts new file mode 100644 index 0000000000..a0c6348a79 --- /dev/null +++ b/examples/agent/a2ui_agent/samples/client/lit/contact/client.ts @@ -0,0 +1,65 @@ +/* + Copyright 2025 Google LLC + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import { v0_8 } from "@a2ui/lit"; +import { registerContactComponents } from "./ui/custom-components/register-components.js"; +type A2TextPayload = { + kind: "text"; + text: string; +}; + +type A2DataPayload = { + kind: "data"; + data: v0_8.Types.ServerToClientMessage; +}; + +type A2AServerPayload = + | Array + | { error: string }; + +export class A2UIClient { + #ready: Promise = Promise.resolve(); + get ready() { + return this.#ready; + } + + async send( + message: v0_8.Types.A2UIClientEventMessage + ): Promise { + const response = await fetch("/a2a", { + body: JSON.stringify(message), + method: "POST", + }); + + if (response.ok) { + const data = (await response.json()) as A2AServerPayload; + const messages: v0_8.Types.ServerToClientMessage[] = []; + if ("error" in data) { + throw new Error(data.error); + } else { + for (const item of data) { + if (item.kind === "text") continue; + messages.push(item.data); + } + } + return messages; + } + + const error = (await response.json()) as { error: string }; + throw new Error(error.error); + } +} +registerContactComponents(); diff --git a/examples/agent/a2ui_agent/samples/client/lit/contact/contact.ts b/examples/agent/a2ui_agent/samples/client/lit/contact/contact.ts new file mode 100644 index 0000000000..8db1028659 --- /dev/null +++ b/examples/agent/a2ui_agent/samples/client/lit/contact/contact.ts @@ -0,0 +1,376 @@ +/* + Copyright 2025 Google LLC + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import { SignalWatcher } from "@lit-labs/signals"; +import { provide } from "@lit/context"; +import { + LitElement, + html, + css, + nothing, + HTMLTemplateResult, + unsafeCSS, +} from "lit"; +import { customElement, state } from "lit/decorators.js"; +import { theme as uiTheme } from "./theme/theme.js"; +import { A2UIClient } from "./client.js"; +import { + SnackbarAction, + SnackbarMessage, + SnackbarUUID, + SnackType, +} from "./types/types.js"; +import { type Snackbar } from "./ui/snackbar.js"; +import { repeat } from "lit/directives/repeat.js"; +import { v0_8 } from "@a2ui/lit"; +import * as UI from "@a2ui/lit/ui"; + +// Demo elements. +import "./ui/ui.js"; +import { registerContactComponents } from "./ui/custom-components/register-components.js"; + +// Register custom components for the contact app +registerContactComponents(); + +@customElement("a2ui-contact") +export class A2UIContactFinder extends SignalWatcher(LitElement) { + @provide({ context: UI.Context.themeContext }) + accessor theme: v0_8.Types.Theme = uiTheme; + + @state() + accessor #requesting = false; + + @state() + accessor #error: string | null = null; + + @state() + accessor #lastMessages: v0_8.Types.ServerToClientMessage[] = []; + + static styles = [ + unsafeCSS(v0_8.Styles.structuralStyles), + css` + :host { + display: block; + max-width: 640px; + margin: 0 auto; + min-height: 100%; + } + + #surfaces { + display: flex; + flex-direction: column; + width: 100%; + padding: var(--bb-grid-size-3) 0; + animation: fadeIn 1s cubic-bezier(0, 0, 0.3, 1) 0.3s backwards; + + & a2ui-surface { + align-items: center; + } + } + + form { + display: flex; + flex-direction: column; + flex: 1; + gap: 16px; + align-items: center; + padding: 16px 0; + animation: fadeIn 1s cubic-bezier(0, 0, 0.3, 1) 1s backwards; + + & > div { + display: flex; + flex: 1; + gap: 16px; + align-items: center; + width: 100%; + + & > input { + display: block; + flex: 1; + border-radius: 32px; + padding: 16px 24px; + border: 1px solid var(--p-60); + font-size: 16px; + } + + & > button { + display: flex; + align-items: center; + background: var(--p-40); + color: var(--n-100); + border: none; + padding: 8px 16px; + border-radius: 32px; + opacity: 0.5; + + &:not([disabled]) { + cursor: pointer; + opacity: 1; + } + } + } + } + + .rotate { + animation: rotate 1s linear infinite; + } + + .pending { + width: 100%; + min-height: 200px; + display: flex; + align-items: center; + justify-content: center; + animation: fadeIn 1s cubic-bezier(0, 0, 0.3, 1) 0.3s backwards; + + & .g-icon { + margin-right: 8px; + } + } + + .error { + color: var(--e-40); + background-color: var(--e-95); + border: 1px solid var(--e-80); + padding: 16px; + border-radius: 8px; + } + + @keyframes fadeIn { + from { + opacity: 0; + } + + to { + opacity: 1; + } + } + + @keyframes rotate { + from { + rotate: 0deg; + } + + to { + rotate: 360deg; + } + } + `, + ]; + + #processor = v0_8.Data.createSignalA2uiMessageProcessor(); + #a2uiClient = new A2UIClient(); + #snackbar: Snackbar | undefined = undefined; + #pendingSnackbarMessages: Array<{ + message: SnackbarMessage; + replaceAll: boolean; + }> = []; + + render() { + return [ + this.#maybeRenderForm(), + this.#maybeRenderData(), + this.#maybeRenderError(), + ]; + } + + #maybeRenderError() { + if (!this.#error) return nothing; + + return html`
${this.#error}
`; + } + + #maybeRenderForm() { + if (this.#requesting) return nothing; + if (this.#lastMessages.length > 0) return nothing; + return html`
{ + evt.preventDefault(); + if (!(evt.target instanceof HTMLFormElement)) { + return; + } + const data = new FormData(evt.target); + const body = data.get("body") ?? null; + if (!body) { + return; + } + const message = body as v0_8.Types.A2UIClientEventMessage; + await this.#sendAndProcessMessage(message); + }} + > +

+ Contact Finder +

+
+ + +
+
`; + } + + #maybeRenderData() { + if (this.#requesting) { + return html`
+ progress_activity + Awaiting an answer... +
`; + } + + const surfaces = this.#processor.getSurfaces(); + if (surfaces.size === 0) { + return nothing; + } + + return html`
+ ${repeat( + this.#processor.getSurfaces(), + ([surfaceId]) => surfaceId, + ([surfaceId, surface]) => { + return html` + ) => { + const [target] = evt.composedPath(); + if (!(target instanceof HTMLElement)) { + return; + } + + const context: v0_8.Types.A2UIClientEventMessage["userAction"]["context"] = + {}; + if (evt.detail.action.context) { + const srcContext = evt.detail.action.context; + for (const item of srcContext) { + if (item.value.literalBoolean) { + context[item.key] = item.value.literalBoolean; + } else if (item.value.literalNumber) { + context[item.key] = item.value.literalNumber; + } else if (item.value.literalString) { + context[item.key] = item.value.literalString; + } else if (item.value.path) { + const path = this.#processor.resolvePath( + item.value.path, + evt.detail.dataContextPath + ); + const value = this.#processor.getData( + evt.detail.sourceComponent, + path, + surfaceId + ); + context[item.key] = value; + } + } + } + + const message: v0_8.Types.A2UIClientEventMessage = { + userAction: { + surfaceId: surfaceId, + name: evt.detail.action.name, + sourceComponentId: target.id, + timestamp: new Date().toISOString(), + context, + }, + }; + + await this.#sendAndProcessMessage(message); + }} + .surfaceId=${surfaceId} + .surface=${surface} + .processor=${this.#processor} + >`; + } + )} +
`; + } + + async #sendAndProcessMessage(request) { + const messages = await this.#sendMessage(request); + + this.#lastMessages = messages; + this.#processor.clearSurfaces(); + this.#processor.processMessages(messages); + } + + async #sendMessage( + message: v0_8.Types.A2UIClientEventMessage + ): Promise { + try { + this.#requesting = true; + const response = this.#a2uiClient.send(message); + await response; + this.#requesting = false; + + return response; + } catch (err) { + this.snackbar(err as string, SnackType.ERROR); + } finally { + this.#requesting = false; + } + + return []; + } + + snackbar( + message: string | HTMLTemplateResult, + type: SnackType, + actions: SnackbarAction[] = [], + persistent = false, + id = globalThis.crypto.randomUUID(), + replaceAll = false + ) { + if (!this.#snackbar) { + this.#pendingSnackbarMessages.push({ + message: { + id, + message, + type, + persistent, + actions, + }, + replaceAll, + }); + return; + } + + return this.#snackbar.show( + { + id, + message, + type, + persistent, + actions, + }, + replaceAll + ); + } + + unsnackbar(id?: SnackbarUUID) { + if (!this.#snackbar) { + return; + } + + this.#snackbar.hide(id); + } +} diff --git a/examples/agent/a2ui_agent/samples/client/lit/contact/events/events.ts b/examples/agent/a2ui_agent/samples/client/lit/contact/events/events.ts new file mode 100644 index 0000000000..0ab66c3b76 --- /dev/null +++ b/examples/agent/a2ui_agent/samples/client/lit/contact/events/events.ts @@ -0,0 +1,35 @@ +/* + Copyright 2025 Google LLC + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import { HTMLTemplateResult } from "lit"; + +const eventInit = { + bubbles: true, + cancelable: true, + composed: true, +}; + +export class SnackbarActionEvent extends Event { + static eventName = "snackbaraction"; + + constructor( + public readonly action: string, + public readonly value?: HTMLTemplateResult | string, + public readonly callback?: () => void + ) { + super(SnackbarActionEvent.eventName, { ...eventInit }); + } +} diff --git a/examples/agent/a2ui_agent/samples/client/lit/contact/index.html b/examples/agent/a2ui_agent/samples/client/lit/contact/index.html new file mode 100644 index 0000000000..5dc8a050b1 --- /dev/null +++ b/examples/agent/a2ui_agent/samples/client/lit/contact/index.html @@ -0,0 +1,196 @@ + + + + + + + + Contact Finder Agent + + + + + + + + + + diff --git a/examples/agent/a2ui_agent/samples/client/lit/contact/middleware/a2a.ts b/examples/agent/a2ui_agent/samples/client/lit/contact/middleware/a2a.ts new file mode 100644 index 0000000000..fc71c7b5f6 --- /dev/null +++ b/examples/agent/a2ui_agent/samples/client/lit/contact/middleware/a2a.ts @@ -0,0 +1,153 @@ +/* + Copyright 2025 Google LLC + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import { IncomingMessage, ServerResponse } from "http"; +import { Plugin, ViteDevServer } from "vite"; +import { A2AClient } from "@a2a-js/sdk/client"; +import { + MessageSendParams, + Part, + SendMessageSuccessResponse, + Task, +} from "@a2a-js/sdk"; +import { v4 as uuidv4 } from "uuid"; + +const A2AUI_MIME_TYPE = "application/json+a2aui"; + +const fetchWithCustomHeader: typeof fetch = async (url, init) => { + const headers = new Headers(init?.headers); + headers.set("X-A2A-Extensions", "https://a2ui.org/a2a-extension/a2ui/v0.8"); + + const newInit = { ...init, headers }; + return fetch(url, newInit); +}; + +const isJson = (str: string) => { + try { + const parsed = JSON.parse(str); + return ( + typeof parsed === "object" && parsed !== null && !Array.isArray(parsed) + ); + } catch (err) { + console.warn(err); + return false; + } +}; + +let client: A2AClient | null = null; +const createOrGetClient = async () => { + if (!client) { + // Create a client pointing to the agent's Agent Card URL. + client = await A2AClient.fromCardUrl( + "http://localhost:10002/.well-known/agent-card.json", + { fetchImpl: fetchWithCustomHeader } + ); + } + + return client; +}; + +export const plugin = (): Plugin => { + return { + name: "a2a-handler", + configureServer(server: ViteDevServer) { + server.middlewares.use( + "/a2a", + async (req: IncomingMessage, res: ServerResponse, next: () => void) => { + if (req.method === "POST") { + let originalBody = ""; + + req.on("data", (chunk) => { + originalBody += chunk.toString(); + }); + + req.on("end", async () => { + let sendParams: MessageSendParams; + + if (isJson(originalBody)) { + console.log( + "[a2a-middleware] Received JSON UI event:", + originalBody + ); + + const clientEvent = JSON.parse(originalBody); + sendParams = { + message: { + messageId: uuidv4(), + role: "user", + parts: [ + { + kind: "data", + data: clientEvent, + metadata: { 'mimeType': A2AUI_MIME_TYPE }, + } as Part, + ], + kind: "message", + }, + }; + } else { + console.log( + "[a2a-middleware] Received text query:", + originalBody + ); + sendParams = { + message: { + messageId: uuidv4(), + role: "user", + parts: [ + { + kind: "text", + text: originalBody, + }, + ], + kind: "message", + }, + }; + } + + const client = await createOrGetClient(); + const response = await client.sendMessage(sendParams); + if ("error" in response) { + console.error("Error:", response.error.message); + res.statusCode = 500; + res.setHeader("Content-Type", "application/json"); + res.end(JSON.stringify({ error: response.error.message })); + return; + } else { + const result = (response as SendMessageSuccessResponse) + .result as Task; + if (result.kind === "task") { + res.statusCode = 200; + res.setHeader("Content-Type", "application/json"); + res.end(JSON.stringify(result.status.message?.parts)); + return; + } + } + + res.statusCode = 200; + res.setHeader("Content-Type", "application/json"); + res.end(JSON.stringify([])); + }); + + return; + } else { + next(); + } + } + ); + }, + }; +}; diff --git a/examples/agent/a2ui_agent/samples/client/lit/contact/middleware/index.ts b/examples/agent/a2ui_agent/samples/client/lit/contact/middleware/index.ts new file mode 100644 index 0000000000..4ecec532c4 --- /dev/null +++ b/examples/agent/a2ui_agent/samples/client/lit/contact/middleware/index.ts @@ -0,0 +1,17 @@ +/* + Copyright 2025 Google LLC + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +export * as A2AMiddleware from "./a2a.js"; diff --git a/examples/agent/a2ui_agent/samples/client/lit/contact/package.json b/examples/agent/a2ui_agent/samples/client/lit/contact/package.json new file mode 100644 index 0000000000..58da6a441b --- /dev/null +++ b/examples/agent/a2ui_agent/samples/client/lit/contact/package.json @@ -0,0 +1,84 @@ +{ + "name": "@a2ui/contact", + "private": true, + "version": "0.8.1", + "description": "A2UI Contact Demo", + "main": "./dist/contact.js", + "types": "./dist/contact.d.ts", + "type": "module", + "scripts": { + "prepack": "npm run build", + "build": "wireit", + "build:tsc": "wireit", + "dev": "npm run serve --watch", + "test": "wireit", + "serve": "wireit" + }, + "wireit": { + "serve": { + "command": "vite dev", + "dependencies": [ + "build" + ], + "service": true + }, + "test": { + "command": "node --test --enable-source-maps --test-reporter spec dist/src/0.8/tests/**/*.test.js", + "dependencies": [ + "build" + ] + }, + "build": { + "dependencies": [ + "build:tsc" + ] + }, + "build:tsc": { + "command": "tsc -b --pretty", + "env": { + "FORCE_COLOR": "1" + }, + "dependencies": [ + "../../../../renderers/lit:build:tsc" + ], + "files": [ + "**/*.ts", + "tsconfig.json" + ], + "output": [ + "dist/", + "!dist/**/*.min.js{,.map}" + ], + "clean": "if-file-deleted" + } + }, + "repository": { + "directory": "samples/client/lit/contact", + "type": "git", + "url": "git+https://github.com/google/A2UI.git" + }, + "files": [ + "dist" + ], + "keywords": [], + "author": "Google", + "license": "Apache-2.0", + "bugs": { + "url": "https://github.com/google/A2UI/issues" + }, + "homepage": "https://github.com/google/A2UI/tree/main/web#readme", + "devDependencies": { + "dotenv": "^17.2.3", + "typescript": "^5.8.3", + "uuid": "^13.0.0", + "vite": "^7.1.11", + "wireit": "^0.15.0-pre.2" + }, + "dependencies": { + "@a2a-js/sdk": "^0.3.4", + "@a2ui/lit": "file:../../../../renderers/lit", + "@lit-labs/signals": "^0.1.3", + "@lit/context": "^1.1.4", + "lit": "^3.3.1" + } +} diff --git a/examples/agent/a2ui_agent/samples/client/lit/contact/theme/theme.ts b/examples/agent/a2ui_agent/samples/client/lit/contact/theme/theme.ts new file mode 100644 index 0000000000..e9aef950bb --- /dev/null +++ b/examples/agent/a2ui_agent/samples/client/lit/contact/theme/theme.ts @@ -0,0 +1,463 @@ +/* + Copyright 2025 Google LLC + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import { v0_8 } from "@a2ui/lit"; + +/** Elements */ + +const a = { + "typography-f-sf": true, + "typography-fs-n": true, + "typography-w-500": true, + "layout-as-n": true, + "layout-dis-iflx": true, + "layout-al-c": true, +}; + +const audio = { + "layout-w-100": true, +}; + +const body = { + "typography-f-s": true, + "typography-fs-n": true, + "typography-w-400": true, + "layout-mt-0": true, + "layout-mb-2": true, + "typography-sz-bm": true, + "color-c-n10": true, +}; + +const button = { + "typography-f-sf": true, + "typography-fs-n": true, + "typography-w-500": true, + "layout-pt-3": true, + "layout-pb-3": true, + "layout-pl-5": true, + "layout-pr-5": true, + "layout-mb-1": true, + "border-br-16": true, + "border-bw-0": true, + "border-c-n70": true, + "border-bs-s": true, + "color-bgc-s30": true, + "color-c-n100": true, + "behavior-ho-80": true, +}; + +const heading = { + "typography-f-sf": true, + "typography-fs-n": true, + "typography-w-500": true, + "layout-mt-0": true, + "layout-mb-2": true, + "color-c-n10": true, +}; + +const h1 = { + ...heading, + "typography-sz-tl": true, +}; + +const h2 = { + ...heading, + "typography-sz-tm": true, +}; + +const h3 = { + ...heading, + "typography-sz-ts": true, +}; + +const iframe = { + "behavior-sw-n": true, +}; + +const input = { + "typography-f-sf": true, + "typography-fs-n": true, + "typography-w-400": true, + "layout-pl-4": true, + "layout-pr-4": true, + "layout-pt-2": true, + "layout-pb-2": true, + "border-br-6": true, + "border-bw-1": true, + "color-bc-s70": true, + "border-bs-s": true, + "layout-as-n": true, + "color-c-n10": true, +}; + +const p = { + "typography-f-s": true, + "typography-fs-n": true, + "typography-w-400": true, + "layout-m-0": true, + "typography-sz-bm": true, + "layout-as-n": true, + "color-c-n10": true, +}; + +const orderedList = { + "typography-f-s": true, + "typography-fs-n": true, + "typography-w-400": true, + "layout-m-0": true, + "typography-sz-bm": true, + "layout-as-n": true, +}; + +const unorderedList = { + "typography-f-s": true, + "typography-fs-n": true, + "typography-w-400": true, + "layout-m-0": true, + "typography-sz-bm": true, + "layout-as-n": true, +}; + +const listItem = { + "typography-f-s": true, + "typography-fs-n": true, + "typography-w-400": true, + "layout-m-0": true, + "typography-sz-bm": true, + "layout-as-n": true, +}; + +const pre = { + "typography-f-c": true, + "typography-fs-n": true, + "typography-w-400": true, + "typography-sz-bm": true, + "typography-ws-p": true, + "layout-as-n": true, +}; + +const textarea = { + ...input, + "layout-r-none": true, + "layout-fs-c": true, +}; + +const video = { + "layout-el-cv": true, +}; + +const aLight = v0_8.Styles.merge(a, { "color-c-p30": true }); +const inputLight = v0_8.Styles.merge(input, { "color-c-n5": true }); +const textareaLight = v0_8.Styles.merge(textarea, { "color-c-n5": true }); +const buttonLight = v0_8.Styles.merge(button, { "color-c-n100": true }); +const h1Light = v0_8.Styles.merge(h1, { "color-c-n5": true }); +const h2Light = v0_8.Styles.merge(h2, { "color-c-n5": true }); +const h3Light = v0_8.Styles.merge(h3, { "color-c-n5": true }); +const bodyLight = v0_8.Styles.merge(body, { "color-c-n5": true }); +const pLight = v0_8.Styles.merge(p, { "color-c-n60": true }); +const preLight = v0_8.Styles.merge(pre, { "color-c-n35": true }); +const orderedListLight = v0_8.Styles.merge(orderedList, { + "color-c-n35": true, +}); +const unorderedListLight = v0_8.Styles.merge(unorderedList, { + "color-c-n35": true, +}); +const listItemLight = v0_8.Styles.merge(listItem, { + "color-c-n35": true, +}); + +export const theme: v0_8.Types.Theme = { + additionalStyles: { + Card: { + "min-width": "320px", + }, + Button: { + "--n-60": "var(--n-100)", + }, + Image: { + "max-width": "120px", + "max-height": "120px", + marginLeft: "auto", + marginRight: "auto", + }, + }, + components: { + AudioPlayer: {}, + Button: { + "layout-pt-2": true, + "layout-pb-2": true, + "layout-pl-5": true, + "layout-pr-5": true, + "border-br-2": true, + "border-bw-0": true, + "border-bs-s": true, + "color-bgc-p30": true, + "color-c-n100": true, + "behavior-ho-70": true, + }, + Card: { + "border-br-4": true, + "color-bgc-p100": true, + "color-bc-n90": true, + "border-bw-1": true, + "border-bs-s": true, + "layout-pt-10": true, + "layout-pb-10": true, + "layout-pl-4": true, + "layout-pr-4": true, + }, + CheckBox: { + element: { + "layout-m-0": true, + "layout-mr-2": true, + "layout-p-2": true, + "border-br-12": true, + "border-bw-1": true, + "border-bs-s": true, + "color-bgc-p100": true, + "color-bc-p60": true, + "color-c-n30": true, + "color-c-p30": true, + }, + label: { + "color-c-p30": true, + "typography-f-sf": true, + "typography-v-r": true, + "typography-w-400": true, + "layout-flx-1": true, + "typography-sz-ll": true, + }, + container: { + "layout-dsp-iflex": true, + "layout-al-c": true, + }, + }, + Column: {}, + DateTimeInput: { + container: {}, + label: {}, + element: { + "layout-pt-2": true, + "layout-pb-2": true, + "layout-pl-3": true, + "layout-pr-3": true, + "border-br-12": true, + "border-bw-1": true, + "border-bs-s": true, + "color-bgc-p100": true, + "color-bc-p60": true, + "color-c-n30": true, + }, + }, + Divider: { + "color-bgc-n90": true, + "layout-mt-6": true, + "layout-mb-6": true, + }, + Image: { + all: { + "border-br-50pc": true, + "layout-el-cv": true, + "layout-w-100": true, + "layout-h-100": true, + "layout-dsp-flexhor": true, + "layout-al-c": true, + "layout-sp-c": true, + "layout-mb-3": true, + }, + avatar: {}, + header: {}, + icon: {}, + largeFeature: {}, + mediumFeature: {}, + smallFeature: {}, + }, + Icon: { + "border-br-1": true, + "layout-p-2": true, + "color-bgc-n98": true, + "layout-dsp-flexhor": true, + "layout-al-c": true, + "layout-sp-c": true, + }, + List: { + "layout-g-4": true, + "layout-p-2": true, + }, + Modal: { + backdrop: { "color-bbgc-p60_20": true }, + element: { + "border-br-2": true, + "color-bgc-p100": true, + "layout-p-4": true, + "border-bw-1": true, + "border-bs-s": true, + "color-bc-p80": true, + }, + }, + MultipleChoice: { + container: {}, + label: {}, + element: {}, + }, + Row: { + "layout-g-4": true, + "layout-mb-3": true, + }, + Slider: { + container: {}, + label: {}, + element: {}, + }, + Tabs: { + container: {}, + controls: { all: {}, selected: {} }, + element: {}, + }, + Text: { + all: { + "layout-w-100": true, + "layout-g-2": true, + "color-c-p30": true, + }, + h1: { + "typography-f-sf": true, + "typography-ta-c": true, + "typography-v-r": true, + "typography-w-500": true, + "layout-mt-0": true, + "layout-mr-0": true, + "layout-ml-0": true, + "layout-mb-2": true, + "layout-p-0": true, + "typography-sz-tl": true, + }, + h2: { + "typography-f-sf": true, + "typography-ta-c": true, + "typography-v-r": true, + "typography-w-500": true, + "layout-mt-0": true, + "layout-mr-0": true, + "layout-ml-0": true, + "layout-mb-2": true, + "layout-p-0": true, + "typography-sz-tl": true, + }, + h3: { + "typography-f-sf": true, + "typography-ta-c": true, + "typography-v-r": true, + "typography-w-500": true, + "layout-mt-0": true, + "layout-mr-0": true, + "layout-ml-0": true, + "layout-mb-0": true, + "layout-p-0": true, + "typography-sz-ts": true, + }, + h4: { + "typography-f-sf": true, + "typography-ta-c": true, + "typography-v-r": true, + "typography-w-500": true, + "layout-mt-0": true, + "layout-mr-0": true, + "layout-ml-0": true, + "layout-mb-0": true, + "layout-p-0": true, + "typography-sz-bl": true, + }, + h5: { + "typography-f-sf": true, + "typography-ta-c": true, + "typography-v-r": true, + "typography-w-500": true, + "layout-mt-0": true, + "layout-mr-0": true, + "layout-ml-0": true, + "layout-mb-0": true, + "layout-p-0": true, + "color-c-n30": true, + "typography-sz-bm": true, + "layout-mb-1": true, + }, + body: {}, + caption: {}, + }, + TextField: { + container: { + "typography-sz-bm": true, + "layout-w-100": true, + "layout-g-2": true, + "layout-dsp-flexhor": true, + "layout-al-c": true, + }, + label: { + "layout-flx-0": true, + }, + element: { + "typography-sz-bm": true, + "layout-pt-2": true, + "layout-pb-2": true, + "layout-pl-3": true, + "layout-pr-3": true, + "border-br-12": true, + "border-bw-1": true, + "border-bs-s": true, + "color-bgc-p100": true, + "color-bc-p60": true, + "color-c-n30": true, + "color-c-p30": true, + }, + }, + Video: { + "border-br-5": true, + "layout-el-cv": true, + }, + }, + elements: { + a: aLight, + audio, + body: bodyLight, + button: buttonLight, + h1: h1Light, + h2: h2Light, + h3: h3Light, + h4: {}, + h5: {}, + iframe, + input: inputLight, + p: pLight, + pre: preLight, + textarea: textareaLight, + video, + }, + markdown: { + p: [...Object.keys(pLight)], + h1: [...Object.keys(h1Light)], + h2: [...Object.keys(h2Light)], + h3: [...Object.keys(h3Light)], + h4: [], + h5: [], + ul: [...Object.keys(unorderedListLight)], + ol: [...Object.keys(orderedListLight)], + li: [...Object.keys(listItemLight)], + a: [...Object.keys(aLight)], + strong: [], + em: [], + }, +}; diff --git a/examples/agent/a2ui_agent/samples/client/lit/contact/tsconfig.json b/examples/agent/a2ui_agent/samples/client/lit/contact/tsconfig.json new file mode 100644 index 0000000000..dd157690fe --- /dev/null +++ b/examples/agent/a2ui_agent/samples/client/lit/contact/tsconfig.json @@ -0,0 +1,30 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + + "compilerOptions": { + "composite": false, + "declaration": true, + "declarationMap": true, + "incremental": true, + "forceConsistentCasingInFileNames": true, + "inlineSources": false, + "preserveWatchOutput": true, + "sourceMap": true, + "target": "es2022", + "module": "es2022", + "lib": ["ESNext", "DOM", "DOM.Iterable"], + "skipLibCheck": true, + "useDefineForClassFields": false, + "rootDir": ".", + "outDir": "dist", + "tsBuildInfoFile": "dist/.tsbuildinfo", + "moduleResolution": "bundler", + "resolveJsonModule": true, + "isolatedModules": true, + "strict": false, + "noUnusedLocals": false, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true + }, + "references": [{ "path": "../../../../renderers/lit" }] +} diff --git a/examples/agent/a2ui_agent/samples/client/lit/contact/types/types.ts b/examples/agent/a2ui_agent/samples/client/lit/contact/types/types.ts new file mode 100644 index 0000000000..90644ce08d --- /dev/null +++ b/examples/agent/a2ui_agent/samples/client/lit/contact/types/types.ts @@ -0,0 +1,42 @@ +/* + Copyright 2025 Google LLC + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import { HTMLTemplateResult } from "lit"; + +export enum SnackType { + NONE = "none", + INFORMATION = "information", + WARNING = "warning", + ERROR = "error", + PENDING = "pending", +} + +export type SnackbarUUID = ReturnType; + +export type SnackbarAction = { + title: string; + action: string; + value?: HTMLTemplateResult | string; + callback?: () => void; +}; + +export type SnackbarMessage = { + id: SnackbarUUID; + type: SnackType; + persistent: boolean; + message: string | HTMLTemplateResult; + actions?: SnackbarAction[]; +}; diff --git a/examples/agent/a2ui_agent/samples/client/lit/contact/ui/custom-components/README.md b/examples/agent/a2ui_agent/samples/client/lit/contact/ui/custom-components/README.md new file mode 100644 index 0000000000..18f6111322 --- /dev/null +++ b/examples/agent/a2ui_agent/samples/client/lit/contact/ui/custom-components/README.md @@ -0,0 +1,167 @@ +# A2UI custom component integration guide + +This guide details how to create, register, and use a custom component in the A2UI client. + +## Create the component + +Create a new Lit component file in `lib/src/0.8/ui/custom-components/`. +Example: `my-component.ts` + +```typescript +import { html, css } from "lit"; +import { property } from "lit/decorators.js"; + +import { Root } from "../root.js"; + +export class MyComponent extends Root { + @property() accessor myProp: string = "Default"; + + static styles = [ + ...Root.styles, // Inherit base styles + css` + :host { + display: block; + padding: 16px; + border: 1px solid #ccc; + } + `, + ]; + + render() { + return html` +
+

My Custom Component

+

Prop value: ${this.myProp}

+
+ `; + } +} +``` + +## Register the component + +Update `lib/src/0.8/ui/custom-components/index.ts` to register your new component. +You must pass the desired tag name as the third argument. + +```typescript +import { componentRegistry } from "../component-registry.js"; +import { MyComponent } from "./my-component.js"; // Import your component + +export function registerCustomComponents() { + // Register with explicit tag name + componentRegistry.register("MyComponent", MyComponent, "my-component"); +} + +export { MyComponent }; // Export for type usage if needed +``` + +## Define the schema (server-side) + +Create a JSON schema for your component properties. This will be used by the server to validate messages. +Example: `lib/my_component_schema.json` + +```json +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "properties": { + "type": { "const": "object" }, + "properties": { + "type": "object", + "additionalProperties": false, + "properties": { + "myProp": { + "type": "string", + "description": "A sample property." + } + }, + "required": ["myProp"] + } + }, + "required": ["type", "properties"] +} +``` + +## Use in client application + +In your client application (e.g., `contact` sample), ensure you import and call the registration function. + +```typescript +import { registerCustomComponents } from "@a2ui/lit/ui"; + +// Call this once at startup +registerCustomComponents(); +``` + +## Overriding standard components + +You can replace standard A2UI components (like `TextField`, `Video`, `Button`) with your own custom implementations. + +### Steps to override + +1. **Create your component** extending `Root` (just like a custom component). + +2. **Ensure it accepts the standard properties** for that component type (e.g., `label` and `text` for `TextField`). + +3. **Register it** using the **standard type name** (e.g., `"TextField"`). + + ```typescript + // 1. Define your override + class MyPremiumTextField extends Root { + @property() accessor label = ""; + @property() accessor text = ""; + + static styles = [ + ...Root.styles, + css` + /* your premium styles */ + `, + ]; + + render() { + return html` +
+ + +
+ `; + } + } + + // 2. Register with the STANDARD type name + import { componentRegistry } from "@a2ui/lit/ui"; + componentRegistry.register( + "TextField", + MyPremiumTextField, + "my-premium-textfield" + ); + ``` + +**Result:** +When the server sends a `TextField` component, the client will now render `` instead of the default ``. + +## Verify + +You can verify the component by creating a simple HTML test file or by sending a server message with the new component type. + +**Server message example:** + +```json +{ + "surfaceId": "main", + "component": { + "type": "MyComponent", + "id": "comp-1", + "properties": { + "myProp": "Hello World" + } + } +} +``` + +## Troubleshooting + +- **`NotSupportedError`**: If you see "constructor has already been used", ensure you **removed** the `@customElement` decorator from your component class. +- **Component not rendering**: Check if `registerCustomComponents()` is actually called. Verify the tag name in the DOM matches what you registered (e.g., `` vs ``). +- **Styles missing**: Ensure `static styles` includes `...Root.styles`. diff --git a/examples/agent/a2ui_agent/samples/client/lit/contact/ui/custom-components/org-chart.ts b/examples/agent/a2ui_agent/samples/client/lit/contact/ui/custom-components/org-chart.ts new file mode 100644 index 0000000000..85401e2c99 --- /dev/null +++ b/examples/agent/a2ui_agent/samples/client/lit/contact/ui/custom-components/org-chart.ts @@ -0,0 +1,160 @@ +/* + Copyright 2025 Google LLC + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import { Root } from '@a2ui/lit/ui'; +import { v0_8 } from '@a2ui/lit'; +import { html, css, TemplateResult } from 'lit'; +import { customElement, property } from 'lit/decorators.js'; +import { map } from 'lit/directives/map.js'; + +// Use aliases for convenience +const StateEvent = v0_8.Events.StateEvent; +type Action = v0_8.Types.Action; + +export interface OrgChartNode { + title: string; + name: string; +} + +@customElement('org-chart') +export class OrgChart extends Root { + @property({ type: Array }) accessor chain: OrgChartNode[] = []; + @property({ type: Object }) accessor action: Action | null = null; + + static styles = [ + ...Root.styles, + css` + :host { + display: block; + padding: 16px; + font-family: 'Roboto', sans-serif; + } + + .container { + display: flex; + flex-direction: column; + align-items: center; + gap: 16px; + } + + .node { + display: flex; + flex-direction: column; + align-items: center; + padding: 12px 24px; + background: #fff; + border: 1px solid #e0e0e0; + border-radius: 8px; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05); + min-width: 200px; + position: relative; + transition: transform 0.2s, box-shadow 0.2s; + cursor: pointer; + } + + .node:hover { + transform: translateY(-2px); + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); + } + + .node:focus { + outline: 2px solid #1a73e8; + outline-offset: 2px; + } + + .node.current { + background: #e8f0fe; + border-color: #1a73e8; + border-width: 2px; + } + + .title { + font-size: 0.85rem; + color: #5f6368; + text-transform: uppercase; + letter-spacing: 0.5px; + margin-bottom: 4px; + } + + .name { + font-size: 1.1rem; + font-weight: 500; + color: #202124; + } + + .arrow { + color: #9aa0a6; + font-size: 24px; + line-height: 1; + } + `]; + + render() { + if (!this.chain || this.chain.length === 0) { + return html`
No hierarchy data
`; + } + + return html` +
+ ${map(this.chain, (node, index) => { + const isLast = index === this.chain.length - 1; + return html` + + ${!isLast ? html`
` : ''} + `; + })} +
+ `; + } + + private handleNodeClick(node: OrgChartNode) { + if (!this.action) return; + + // Create a new action with the node's context merged in + const newContext = [ + ...(this.action.context || []), + { + key: 'clickedNodeTitle', + value: { literalString: node.title } + }, + { + key: 'clickedNodeName', + value: { literalString: node.name } + } + ]; + + const actionWithContext: Action = { + ...this.action, + context: newContext as Action['context'] + }; + + const evt = new StateEvent<"a2ui.action">({ + eventType: "a2ui.action", + action: actionWithContext, + dataContextPath: this.dataContextPath, + sourceComponentId: this.id, + sourceComponent: this.component, + }); + this.dispatchEvent(evt); + } +} diff --git a/examples/agent/a2ui_agent/samples/client/lit/contact/ui/custom-components/premium-text-field.ts b/examples/agent/a2ui_agent/samples/client/lit/contact/ui/custom-components/premium-text-field.ts new file mode 100644 index 0000000000..968b0fbfdb --- /dev/null +++ b/examples/agent/a2ui_agent/samples/client/lit/contact/ui/custom-components/premium-text-field.ts @@ -0,0 +1,100 @@ +/* + Copyright 2025 Google LLC + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import { Root } from '@a2ui/lit/ui'; +import { html, css } from 'lit'; +import { property } from 'lit/decorators.js'; + +export class PremiumTextField extends Root { + @property() accessor label = ''; + @property() accessor text = ''; + + static styles = [ + ...Root.styles, + css` + :host { + display: block; + padding: 16px; + background: #fff; + border-radius: 12px; + box-shadow: 0 4px 12px rgba(0,0,0,0.08); + border: 1px solid #e0e0e0; + transition: all 0.2s ease; + font-family: 'Inter', sans-serif; + } + :host(:hover) { + transform: translateY(-2px); + box-shadow: 0 6px 16px rgba(0,0,0,0.12); + } + .input-container { + position: relative; + margin-top: 8px; + } + input { + width: 100%; + padding: 12px 16px; + font-size: 16px; + border: 2px solid #e0e0e0; + border-radius: 8px; + outline: none; + transition: border-color 0.2s; + box-sizing: border-box; + background: #fafafa; + } + input:focus { + border-color: #6200ee; + background: #fff; + } + label { + display: block; + font-size: 14px; + font-weight: 600; + color: #333; + margin-bottom: 4px; + } + .hint { + margin-top: 8px; + font-size: 12px; + color: #666; + display: flex; + align-items: center; + gap: 4px; + } + .badge { + background: #6200ee; + color: white; + padding: 2px 6px; + border-radius: 4px; + font-size: 10px; + font-weight: bold; + text-transform: uppercase; + } + ` + ]; + + render() { + return html` + +
+ +
+
+ Custom + This is a premium override of the standard TextField. +
+ `; + } +} diff --git a/examples/agent/a2ui_agent/samples/client/lit/contact/ui/custom-components/register-components.ts b/examples/agent/a2ui_agent/samples/client/lit/contact/ui/custom-components/register-components.ts new file mode 100644 index 0000000000..0b77b6ca5e --- /dev/null +++ b/examples/agent/a2ui_agent/samples/client/lit/contact/ui/custom-components/register-components.ts @@ -0,0 +1,33 @@ +/* + Copyright 2025 Google LLC + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import { componentRegistry } from "@a2ui/lit/ui"; +import { OrgChart } from "./org-chart.js"; +import { PremiumTextField } from "./premium-text-field.js"; + +export function registerContactComponents() { + // Register OrgChart + componentRegistry.register("OrgChart", OrgChart, "org-chart"); + + // Register PremiumTextField as an override for TextField + componentRegistry.register( + "TextField", + PremiumTextField, + "premium-text-field" + ); + + console.log("Registered Contact App Custom Components"); +} diff --git a/examples/agent/a2ui_agent/samples/client/lit/contact/ui/custom-components/test/README.md b/examples/agent/a2ui_agent/samples/client/lit/contact/ui/custom-components/test/README.md new file mode 100644 index 0000000000..f4277bfeb5 --- /dev/null +++ b/examples/agent/a2ui_agent/samples/client/lit/contact/ui/custom-components/test/README.md @@ -0,0 +1,30 @@ +# Contact sample verification tests + +This directory contains tests to verify custom component integration specifically within the `contact` sample application environment. + +## How to run + +These tests run via the Vite development server used by the contact sample. + +### 1. Start the dev server +From the `web/lit/samples/contact` directory, run: + +```bash +npm run dev +``` + +### 2. Access the tests +Open your browser and navigate to the local server (usually port 5173): + +- **Component override test**: + [http://localhost:5173/ui/custom-components/test/override-test.html](http://localhost:5173/ui/custom-components/test/override-test.html) + *Verifies that a standard component (TextField) can be overridden by a custom implementation.* + +- **Hierarchy graph integration test**: + [http://localhost:5173/ui/custom-components/test/hierarchy-test.html](http://localhost:5173/ui/custom-components/test/hierarchy-test.html) + *Verifies that the HierarchyGraph component renders correctly within the contact app's build setup.* + +## Files + +- `override-test.html` & `override-test.ts`: Implements and tests a custom `TextField` override. +- `hierarchy-test.html`: Tests the `HierarchyGraph` component. diff --git a/examples/agent/a2ui_agent/samples/client/lit/contact/ui/custom-components/test/org-chart-test.html b/examples/agent/a2ui_agent/samples/client/lit/contact/ui/custom-components/test/org-chart-test.html new file mode 100644 index 0000000000..b54b5710cf --- /dev/null +++ b/examples/agent/a2ui_agent/samples/client/lit/contact/ui/custom-components/test/org-chart-test.html @@ -0,0 +1,86 @@ + + + + + + + + + A2UI Org Chart Test (Contact Sample) + + + + + +

A2UI Org Chart Test (Contact Sample)

+ +
+ +
+ + + + + \ No newline at end of file diff --git a/examples/agent/a2ui_agent/samples/client/lit/contact/ui/custom-components/test/override-test.html b/examples/agent/a2ui_agent/samples/client/lit/contact/ui/custom-components/test/override-test.html new file mode 100644 index 0000000000..7edf0291c5 --- /dev/null +++ b/examples/agent/a2ui_agent/samples/client/lit/contact/ui/custom-components/test/override-test.html @@ -0,0 +1,43 @@ + + + + + + + + A2UI Component Override Test + + + + + +

Component Override Test

+
+ + + \ No newline at end of file diff --git a/examples/agent/a2ui_agent/samples/client/lit/contact/ui/custom-components/test/override-test.ts b/examples/agent/a2ui_agent/samples/client/lit/contact/ui/custom-components/test/override-test.ts new file mode 100644 index 0000000000..7ded768bd4 --- /dev/null +++ b/examples/agent/a2ui_agent/samples/client/lit/contact/ui/custom-components/test/override-test.ts @@ -0,0 +1,46 @@ +/* + Copyright 2025 Google LLC + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import { componentRegistry, Root } from "@a2ui/lit/ui"; +import { html, css } from "lit"; +import { property } from "lit/decorators.js"; +// 1. Define the override +import { PremiumTextField } from "../premium-text-field.js"; + +// 2. Register it as "TextField" +componentRegistry.register("TextField", PremiumTextField, "premium-text-field"); +console.log("Registered PremiumTextField override"); + +// 3. Render a standard TextField component node +const container = document.getElementById("app"); +if (container) { + const root = document.createElement("a2ui-root") as Root; + + const textFieldComponent = { + type: "TextField", + id: "tf-1", + properties: { + label: "Enter your name", + text: "John Doe", + }, + }; + + // Root renders its *children*, so we must pass the component as a child. + root.childComponents = [textFieldComponent]; + + root.enableCustomElements = true; // Enable the feature + container.appendChild(root); +} diff --git a/examples/agent/a2ui_agent/samples/client/lit/contact/ui/snackbar.ts b/examples/agent/a2ui_agent/samples/client/lit/contact/ui/snackbar.ts new file mode 100644 index 0000000000..91f2f036dc --- /dev/null +++ b/examples/agent/a2ui_agent/samples/client/lit/contact/ui/snackbar.ts @@ -0,0 +1,299 @@ +/* + Copyright 2025 Google LLC + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ +import { LitElement, html, css, nothing, unsafeCSS } from "lit"; +import { customElement, property } from "lit/decorators.js"; +import { SnackbarMessage, SnackbarUUID, SnackType } from "../types/types"; +import { repeat } from "lit/directives/repeat.js"; +import { SnackbarActionEvent } from "../events/events"; +import { classMap } from "lit/directives/class-map.js"; +import { v0_8 } from "@a2ui/lit"; + +const DEFAULT_TIMEOUT = 8000; + +@customElement("ui-snackbar") +export class Snackbar extends LitElement { + @property({ reflect: true, type: Boolean }) + accessor active = false; + + @property({ reflect: true, type: Boolean }) + accessor error = false; + + @property() + accessor timeout = DEFAULT_TIMEOUT; + + #messages: SnackbarMessage[] = []; + #timeout = 0; + + static styles = [ + unsafeCSS(v0_8.Styles.structuralStyles), + css` + :host { + --text-color: var(--n-0); + --bb-body-medium: 16px; + --bb-body-line-height-medium: 24px; + + display: flex; + align-items: center; + position: fixed; + bottom: var(--bb-grid-size-7); + left: 50%; + translate: -50% 0; + opacity: 0; + pointer-events: none; + border-radius: var(--bb-grid-size-2); + background: var(--n-90); + padding: var(--bb-grid-size-3) var(--bb-grid-size-6); + width: 60svw; + max-width: 720px; + z-index: 1800; + scrollbar-width: none; + overflow-x: scroll; + font: 400 var(--bb-body-medium) / var(--bb-body-line-height-medium) + var(--bb-font-family); + } + + :host([active]) { + transition: opacity 0.3s cubic-bezier(0, 0, 0.3, 1) 0.2s; + opacity: 1; + pointer-events: auto; + } + + :host([error]) { + background: var(--e-90); + --text-color: var(--e-40); + } + + .g-icon { + flex: 0 0 auto; + color: var(--text-color); + margin-right: var(--bb-grid-size-4); + + &.rotate { + animation: 1s linear 0s infinite normal forwards running rotate; + } + } + + #messages { + color: var(--text-color); + flex: 1 1 auto; + margin-right: var(--bb-grid-size-11); + + a, + a:visited { + color: var(--bb-ui-600); + text-decoration: none; + + &:hover { + color: var(--bb-ui-500); + text-decoration: underline; + } + } + } + + #actions { + flex: 0 1 auto; + width: fit-content; + margin-right: var(--bb-grid-size-3); + + & button { + font: 500 var(--bb-body-medium) / var(--bb-body-line-height-medium) + var(--bb-font-family); + padding: 0; + background: transparent; + border: none; + margin: 0 var(--bb-grid-size-4); + color: var(--text-color); + opacity: 0.7; + transition: opacity 0.2s cubic-bezier(0, 0, 0.3, 1); + + &:not([disabled]) { + cursor: pointer; + + &:hover, + &:focus { + opacity: 1; + } + } + } + } + + #close { + display: flex; + align-items: center; + padding: 0; + color: var(--text-color); + background: transparent; + border: none; + margin: 0 0 0 var(--bb-grid-size-2); + opacity: 0.7; + transition: opacity 0.2s cubic-bezier(0, 0, 0.3, 1); + + .g-icon { + margin-right: 0; + } + + &:not([disabled]) { + cursor: pointer; + + &:hover, + &:focus { + opacity: 1; + } + } + } + + @keyframes rotate { + from { + rotate: 0deg; + } + + to { + rotate: 360deg; + } + } + `, + ]; + + show(message: SnackbarMessage, replaceAll = false) { + const existingMessage = this.#messages.findIndex( + (msg) => msg.id === message.id + ); + if (existingMessage === -1) { + if (replaceAll) { + this.#messages.length = 0; + } + + this.#messages.push(message); + } else { + this.#messages[existingMessage] = message; + } + + window.clearTimeout(this.#timeout); + if (!this.#messages.every((msg) => msg.persistent)) { + this.#timeout = window.setTimeout(() => { + this.hide(); + }, this.timeout); + } + + this.error = this.#messages.some((msg) => msg.type === SnackType.ERROR); + this.active = true; + this.requestUpdate(); + + return message.id; + } + + hide(id?: SnackbarUUID) { + if (id) { + const idx = this.#messages.findIndex((msg) => msg.id === id); + if (idx !== -1) { + this.#messages.splice(idx, 1); + } + } else { + this.#messages.length = 0; + } + + this.active = this.#messages.length !== 0; + this.updateComplete.then((avoidedUpdate) => { + if (!avoidedUpdate) { + return; + } + + this.requestUpdate(); + }); + } + + render() { + let rotate = false; + let icon = ""; + for (let i = this.#messages.length - 1; i >= 0; i--) { + if ( + !this.#messages[i].type || + this.#messages[i].type === SnackType.NONE + ) { + continue; + } + + icon = this.#messages[i].type; + if (this.#messages[i].type === SnackType.PENDING) { + icon = "progress_activity"; + rotate = true; + } + break; + } + + return html` ${icon + ? html`${icon}` + : nothing} +
+ ${repeat( + this.#messages, + (message) => message.id, + (message) => { + return html`
${message.message}
`; + } + )} +
+
+ ${repeat( + this.#messages, + (message) => message.id, + (message) => { + if (!message.actions) { + return nothing; + } + + return html`${repeat( + message.actions, + (action) => action.value, + (action) => { + return html``; + } + )}`; + } + )} +
+ `; + } +} diff --git a/examples/agent/a2ui_agent/samples/client/lit/contact/ui/ui.ts b/examples/agent/a2ui_agent/samples/client/lit/contact/ui/ui.ts new file mode 100644 index 0000000000..7726f29d73 --- /dev/null +++ b/examples/agent/a2ui_agent/samples/client/lit/contact/ui/ui.ts @@ -0,0 +1,17 @@ +/* + Copyright 2025 Google LLC + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +export { Snackbar } from "./snackbar"; diff --git a/examples/agent/a2ui_agent/samples/client/lit/contact/vite.config.ts b/examples/agent/a2ui_agent/samples/client/lit/contact/vite.config.ts new file mode 100644 index 0000000000..ce663a7a29 --- /dev/null +++ b/examples/agent/a2ui_agent/samples/client/lit/contact/vite.config.ts @@ -0,0 +1,45 @@ +/* + Copyright 2025 Google LLC + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import { config } from "dotenv"; +import { UserConfig } from "vite"; +import * as Middleware from "./middleware"; +import { dirname, resolve } from "node:path"; +import { fileURLToPath } from "node:url"; + +const __dirname = dirname(fileURLToPath(import.meta.url)); + +export default async () => { + config(); + + const entry: Record = { + contact: resolve(__dirname, "index.html"), + }; + + return { + plugins: [Middleware.A2AMiddleware.plugin()], + build: { + rollupOptions: { + input: entry, + }, + target: "esnext", + }, + define: {}, + resolve: { + dedupe: ["lit"], + }, + } satisfies UserConfig; +}; diff --git a/examples/agent/a2ui_agent/samples/client/lit/package-lock.json b/examples/agent/a2ui_agent/samples/client/lit/package-lock.json new file mode 100644 index 0000000000..2497918558 --- /dev/null +++ b/examples/agent/a2ui_agent/samples/client/lit/package-lock.json @@ -0,0 +1,2771 @@ +{ + "name": "@a2ui/lit-samples", + "version": "0.8.1", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "@a2ui/lit-samples", + "version": "0.8.1", + "workspaces": [ + "contact", + "restaurant", + "shell" + ], + "devDependencies": { + "concurrently": "9.2.1" + } + }, + "../../../renderers/lit": { + "name": "@a2ui/lit", + "version": "0.8.1", + "license": "Apache-2.0", + "dependencies": { + "@lit-labs/signals": "^0.1.3", + "@lit/context": "^1.1.4", + "lit": "^3.3.1", + "markdown-it": "^14.1.0", + "signal-utils": "^0.21.1" + }, + "devDependencies": { + "@types/markdown-it": "^14.1.2", + "@types/node": "^24.10.1", + "google-artifactregistry-auth": "^3.5.0", + "typescript": "^5.8.3", + "wireit": "^0.15.0-pre.2" + } + }, + "contact": { + "name": "@a2ui/contact", + "version": "0.8.1", + "license": "Apache-2.0", + "dependencies": { + "@a2a-js/sdk": "^0.3.4", + "@a2ui/lit": "file:../../../../renderers/lit", + "@lit-labs/signals": "^0.1.3", + "@lit/context": "^1.1.4", + "lit": "^3.3.1" + }, + "devDependencies": { + "dotenv": "^17.2.3", + "typescript": "^5.8.3", + "uuid": "^13.0.0", + "vite": "^7.1.11", + "wireit": "^0.15.0-pre.2" + } + }, + "node_modules/@a2a-js/sdk": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/@a2a-js/sdk/-/sdk-0.3.6.tgz", + "integrity": "sha512-i2kFa4cwrxnSHxAfNQLDF5Q8gU75BHpRYAaZouHydXsKX1bOLaauautpsyOZRroejzFyYEdsm+65PEzZ5FnHmQ==", + "license": "Apache-2.0", + "dependencies": { + "uuid": "^11.1.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "express": "^4.21.2 || ^5.1.0" + }, + "peerDependenciesMeta": { + "express": { + "optional": true + } + } + }, + "node_modules/@a2a-js/sdk/node_modules/uuid": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz", + "integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/esm/bin/uuid" + } + }, + "node_modules/@a2ui/contact": { + "resolved": "contact", + "link": true + }, + "node_modules/@a2ui/lit": { + "resolved": "../../../renderers/lit", + "link": true + }, + "node_modules/@a2ui/shell": { + "resolved": "shell", + "link": true + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.1.tgz", + "integrity": "sha512-HHB50pdsBX6k47S4u5g/CaLjqS3qwaOVE5ILsq64jyzgMhLuCuZ8rGzM9yhsAjfjkbgUPMzZEPa7DAp7yz6vuA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.1.tgz", + "integrity": "sha512-kFqa6/UcaTbGm/NncN9kzVOODjhZW8e+FRdSeypWe6j33gzclHtwlANs26JrupOntlcWmB0u8+8HZo8s7thHvg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.1.tgz", + "integrity": "sha512-45fuKmAJpxnQWixOGCrS+ro4Uvb4Re9+UTieUY2f8AEc+t7d4AaZ6eUJ3Hva7dtrxAAWHtlEFsXFMAgNnGU9uQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.1.tgz", + "integrity": "sha512-LBEpOz0BsgMEeHgenf5aqmn/lLNTFXVfoWMUox8CtWWYK9X4jmQzWjoGoNb8lmAYml/tQ/Ysvm8q7szu7BoxRQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.1.tgz", + "integrity": "sha512-veg7fL8eMSCVKL7IW4pxb54QERtedFDfY/ASrumK/SbFsXnRazxY4YykN/THYqFnFwJ0aVjiUrVG2PwcdAEqQQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.1.tgz", + "integrity": "sha512-+3ELd+nTzhfWb07Vol7EZ+5PTbJ/u74nC6iv4/lwIU99Ip5uuY6QoIf0Hn4m2HoV0qcnRivN3KSqc+FyCHjoVQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.1.tgz", + "integrity": "sha512-/8Rfgns4XD9XOSXlzUDepG8PX+AVWHliYlUkFI3K3GB6tqbdjYqdhcb4BKRd7C0BhZSoaCxhv8kTcBrcZWP+xg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.1.tgz", + "integrity": "sha512-GITpD8dK9C+r+5yRT/UKVT36h/DQLOHdwGVwwoHidlnA168oD3uxA878XloXebK4Ul3gDBBIvEdL7go9gCUFzQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.1.tgz", + "integrity": "sha512-ieMID0JRZY/ZeCrsFQ3Y3NlHNCqIhTprJfDgSB3/lv5jJZ8FX3hqPyXWhe+gvS5ARMBJ242PM+VNz/ctNj//eA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.1.tgz", + "integrity": "sha512-W9//kCrh/6in9rWIBdKaMtuTTzNj6jSeG/haWBADqLLa9P8O5YSRDzgD5y9QBok4AYlzS6ARHifAb75V6G670Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.1.tgz", + "integrity": "sha512-VIUV4z8GD8rtSVMfAj1aXFahsi/+tcoXXNYmXgzISL+KB381vbSTNdeZHHHIYqFyXcoEhu9n5cT+05tRv13rlw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.1.tgz", + "integrity": "sha512-l4rfiiJRN7sTNI//ff65zJ9z8U+k6zcCg0LALU5iEWzY+a1mVZ8iWC1k5EsNKThZ7XCQ6YWtsZ8EWYm7r1UEsg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.1.tgz", + "integrity": "sha512-U0bEuAOLvO/DWFdygTHWY8C067FXz+UbzKgxYhXC0fDieFa0kDIra1FAhsAARRJbvEyso8aAqvPdNxzWuStBnA==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.1.tgz", + "integrity": "sha512-NzdQ/Xwu6vPSf/GkdmRNsOfIeSGnh7muundsWItmBsVpMoNPVpM61qNzAVY3pZ1glzzAxLR40UyYM23eaDDbYQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.1.tgz", + "integrity": "sha512-7zlw8p3IApcsN7mFw0O1Z1PyEk6PlKMu18roImfl3iQHTnr/yAfYv6s4hXPidbDoI2Q0pW+5xeoM4eTCC0UdrQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.1.tgz", + "integrity": "sha512-cGj5wli+G+nkVQdZo3+7FDKC25Uh4ZVwOAK6A06Hsvgr8WqBBuOy/1s+PUEd/6Je+vjfm6stX0kmib5b/O2Ykw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.1.tgz", + "integrity": "sha512-z3H/HYI9MM0HTv3hQZ81f+AKb+yEoCRlUby1F80vbQ5XdzEMyY/9iNlAmhqiBKw4MJXwfgsh7ERGEOhrM1niMA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.1.tgz", + "integrity": "sha512-wzC24DxAvk8Em01YmVXyjl96Mr+ecTPyOuADAvjGg+fyBpGmxmcr2E5ttf7Im8D0sXZihpxzO1isus8MdjMCXQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.1.tgz", + "integrity": "sha512-1YQ8ybGi2yIXswu6eNzJsrYIGFpnlzEWRl6iR5gMgmsrR0FcNoV1m9k9sc3PuP5rUBLshOZylc9nqSgymI+TYg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.1.tgz", + "integrity": "sha512-5Z+DzLCrq5wmU7RDaMDe2DVXMRm2tTDvX2KU14JJVBN2CT/qov7XVix85QoJqHltpvAOZUAc3ndU56HSMWrv8g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.1.tgz", + "integrity": "sha512-Q73ENzIdPF5jap4wqLtsfh8YbYSZ8Q0wnxplOlZUOyZy7B4ZKW8DXGWgTCZmF8VWD7Tciwv5F4NsRf6vYlZtqg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.1.tgz", + "integrity": "sha512-ajbHrGM/XiK+sXM0JzEbJAen+0E+JMQZ2l4RR4VFwvV9JEERx+oxtgkpoKv1SevhjavK2z2ReHk32pjzktWbGg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.1.tgz", + "integrity": "sha512-IPUW+y4VIjuDVn+OMzHc5FV4GubIwPnsz6ubkvN8cuhEqH81NovB53IUlrlBkPMEPxvNnf79MGBoz8rZ2iW8HA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.1.tgz", + "integrity": "sha512-RIVRWiljWA6CdVu8zkWcRmGP7iRRIIwvhDKem8UMBjPql2TXM5PkDVvvrzMtj1V+WFPB4K7zkIGM7VzRtFkjdg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.1.tgz", + "integrity": "sha512-2BR5M8CPbptC1AK5JbJT1fWrHLvejwZidKx3UMSF0ecHMa+smhi16drIrCEggkgviBwLYd5nwrFLSl5Kho96RQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.1.tgz", + "integrity": "sha512-d5X6RMYv6taIymSk8JBP+nxv8DQAMY6A51GPgusqLdK9wBz5wWIXy1KjTck6HnjE9hqJzJRdk+1p/t5soSbCtw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@google/genai": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/@google/genai/-/genai-1.33.0.tgz", + "integrity": "sha512-ThUjFZ1N0DU88peFjnQkb8K198EWaW2RmmnDShFQ+O+xkIH9itjpRe358x3L/b4X/A7dimkvq63oz49Vbh7Cog==", + "license": "Apache-2.0", + "dependencies": { + "google-auth-library": "^10.3.0", + "ws": "^8.18.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "@modelcontextprotocol/sdk": "^1.24.0" + }, + "peerDependenciesMeta": { + "@modelcontextprotocol/sdk": { + "optional": true + } + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "license": "MIT" + }, + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", + "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/@lit-labs/signals": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@lit-labs/signals/-/signals-0.1.3.tgz", + "integrity": "sha512-P0yWgH5blwVyEwBg+WFspLzeu1i0ypJP1QB0l1Omr9qZLIPsUu0p4Fy2jshOg7oQyha5n163K3GJGeUhQQ682Q==", + "license": "BSD-3-Clause", + "dependencies": { + "lit": "^2.0.0 || ^3.0.0", + "signal-polyfill": "^0.2.0" + } + }, + "node_modules/@lit-labs/ssr-dom-shim": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@lit-labs/ssr-dom-shim/-/ssr-dom-shim-1.4.0.tgz", + "integrity": "sha512-ficsEARKnmmW5njugNYKipTm4SFnbik7CXtoencDZzmzo/dQ+2Q0bgkzJuoJP20Aj0F+izzJjOqsnkd6F/o1bw==", + "license": "BSD-3-Clause" + }, + "node_modules/@lit/context": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@lit/context/-/context-1.1.6.tgz", + "integrity": "sha512-M26qDE6UkQbZA2mQ3RjJ3Gzd8TxP+/0obMgE5HfkfLhEEyYE3Bui4A5XHiGPjy0MUGAyxB3QgVuw2ciS0kHn6A==", + "license": "BSD-3-Clause", + "dependencies": { + "@lit/reactive-element": "^1.6.2 || ^2.1.0" + } + }, + "node_modules/@lit/reactive-element": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@lit/reactive-element/-/reactive-element-2.1.1.tgz", + "integrity": "sha512-N+dm5PAYdQ8e6UlywyyrgI2t++wFGXfHx+dSJ1oBrg6FAxUj40jId++EaRm80MKX5JnlH1sBsyZ5h0bcZKemCg==", + "license": "BSD-3-Clause", + "dependencies": { + "@lit-labs/ssr-dom-shim": "^1.4.0" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.53.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.53.5.tgz", + "integrity": "sha512-iDGS/h7D8t7tvZ1t6+WPK04KD0MwzLZrG0se1hzBjSi5fyxlsiggoJHwh18PCFNn7tG43OWb6pdZ6Y+rMlmyNQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.53.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.53.5.tgz", + "integrity": "sha512-wrSAViWvZHBMMlWk6EJhvg8/rjxzyEhEdgfMMjREHEq11EtJ6IP6yfcCH57YAEca2Oe3FNCE9DSTgU70EIGmVw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.53.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.53.5.tgz", + "integrity": "sha512-S87zZPBmRO6u1YXQLwpveZm4JfPpAa6oHBX7/ghSiGH3rz/KDgAu1rKdGutV+WUI6tKDMbaBJomhnT30Y2t4VQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.53.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.53.5.tgz", + "integrity": "sha512-YTbnsAaHo6VrAczISxgpTva8EkfQus0VPEVJCEaboHtZRIb6h6j0BNxRBOwnDciFTZLDPW5r+ZBmhL/+YpTZgA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.53.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.53.5.tgz", + "integrity": "sha512-1T8eY2J8rKJWzaznV7zedfdhD1BqVs1iqILhmHDq/bqCUZsrMt+j8VCTHhP0vdfbHK3e1IQ7VYx3jlKqwlf+vw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.53.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.53.5.tgz", + "integrity": "sha512-sHTiuXyBJApxRn+VFMaw1U+Qsz4kcNlxQ742snICYPrY+DDL8/ZbaC4DVIB7vgZmp3jiDaKA0WpBdP0aqPJoBQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.53.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.53.5.tgz", + "integrity": "sha512-dV3T9MyAf0w8zPVLVBptVlzaXxka6xg1f16VAQmjg+4KMSTWDvhimI/Y6mp8oHwNrmnmVl9XxJ/w/mO4uIQONA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.53.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.53.5.tgz", + "integrity": "sha512-wIGYC1x/hyjP+KAu9+ewDI+fi5XSNiUi9Bvg6KGAh2TsNMA3tSEs+Sh6jJ/r4BV/bx/CyWu2ue9kDnIdRyafcQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.53.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.53.5.tgz", + "integrity": "sha512-Y+qVA0D9d0y2FRNiG9oM3Hut/DgODZbU9I8pLLPwAsU0tUKZ49cyV1tzmB/qRbSzGvY8lpgGkJuMyuhH7Ma+Vg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.53.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.53.5.tgz", + "integrity": "sha512-juaC4bEgJsyFVfqhtGLz8mbopaWD+WeSOYr5E16y+1of6KQjc0BpwZLuxkClqY1i8sco+MdyoXPNiCkQou09+g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.53.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.53.5.tgz", + "integrity": "sha512-rIEC0hZ17A42iXtHX+EPJVL/CakHo+tT7W0pbzdAGuWOt2jxDFh7A/lRhsNHBcqL4T36+UiAgwO8pbmn3dE8wA==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.53.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.53.5.tgz", + "integrity": "sha512-T7l409NhUE552RcAOcmJHj3xyZ2h7vMWzcwQI0hvn5tqHh3oSoclf9WgTl+0QqffWFG8MEVZZP1/OBglKZx52Q==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.53.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.53.5.tgz", + "integrity": "sha512-7OK5/GhxbnrMcxIFoYfhV/TkknarkYC1hqUw1wU2xUN3TVRLNT5FmBv4KkheSG2xZ6IEbRAhTooTV2+R5Tk0lQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.53.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.53.5.tgz", + "integrity": "sha512-GwuDBE/PsXaTa76lO5eLJTyr2k8QkPipAyOrs4V/KJufHCZBJ495VCGJol35grx9xryk4V+2zd3Ri+3v7NPh+w==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.53.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.53.5.tgz", + "integrity": "sha512-IAE1Ziyr1qNfnmiQLHBURAD+eh/zH1pIeJjeShleII7Vj8kyEm2PF77o+lf3WTHDpNJcu4IXJxNO0Zluro8bOw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.53.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.53.5.tgz", + "integrity": "sha512-Pg6E+oP7GvZ4XwgRJBuSXZjcqpIW3yCBhK4BcsANvb47qMvAbCjR6E+1a/U2WXz1JJxp9/4Dno3/iSJLcm5auw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.53.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.53.5.tgz", + "integrity": "sha512-txGtluxDKTxaMDzUduGP0wdfng24y1rygUMnmlUJ88fzCCULCLn7oE5kb2+tRB+MWq1QDZT6ObT5RrR8HFRKqg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.53.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.53.5.tgz", + "integrity": "sha512-3DFiLPnTxiOQV993fMc+KO8zXHTcIjgaInrqlG8zDp1TlhYl6WgrOHuJkJQ6M8zHEcntSJsUp1XFZSY8C1DYbg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.53.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.53.5.tgz", + "integrity": "sha512-nggc/wPpNTgjGg75hu+Q/3i32R00Lq1B6N1DO7MCU340MRKL3WZJMjA9U4K4gzy3dkZPXm9E1Nc81FItBVGRlA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.53.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.53.5.tgz", + "integrity": "sha512-U/54pTbdQpPLBdEzCT6NBCFAfSZMvmjr0twhnD9f4EIvlm9wy3jjQ38yQj1AGznrNO65EWQMgm/QUjuIVrYF9w==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.53.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.53.5.tgz", + "integrity": "sha512-2NqKgZSuLH9SXBBV2dWNRCZmocgSOx8OJSdpRaEcRlIfX8YrKxUT6z0F1NpvDVhOsl190UFTRh2F2WDWWCYp3A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.53.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.53.5.tgz", + "integrity": "sha512-JRpZUhCfhZ4keB5v0fe02gQJy05GqboPOaxvjugW04RLSYYoB/9t2lx2u/tMs/Na/1NXfY8QYjgRljRpN+MjTQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "24.10.4", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.4.tgz", + "integrity": "sha512-vnDVpYPMzs4wunl27jHrfmwojOGKya0xyM3sH+UE5iv5uPS6vX7UIoh6m+vQc5LGBq52HBKPIn/zcSZVzeDEZg==", + "license": "MIT", + "peer": true, + "dependencies": { + "undici-types": "~7.16.0" + } + }, + "node_modules/@types/trusted-types": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", + "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", + "license": "MIT" + }, + "node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/anymatch/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "license": "MIT" + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/bignumber.js": { + "version": "9.3.1", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.3.1.tgz", + "integrity": "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==", + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", + "license": "BSD-3-Clause" + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chalk/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/concurrently": { + "version": "9.2.1", + "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-9.2.1.tgz", + "integrity": "sha512-fsfrO0MxV64Znoy8/l1vVIjjHa29SZyyqPgQBwhiDcaW8wJc2W3XWVOGx4M3oJBnv/zdUZIIp1gDeS98GzP8Ng==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "4.1.2", + "rxjs": "7.8.2", + "shell-quote": "1.8.3", + "supports-color": "8.1.1", + "tree-kill": "1.2.2", + "yargs": "17.7.2" + }, + "bin": { + "conc": "dist/bin/concurrently.js", + "concurrently": "dist/bin/concurrently.js" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/open-cli-tools/concurrently?sponsor=1" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/data-uri-to-buffer": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", + "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/dotenv": { + "version": "17.2.3", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.3.tgz", + "integrity": "sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "license": "MIT" + }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/esbuild": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.1.tgz", + "integrity": "sha512-yY35KZckJJuVVPXpvjgxiCuVEJT67F6zDeVTv4rizyPrfGBUpZQsvmxnN+C371c2esD/hNMjj4tpBhuueLN7aA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.27.1", + "@esbuild/android-arm": "0.27.1", + "@esbuild/android-arm64": "0.27.1", + "@esbuild/android-x64": "0.27.1", + "@esbuild/darwin-arm64": "0.27.1", + "@esbuild/darwin-x64": "0.27.1", + "@esbuild/freebsd-arm64": "0.27.1", + "@esbuild/freebsd-x64": "0.27.1", + "@esbuild/linux-arm": "0.27.1", + "@esbuild/linux-arm64": "0.27.1", + "@esbuild/linux-ia32": "0.27.1", + "@esbuild/linux-loong64": "0.27.1", + "@esbuild/linux-mips64el": "0.27.1", + "@esbuild/linux-ppc64": "0.27.1", + "@esbuild/linux-riscv64": "0.27.1", + "@esbuild/linux-s390x": "0.27.1", + "@esbuild/linux-x64": "0.27.1", + "@esbuild/netbsd-arm64": "0.27.1", + "@esbuild/netbsd-x64": "0.27.1", + "@esbuild/openbsd-arm64": "0.27.1", + "@esbuild/openbsd-x64": "0.27.1", + "@esbuild/openharmony-arm64": "0.27.1", + "@esbuild/sunos-x64": "0.27.1", + "@esbuild/win32-arm64": "0.27.1", + "@esbuild/win32-ia32": "0.27.1", + "@esbuild/win32-x64": "0.27.1" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "license": "MIT" + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fastq": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", + "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/fetch-blob": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", + "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "paypal", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "dependencies": { + "node-domexception": "^1.0.0", + "web-streams-polyfill": "^3.0.3" + }, + "engines": { + "node": "^12.20 || >= 14.13" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/formdata-polyfill": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", + "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", + "license": "MIT", + "dependencies": { + "fetch-blob": "^3.1.2" + }, + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/gaxios": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-7.1.3.tgz", + "integrity": "sha512-YGGyuEdVIjqxkxVH1pUTMY/XtmmsApXrCVv5EU25iX6inEPbV+VakJfLealkBtJN69AQmh1eGOdCl9Sm1UP6XQ==", + "license": "Apache-2.0", + "dependencies": { + "extend": "^3.0.2", + "https-proxy-agent": "^7.0.1", + "node-fetch": "^3.3.2", + "rimraf": "^5.0.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/gcp-metadata": { + "version": "8.1.2", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-8.1.2.tgz", + "integrity": "sha512-zV/5HKTfCeKWnxG0Dmrw51hEWFGfcF2xiXqcA3+J90WDuP0SvoiSO5ORvcBsifmx/FoIjgQN3oNOGaQ5PhLFkg==", + "license": "Apache-2.0", + "dependencies": { + "gaxios": "^7.0.0", + "google-logging-utils": "^1.0.0", + "json-bigint": "^1.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/google-auth-library": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-10.5.0.tgz", + "integrity": "sha512-7ABviyMOlX5hIVD60YOfHw4/CxOfBhyduaYB+wbFWCWoni4N7SLcV46hrVRktuBbZjFC9ONyqamZITN7q3n32w==", + "license": "Apache-2.0", + "dependencies": { + "base64-js": "^1.3.0", + "ecdsa-sig-formatter": "^1.0.11", + "gaxios": "^7.0.0", + "gcp-metadata": "^8.0.0", + "google-logging-utils": "^1.0.0", + "gtoken": "^8.0.0", + "jws": "^4.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/google-logging-utils": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/google-logging-utils/-/google-logging-utils-1.1.3.tgz", + "integrity": "sha512-eAmLkjDjAFCVXg7A1unxHsLf961m6y17QFqXqAXGj/gVkKFrEICfStRfwUlGNfeCEjNRa32JEWOUTlYXPyyKvA==", + "license": "Apache-2.0", + "engines": { + "node": ">=14" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/gtoken": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-8.0.0.tgz", + "integrity": "sha512-+CqsMbHPiSTdtSO14O51eMNlrp9N79gmeqmXeouJOhfucAedHw9noVe/n5uJk3tbKE6a+6ZCQg3RPhVhHByAIw==", + "license": "MIT", + "dependencies": { + "gaxios": "^7.0.0", + "jws": "^4.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "license": "ISC" + }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/json-bigint": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz", + "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==", + "license": "MIT", + "dependencies": { + "bignumber.js": "^9.0.0" + } + }, + "node_modules/jsonc-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", + "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/jwa": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz", + "integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==", + "license": "MIT", + "dependencies": { + "buffer-equal-constant-time": "^1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.1.tgz", + "integrity": "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==", + "license": "MIT", + "dependencies": { + "jwa": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/lit": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/lit/-/lit-3.3.1.tgz", + "integrity": "sha512-Ksr/8L3PTapbdXJCk+EJVB78jDodUMaP54gD24W186zGRARvwrsPfS60wae/SSCTCNZVPd1chXqio1qHQmu4NA==", + "license": "BSD-3-Clause", + "dependencies": { + "@lit/reactive-element": "^2.1.0", + "lit-element": "^4.2.0", + "lit-html": "^3.3.0" + } + }, + "node_modules/lit-element": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/lit-element/-/lit-element-4.2.1.tgz", + "integrity": "sha512-WGAWRGzirAgyphK2urmYOV72tlvnxw7YfyLDgQ+OZnM9vQQBQnumQ7jUJe6unEzwGU3ahFOjuz1iz1jjrpCPuw==", + "license": "BSD-3-Clause", + "dependencies": { + "@lit-labs/ssr-dom-shim": "^1.4.0", + "@lit/reactive-element": "^2.1.0", + "lit-html": "^3.3.0" + } + }, + "node_modules/lit-html": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/lit-html/-/lit-html-3.3.1.tgz", + "integrity": "sha512-S9hbyDu/vs1qNrithiNyeyv64c9yqiW9l+DBgI18fL+MTvOtWoFR0FWiyq1TxaYef5wNlpEmzlXoBlZEO+WjoA==", + "license": "BSD-3-Clause", + "dependencies": { + "@types/trusted-types": "^2.0.2" + } + }, + "node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "license": "ISC" + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/micromatch/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", + "deprecated": "Use your platform's native DOMException instead", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "github", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "engines": { + "node": ">=10.5.0" + } + }, + "node_modules/node-fetch": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", + "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", + "license": "MIT", + "dependencies": { + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/node-fetch" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "license": "BlueOak-1.0.0" + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/proper-lockfile": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/proper-lockfile/-/proper-lockfile-4.1.2.tgz", + "integrity": "sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "retry": "^0.12.0", + "signal-exit": "^3.0.2" + } + }, + "node_modules/proper-lockfile/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/readdirp/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "5.0.10", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.10.tgz", + "integrity": "sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ==", + "license": "ISC", + "dependencies": { + "glob": "^10.3.7" + }, + "bin": { + "rimraf": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rollup": { + "version": "4.53.5", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.53.5.tgz", + "integrity": "sha512-iTNAbFSlRpcHeeWu73ywU/8KuU/LZmNCSxp6fjQkJBD3ivUb8tpDrXhIxEzA05HlYMEwmtaUnb3RP+YNv162OQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.53.5", + "@rollup/rollup-android-arm64": "4.53.5", + "@rollup/rollup-darwin-arm64": "4.53.5", + "@rollup/rollup-darwin-x64": "4.53.5", + "@rollup/rollup-freebsd-arm64": "4.53.5", + "@rollup/rollup-freebsd-x64": "4.53.5", + "@rollup/rollup-linux-arm-gnueabihf": "4.53.5", + "@rollup/rollup-linux-arm-musleabihf": "4.53.5", + "@rollup/rollup-linux-arm64-gnu": "4.53.5", + "@rollup/rollup-linux-arm64-musl": "4.53.5", + "@rollup/rollup-linux-loong64-gnu": "4.53.5", + "@rollup/rollup-linux-ppc64-gnu": "4.53.5", + "@rollup/rollup-linux-riscv64-gnu": "4.53.5", + "@rollup/rollup-linux-riscv64-musl": "4.53.5", + "@rollup/rollup-linux-s390x-gnu": "4.53.5", + "@rollup/rollup-linux-x64-gnu": "4.53.5", + "@rollup/rollup-linux-x64-musl": "4.53.5", + "@rollup/rollup-openharmony-arm64": "4.53.5", + "@rollup/rollup-win32-arm64-msvc": "4.53.5", + "@rollup/rollup-win32-ia32-msvc": "4.53.5", + "@rollup/rollup-win32-x64-gnu": "4.53.5", + "@rollup/rollup-win32-x64-msvc": "4.53.5", + "fsevents": "~2.3.2" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/rxjs": { + "version": "7.8.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", + "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/shell-quote": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.3.tgz", + "integrity": "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/signal-polyfill": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/signal-polyfill/-/signal-polyfill-0.2.2.tgz", + "integrity": "sha512-p63Y4Er5/eMQ9RHg0M0Y64NlsQKpiu6MDdhBXpyywRuWiPywhJTpKJ1iB5K2hJEbFZ0BnDS7ZkJ+0AfTuL37Rg==", + "license": "Apache-2.0" + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", + "dev": true, + "license": "MIT", + "bin": { + "tree-kill": "cli.js" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "license": "MIT" + }, + "node_modules/uuid": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-13.0.0.tgz", + "integrity": "sha512-XQegIaBTVUjSHliKqcnFqYypAd4S+WCYt5NIeRs6w/UAry7z8Y9j5ZwRRL4kzq9U3sD6v+85er9FvkEaBpji2w==", + "dev": true, + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist-node/bin/uuid" + } + }, + "node_modules/vite": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.0.tgz", + "integrity": "sha512-dZwN5L1VlUBewiP6H9s2+B3e3Jg96D0vzN+Ry73sOefebhYr9f94wwkMNN/9ouoU8pV1BqA1d1zGk8928cx0rg==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.27.0", + "fdir": "^6.5.0", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.43.0", + "tinyglobby": "^0.2.15" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "lightningcss": "^1.21.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/web-streams-polyfill": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", + "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wireit": { + "version": "0.15.0-pre.2", + "resolved": "https://registry.npmjs.org/wireit/-/wireit-0.15.0-pre.2.tgz", + "integrity": "sha512-pXOTR56btrL7STFOPQgtq8MjAFWagSqs188E2FflCgcxk5uc0Xbn8CuLIR9FbqK97U3Jw6AK8zDEu/M/9ENqgA==", + "dev": true, + "license": "Apache-2.0", + "workspaces": [ + "vscode-extension", + "website" + ], + "dependencies": { + "brace-expansion": "^4.0.0", + "chokidar": "^3.5.3", + "fast-glob": "^3.2.11", + "jsonc-parser": "^3.0.0", + "proper-lockfile": "^4.1.2" + }, + "bin": { + "wireit": "bin/wireit.js" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/wireit/node_modules/balanced-match": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-3.0.1.tgz", + "integrity": "sha512-vjtV3hiLqYDNRoiAv0zC4QaGAMPomEoq83PRmYIofPswwZurCeWR5LByXm7SyoL0Zh5+2z0+HC7jG8gSZJUh0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 16" + } + }, + "node_modules/wireit/node_modules/brace-expansion": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-4.0.1.tgz", + "integrity": "sha512-YClrbvTCXGe70pU2JiEiPLYXO9gQkyxYeKpJIQHVS/gOs6EWMQP2RYBwjFLNT322Ji8TOC3IMPfsYCedNpzKfA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^3.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/ws": { + "version": "8.18.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", + "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "shell": { + "name": "@a2ui/shell", + "version": "0.8.1", + "license": "Apache-2.0", + "dependencies": { + "@a2a-js/sdk": "^0.3.4", + "@a2ui/lit": "file:../../../../renderers/lit", + "@google/genai": "^1.22.0", + "@lit-labs/signals": "^0.1.3", + "@lit/context": "^1.1.4", + "@types/node": "^24.7.1", + "lit": "^3.3.1" + }, + "devDependencies": { + "dotenv": "^17.2.3", + "typescript": "^5.8.3", + "uuid": "^13.0.0", + "vite": "^7.1.11", + "wireit": "^0.15.0-pre.2" + } + } + } +} diff --git a/examples/agent/a2ui_agent/samples/client/lit/package.json b/examples/agent/a2ui_agent/samples/client/lit/package.json new file mode 100644 index 0000000000..64733cecab --- /dev/null +++ b/examples/agent/a2ui_agent/samples/client/lit/package.json @@ -0,0 +1,24 @@ +{ + "name": "@a2ui/lit-samples", + "private": true, + "version": "0.8.1", + "description": "A2UI Lit Samples", + "workspaces": [ + "contact", + "restaurant", + "shell" + ], + "scripts": { + "serve:agent:restaurant": "cd ../../agent/general_agent && uv run .", + "serve:agent:contact_example": "cd ../../agent/as/contact_example && uv run .", + "serve:agent:contact_multi_surface": "cd ../../agent/adk/contact_multiple_surfaces && uv run .", + "serve:shell": "cd shell && npm run dev", + "build:renderer": "cd ../../../renderers/lit && npm install && npm run build", + "demo:all": "npm install && npm run build:renderer && concurrently -k -n \"SHELL,REST,CONT1\" -c \"magenta,blue,green\" \"npm run serve:shell\" \"npm run serve:agent:restaurant\" \"npm run serve:agent:contact_example\"", + "demo:restaurant": "npm install && npm run build:renderer && concurrently -k -n \"SHELL,REST\" -c \"magenta,blue\" \"npm run serve:shell\" \"npm run serve:agent:restaurant\"", + "demo:contact_example": "npm install && npm run build:renderer && concurrently -k -n \"SHELL,CONT1\" -c \"magenta,green\" \"npm run serve:shell\" \"npm run serve:agent:contact_example\"" + }, + "devDependencies": { + "concurrently": "9.2.1" + } +} diff --git a/examples/agent/a2ui_agent/samples/client/lit/shell/.vite/deps/_metadata.json b/examples/agent/a2ui_agent/samples/client/lit/shell/.vite/deps/_metadata.json new file mode 100644 index 0000000000..97fc992bd5 --- /dev/null +++ b/examples/agent/a2ui_agent/samples/client/lit/shell/.vite/deps/_metadata.json @@ -0,0 +1,8 @@ +{ + "hash": "b676c12b", + "configHash": "2fd5a0c6", + "lockfileHash": "e3b0c442", + "browserHash": "17bb8bb5", + "optimized": {}, + "chunks": {} +} \ No newline at end of file diff --git a/examples/agent/a2ui_agent/samples/client/lit/shell/.vite/deps/package.json b/examples/agent/a2ui_agent/samples/client/lit/shell/.vite/deps/package.json new file mode 100644 index 0000000000..3dbc1ca591 --- /dev/null +++ b/examples/agent/a2ui_agent/samples/client/lit/shell/.vite/deps/package.json @@ -0,0 +1,3 @@ +{ + "type": "module" +} diff --git a/examples/agent/a2ui_agent/samples/client/lit/shell/README.md b/examples/agent/a2ui_agent/samples/client/lit/shell/README.md new file mode 100644 index 0000000000..cede61b2b8 --- /dev/null +++ b/examples/agent/a2ui_agent/samples/client/lit/shell/README.md @@ -0,0 +1,38 @@ +# A2UI Generator + +This is a UI to generate and visualize A2UI responses. + +## Prerequisites + +1. [nodejs](https://nodejs.org/en) + +## Running + +This sample depends on the Lit renderer. Before running this sample, you need to build the renderer. + +1. **Build the renderer:** + ```bash + cd ../../../renderers/lit + npm install + npm run build + ``` + +2. **Run this sample:** + ```bash + cd - # back to the sample directory + npm install + ``` + +3. **Run the servers:** + - Run the [A2A server](../../../agent/general_agent/) + - Run the dev server: `npm run dev` + +After starting the dev server, you can open http://localhost:5173/ to view the sample. + +Important: The sample code provided is for demonstration purposes and illustrates the mechanics of A2UI and the Agent-to-Agent (A2A) protocol. When building production applications, it is critical to treat any agent operating outside of your direct control as a potentially untrusted entity. + +All operational data received from an external agent—including its AgentCard, messages, artifacts, and task statuses—should be handled as untrusted input. For example, a malicious agent could provide crafted data in its fields (e.g., name, skills.description) that, if used without sanitization to construct prompts for a Large Language Model (LLM), could expose your application to prompt injection attacks. + +Similarly, any UI definition or data stream received must be treated as untrusted. Malicious agents could attempt to spoof legitimate interfaces to deceive users (phishing), inject malicious scripts via property values (XSS), or generate excessive layout complexity to degrade client performance (DoS). If your application supports optional embedded content (such as iframes or web views), additional care must be taken to prevent exposure to malicious external sites. + +Developer Responsibility: Failure to properly validate data and strictly sandbox rendered content can introduce severe vulnerabilities. Developers are responsible for implementing appropriate security measures—such as input sanitization, Content Security Policies (CSP), strict isolation for optional embedded content, and secure credential handling—to protect their systems and users. \ No newline at end of file diff --git a/examples/agent/a2ui_agent/samples/client/lit/shell/THEMING.md b/examples/agent/a2ui_agent/samples/client/lit/shell/THEMING.md new file mode 100644 index 0000000000..c2aa83d12f --- /dev/null +++ b/examples/agent/a2ui_agent/samples/client/lit/shell/THEMING.md @@ -0,0 +1,163 @@ +# A2UI Theming & Configuration Guide + +This guide explains how the Universal App Shell handles theming and how to add new sample applications seamlessly. + +## Architecture Overview + +The styling system is built on two distinct layers: + +### 1. **Base Layer (`default-theme.ts`)** + +- **Role**: Structural & Functional Styles. +- **What it does**: Maps A2UI components (like `Text`, `Card`, `Row`) to functional CSS utility classes (e.g., `layout-w-100`, `typography-f-sf`). +- **When to touch**: Rarely. Only if you need to change the fundamental layout behavior of a component across all shell apps. + +### 2. **Configuration Layer (`configs/*.ts`)** + +- **Role**: App Identity & Brand Overrides. +- **What it does**: Allows for app-level theme overrides. +- **Key Mechanism**: The `AppConfig` interface allows you to provide a new theme by setting items in the `theme` property. +- **When to touch**: Whenever you add a new app and want to change an app's theme from the default theme provided with the shell. + +--- + +## How to Add a New Sample App + +Follow these steps to add a new application (e.g., "Flight Booker") with its own unique theme. + +### Step 1: Create the Config + +Create a new file `configs/flights.ts`: + +```typescript +import { AppConfig } from "./types.js"; +import { cloneDefaultTheme } from "../theme/clone-default-theme.js"; + +const theme = cloneDefaultTheme(); +// Set your variables, e.g., theme.components.Card = { 'color-bgc-n100': true } + +export const config: AppConfig = { + key: "flights", + title: "Flight Booker", + heroImage: "/hero-flights.png", + heroImageDark: "/hero-flights-dark.png", // Optional + placeholder: "Where do you want to go?", + loadingText: ["Checking availability...", "Finding best rates..."], + serverUrl: "http://localhost:10004", // Your agent's URL + theme, // Apply the theme. +}; +``` + +### Step 2: Register the Config + +Update `app.ts` to include your new config: + +```typescript +import { config as flightsConfig } from "./configs/flights.js"; + +const configs: Record = { + restaurant: restaurantConfig, + contacts: contactsConfig, + flights: flightsConfig, // Add this line +}; +``` + +### Step 3: Run It + +Access your new app by adding the `app` query parameter: +`http://localhost:5173/?app=flights` + +The App Shell will automatically: + +1. Load your `flights` config. +2. Apply your theme to the A2UI root's theme context. +3. Connect to your specified `serverUrl`. + +--- + +## Reference: Styling Levers + +This section lists the available styling "levers" (utility classes) you can use in your `theme.ts` file or directly in your components. These are defined in the core library (`renderers/lit/src/0.8/styles`). + +### 1. Layout (`layout-`) + +**Source:** `styles/layout.ts` + +| Category | Prefix | Scale/Values | Examples | +| :-------------- | :------------ | :------------------------------------------ | :---------------------------------------------------------- | +| **Padding** | `layout-p-` | 0-24 (1 = 4px) | `layout-p-4` (16px), `layout-pt-2` (Top 8px), `layout-px-4` | +| **Margin** | `layout-m-` | 0-24 (1 = 4px) | `layout-m-0`, `layout-mb-4` (Bottom 16px), `layout-mx-auto` | +| **Gap** | `layout-g-` | 0-24 (1 = 4px) | `layout-g-2` (8px), `layout-g-4` (16px) | +| **Width** | `layout-w-` | 10-100 (Percentage) | `layout-w-100` (100%), `layout-w-50` (50%) | +| **Width (Px)** | `layout-wp-` | 0-15 (1 = 4px) | `layout-wp-10` (40px) | +| **Height** | `layout-h-` | 10-100 (Percentage) | `layout-h-100` (100%) | +| **Height (Px)** | `layout-hp-` | 0-15 (1 = 4px) | `layout-hp-10` (40px) | +| **Display** | `layout-dsp-` | `none`, `block`, `grid`, `flex`, `iflex` | `layout-dsp-flexhor` (Row), `layout-dsp-flexvert` (Col) | +| **Alignment** | `layout-al-` | `fs` (Start), `fe` (End), `c` (Center) | `layout-al-c` (Align Items Center) | +| **Justify** | `layout-sp-` | `c` (Center), `bt` (Between), `ev` (Evenly) | `layout-sp-bt` (Justify Content Space Between) | +| **Flex** | `layout-flx-` | `0` (None), `1` (Grow) | `layout-flx-1` (Flex Grow 1) | +| **Position** | `layout-pos-` | `a` (Absolute), `rel` (Relative) | `layout-pos-rel` | + +### 2. Colors (`color-`) + +**Source:** `styles/colors.ts` + +| Category | Prefix | Scale/Values | Examples | +| :--------------- | :----------- | :------------------ | :-------------------------------------------------------------------- | +| **Text Color** | `color-c-` | Palette Key + Shade | `color-c-p50` (Primary), `color-c-n10` (Black), `color-c-e40` (Error) | +| **Background** | `color-bgc-` | Palette Key + Shade | `color-bgc-p100` (White/Lightest), `color-bgc-s30` (Secondary Dark) | +| **Border Color** | `color-bc-` | Palette Key + Shade | `color-bc-p60` (Primary Border) | + +**Palette Keys:** + +- `p` = Primary (Brand) +- `s` = Secondary +- `t` = Tertiary +- `n` = Neutral (Grays) +- `nv` = Neutral Variant +- `e` = Error + +**Shades:** 0, 5, 10, 15, 20, 25, 30, 35, 40, 50, 60, 70, 80, 90, 95, 98, 99, 100 + +### 3. Typography (`typography-`) + +**Source:** `styles/type.ts` + +| Category | Prefix | Scale/Values | Examples | +| :------------------ | :--------------- | :---------------------------------------- | :----------------------------------------------------------------------------------- | +| **Font Family** | `typography-f-` | `sf` (Sans/Flex), `s` (Serif), `c` (Code) | `typography-f-sf` (System UI / Outfit) | +| **Weight** | `typography-w-` | 100-900 | `typography-w-400` (Regular), `typography-w-500` (Medium), `typography-w-700` (Bold) | +| **Size (Body)** | `typography-sz-` | `bs`, `bm`, `bl` | `typography-sz-bm` (Body Medium - 14px) | +| **Size (Title)** | `typography-sz-` | `ts`, `tm`, `tl` | `typography-sz-tl` (Title Large - 22px) | +| **Size (Headline)** | `typography-sz-` | `hs`, `hm`, `hl` | `typography-sz-hl` (Headline Large - 32px) | +| **Size (Display)** | `typography-sz-` | `ds`, `dm`, `dl` | `typography-sz-dl` (Display Large - 57px) | +| **Align** | `typography-ta-` | `s` (Start), `c` (Center) | `typography-ta-c` | + +### 4. Borders (`border-`) + +**Source:** `styles/border.ts` + +| Category | Prefix | Scale/Values | Examples | +| :--------- | :----------- | :------------- | :---------------------------------------------------- | +| **Radius** | `border-br-` | 0-24 (1 = 4px) | `border-br-4` (16px), `border-br-50pc` (50% / Circle) | +| **Width** | `border-bw-` | 0-24 (Pixels) | `border-bw-1` (1px), `border-bw-2` (2px) | +| **Style** | `border-bs-` | `s` (Solid) | `border-bs-s` | + +### 5. Behavior & Opacity + +**Source:** `styles/behavior.ts`, `styles/opacity.ts` + +| Category | Prefix | Scale/Values | Examples | +| :---------------- | :------------- | :------------------------------------- | :-------------------------------------- | +| **Hover Opacity** | `behavior-ho-` | 0-100 (Step 5) | `behavior-ho-80` (Opacity 0.8 on hover) | +| **Opacity** | `opacity-el-` | 0-100 (Step 5) | `opacity-el-50` (Opacity 0.5) | +| **Overflow** | `behavior-o-` | `s` (Scroll), `a` (Auto), `h` (Hidden) | `behavior-o-h` | +| **Scrollbar** | `behavior-sw-` | `n` (None) | `behavior-sw-n` | + +### 6. Icons + +**Source:** `styles/icons.ts` + +- Class: `.g-icon` +- Variants: `.filled`, `.filled-heavy` +- Usage: `icon_name` diff --git a/examples/agent/a2ui_agent/samples/client/lit/shell/app.ts b/examples/agent/a2ui_agent/samples/client/lit/shell/app.ts new file mode 100644 index 0000000000..33a16cf575 --- /dev/null +++ b/examples/agent/a2ui_agent/samples/client/lit/shell/app.ts @@ -0,0 +1,560 @@ +/* + Copyright 2025 Google LLC + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import { SignalWatcher } from "@lit-labs/signals"; +import { provide } from "@lit/context"; +import { + LitElement, + html, + css, + nothing, + HTMLTemplateResult, + unsafeCSS, +} from "lit"; +import { customElement, state } from "lit/decorators.js"; +import { theme as uiTheme } from "./theme/default-theme.js"; +import { A2UIClient } from "./client.js"; +import { + SnackbarAction, + SnackbarMessage, + SnackbarUUID, + SnackType, +} from "./types/types.js"; +import { type Snackbar } from "./ui/snackbar.js"; +import { repeat } from "lit/directives/repeat.js"; +import { v0_8 } from "@a2ui/lit"; +import * as UI from "@a2ui/lit/ui"; + +// App elements. +import "./ui/ui.js"; + +// Configurations +import { AppConfig } from "./configs/types.js"; +import { config as restaurantConfig } from "./configs/restaurant.js"; +import { config as contactsConfig } from "./configs/contacts.js"; +import { styleMap } from "lit/directives/style-map.js"; + +const configs: Record = { + restaurant: restaurantConfig, + contacts: contactsConfig, +}; + +@customElement("a2ui-shell") +export class A2UILayoutEditor extends SignalWatcher(LitElement) { + @provide({ context: UI.Context.themeContext }) + accessor theme: v0_8.Types.Theme = uiTheme; + + @state() + accessor #requesting = false; + + @state() + accessor #error: string | null = null; + + @state() + accessor #lastMessages: v0_8.Types.ServerToClientMessage[] = []; + + @state() + accessor config: AppConfig = configs.restaurant; + + @state() + accessor #loadingTextIndex = 0; + #loadingInterval: number | undefined; + + static styles = [ + unsafeCSS(v0_8.Styles.structuralStyles), + css` + * { + box-sizing: border-box; + } + + :host { + display: block; + max-width: 640px; + margin: 0 auto; + min-height: 100%; + color: light-dark(var(--n-10), var(--n-90)); + font-family: var(--font-family); + } + + #hero-img { + width: 100%; + max-width: 400px; + aspect-ratio: 1280/720; + height: auto; + margin-bottom: var(--bb-grid-size-6); + display: block; + margin: 0 auto; + background: var(--background-image-light) center center / contain + no-repeat; + } + + #surfaces { + width: 100%; + max-width: 100svw; + padding: var(--bb-grid-size-3); + animation: fadeIn 1s cubic-bezier(0, 0, 0.3, 1) 0.3s backwards; + } + + form { + display: flex; + flex-direction: column; + flex: 1; + gap: 16px; + align-items: center; + padding: 16px 0; + animation: fadeIn 1s cubic-bezier(0, 0, 0.3, 1) 1s backwards; + + & h1 { + color: light-dark(var(--p-40), var(--n-90)); + } + + & > div { + display: flex; + flex: 1; + gap: 16px; + align-items: center; + width: 100%; + + & > input { + display: block; + flex: 1; + border-radius: 32px; + padding: 16px 24px; + border: 1px solid var(--p-60); + background: light-dark(var(--n-100), var(--n-10)); + font-size: 16px; + } + + & > button { + display: flex; + align-items: center; + background: var(--p-40); + color: var(--n-100); + border: none; + padding: 8px 16px; + border-radius: 32px; + opacity: 0.5; + + &:not([disabled]) { + cursor: pointer; + opacity: 1; + } + } + } + } + + .rotate { + animation: rotate 1s linear infinite; + } + + .pending { + width: 100%; + min-height: 200px; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + animation: fadeIn 1s cubic-bezier(0, 0, 0.3, 1) 0.3s backwards; + gap: 16px; + } + + .spinner { + width: 48px; + height: 48px; + border: 4px solid rgba(255, 255, 255, 0.1); + border-left-color: var(--p-60); + border-radius: 50%; + animation: spin 1s linear infinite; + } + + .theme-toggle { + padding: 0; + margin: 0; + border: none; + display: flex; + align-items: center; + justify-content: center; + position: fixed; + top: var(--bb-grid-size-3); + right: var(--bb-grid-size-4); + background: light-dark(var(--n-100), var(--n-0)); + border-radius: 50%; + color: var(--p-30); + cursor: pointer; + width: 48px; + height: 48px; + font-size: 32px; + + & .g-icon { + pointer-events: none; + + &::before { + content: "dark_mode"; + } + } + } + + @container style(--color-scheme: dark) { + .theme-toggle .g-icon::before { + content: "light_mode"; + color: var(--n-90); + } + + #hero-img { + background-image: var(--background-image-dark); + } + } + + @keyframes spin { + to { + transform: rotate(360deg); + } + } + + @keyframes pulse { + 0% { + opacity: 0.6; + } + 50% { + opacity: 1; + } + 100% { + opacity: 0.6; + } + } + + .error { + color: var(--e-40); + background-color: var(--e-95); + border: 1px solid var(--e-80); + padding: 16px; + border-radius: 8px; + } + + @keyframes fadeIn { + from { + opacity: 0; + } + + to { + opacity: 1; + } + } + + @keyframes rotate { + from { + rotate: 0deg; + } + + to { + rotate: 360deg; + } + } + `, + ]; + + #processor = v0_8.Data.createSignalA2uiMessageProcessor(); + #a2uiClient = new A2UIClient(); + #snackbar: Snackbar | undefined = undefined; + #pendingSnackbarMessages: Array<{ + message: SnackbarMessage; + replaceAll: boolean; + }> = []; + + #maybeRenderError() { + if (!this.#error) return nothing; + + return html`
${this.#error}
`; + } + + connectedCallback() { + super.connectedCallback(); + + // Load config from URL + const urlParams = new URLSearchParams(window.location.search); + const appKey = urlParams.get("app") || "restaurant"; + this.config = configs[appKey] || configs.restaurant; + + // Apply the theme directly, which will use the Lit context. + if (this.config.theme) { + this.theme = this.config.theme; + } + + window.document.title = this.config.title; + window.document.documentElement.style.setProperty( + "--background", + this.config.background + ); + + // Initialize client with configured URL + this.#a2uiClient = new A2UIClient(this.config.serverUrl); + } + + render() { + return [ + this.#renderThemeToggle(), + this.#maybeRenderForm(), + this.#maybeRenderData(), + this.#maybeRenderError(), + ]; + } + + #renderThemeToggle() { + return html`
+ +
`; + } + + #maybeRenderForm() { + if (this.#requesting) return nothing; + if (this.#lastMessages.length > 0) return nothing; + + return html`
{ + evt.preventDefault(); + if (!(evt.target instanceof HTMLFormElement)) { + return; + } + const data = new FormData(evt.target); + const body = data.get("body") ?? null; + if (!body) { + return; + } + const message = body as v0_8.Types.A2UIClientEventMessage; + await this.#sendAndProcessMessage(message); + }} + > + ${this.config.heroImage + ? html`
` + : nothing} +

${this.config.title}

+
+ + +
+
`; + } + + #startLoadingAnimation() { + if ( + Array.isArray(this.config.loadingText) && + this.config.loadingText.length > 1 + ) { + this.#loadingTextIndex = 0; + this.#loadingInterval = window.setInterval(() => { + this.#loadingTextIndex = + (this.#loadingTextIndex + 1) % + (this.config.loadingText as string[]).length; + }, 2000); + } + } + + #stopLoadingAnimation() { + if (this.#loadingInterval) { + clearInterval(this.#loadingInterval); + this.#loadingInterval = undefined; + } + } + + async #sendMessage( + message: v0_8.Types.A2UIClientEventMessage + ): Promise { + try { + this.#requesting = true; + this.#startLoadingAnimation(); + const response = this.#a2uiClient.send(message); + await response; + this.#requesting = false; + this.#stopLoadingAnimation(); + + return response; + } catch (err) { + this.snackbar(err as string, SnackType.ERROR); + } finally { + this.#requesting = false; + this.#stopLoadingAnimation(); + } + + return []; + } + + #maybeRenderData() { + if (this.#requesting) { + let text = "Awaiting an answer..."; + if (this.config.loadingText) { + if (Array.isArray(this.config.loadingText)) { + text = this.config.loadingText[this.#loadingTextIndex]; + } else { + text = this.config.loadingText; + } + } + + return html`
+
+
${text}
+
`; + } + + const surfaces = this.#processor.getSurfaces(); + if (surfaces.size === 0) { + return nothing; + } + + return html`
+ ${repeat( + this.#processor.getSurfaces(), + ([surfaceId]) => surfaceId, + ([surfaceId, surface]) => { + return html` + ) => { + const [target] = evt.composedPath(); + if (!(target instanceof HTMLElement)) { + return; + } + + const context: v0_8.Types.A2UIClientEventMessage["userAction"]["context"] = + {}; + if (evt.detail.action.context) { + const srcContext = evt.detail.action.context; + for (const item of srcContext) { + if (item.value.literalBoolean) { + context[item.key] = item.value.literalBoolean; + } else if (item.value.literalNumber) { + context[item.key] = item.value.literalNumber; + } else if (item.value.literalString) { + context[item.key] = item.value.literalString; + } else if (item.value.path) { + const path = this.#processor.resolvePath( + item.value.path, + evt.detail.dataContextPath + ); + const value = this.#processor.getData( + evt.detail.sourceComponent, + path, + surfaceId + ); + context[item.key] = value; + } + } + } + + const message: v0_8.Types.A2UIClientEventMessage = { + userAction: { + name: evt.detail.action.name, + surfaceId, + sourceComponentId: target.id, + timestamp: new Date().toISOString(), + context, + }, + }; + + await this.#sendAndProcessMessage(message); + }} + .surfaceId=${surfaceId} + .surface=${surface} + .processor=${this.#processor} + >`; + } + )} +
`; + } + + async #sendAndProcessMessage(request) { + const messages = await this.#sendMessage(request); + + console.log(messages); + + this.#lastMessages = messages; + this.#processor.clearSurfaces(); + this.#processor.processMessages(messages); + } + + snackbar( + message: string | HTMLTemplateResult, + type: SnackType, + actions: SnackbarAction[] = [], + persistent = false, + id = globalThis.crypto.randomUUID(), + replaceAll = false + ) { + if (!this.#snackbar) { + this.#pendingSnackbarMessages.push({ + message: { + id, + message, + type, + persistent, + actions, + }, + replaceAll, + }); + return; + } + + return this.#snackbar.show( + { + id, + message, + type, + persistent, + actions, + }, + replaceAll + ); + } + + unsnackbar(id?: SnackbarUUID) { + if (!this.#snackbar) { + return; + } + + this.#snackbar.hide(id); + } +} diff --git a/examples/agent/a2ui_agent/samples/client/lit/shell/client.ts b/examples/agent/a2ui_agent/samples/client/lit/shell/client.ts new file mode 100644 index 0000000000..339fea7242 --- /dev/null +++ b/examples/agent/a2ui_agent/samples/client/lit/shell/client.ts @@ -0,0 +1,127 @@ +/* + Copyright 2025 Google LLC + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import { Message, Part, SendMessageSuccessResponse, Task } from "@a2a-js/sdk"; +import { A2AClient } from "@a2a-js/sdk/client"; +import { v0_8 } from "@a2ui/lit"; + +const A2AUI_MIME_TYPE = "application/json+a2aui"; + +export class A2UIClient { + #serverUrl: string; + #client: A2AClient | null = null; + + constructor(serverUrl: string = "") { + this.#serverUrl = serverUrl; + } + + #ready: Promise = Promise.resolve(); + get ready() { + return this.#ready; + } + + async #getClient() { + if (!this.#client) { + // Default to localhost:10002 if no URL provided (fallback for restaurant app default) + const baseUrl = this.#serverUrl || "http://localhost:10002"; + + this.#client = await A2AClient.fromCardUrl( + `${baseUrl}/.well-known/agent-card.json`, + { + fetchImpl: async (url, init) => { + const headers = new Headers(init?.headers); + headers.set("X-A2A-Extensions", "https://a2ui.org/a2a-extension/a2ui/v0.8"); + return fetch(url, { ...init, headers }); + } + } + ); + } + return this.#client; + } + + async send( + message: v0_8.Types.A2UIClientEventMessage | string + ): Promise { + const client = await this.#getClient(); + + let parts: Part[] = []; + + if (typeof message === 'string') { + // Try to parse as JSON first, just in case + try { + const parsed = JSON.parse(message); + if (typeof parsed === 'object' && parsed !== null) { + parts = [{ + kind: "data", + data: parsed as unknown as Record, + mimeType: A2AUI_MIME_TYPE, + } as Part]; + } else { + parts = [{ kind: "text", text: message }]; + } + } catch { + parts = [{ kind: "text", text: message }]; + } + } else { + parts = [{ + kind: "data", + data: message as unknown as Record, + mimeType: A2AUI_MIME_TYPE, + } as Part]; + } + + const response = await client.sendMessage({ + message: { + messageId: crypto.randomUUID(), + role: "user", + parts: parts, + kind: "message", + }, + }); + + if ("error" in response) { + throw new Error(response.error.message); + } + + const result = (response as SendMessageSuccessResponse).result as Task; + if (result.kind === "task" && result.status.message?.parts) { + const messages: v0_8.Types.ServerToClientMessage[] = []; + for (const part of result.status.message.parts) { + if (part.kind === 'data') { + messages.push(part.data as v0_8.Types.ServerToClientMessage); + } + } + return messages; + } + + // const result = (response as SendMessageSuccessResponse).result as Message; + // if (result.kind === "message" && result.parts) { + // const messages: v0_8.Types.ServerToClientMessage[] = []; + // for (const part of result.parts) { + // if (part.kind === 'data') { + // // Parse data.arguments (JSON string) to object + // const parsedData = typeof part.data?.arguments === 'string' + // ? JSON.parse(part.data.arguments) + // : part.data?.arguments; + // messages.push(parsedData as v0_8.Types.ServerToClientMessage); + // } + // } + // return messages; + // } + + return []; + } +} diff --git a/examples/agent/a2ui_agent/samples/client/lit/shell/configs/contacts.ts b/examples/agent/a2ui_agent/samples/client/lit/shell/configs/contacts.ts new file mode 100644 index 0000000000..68256eba39 --- /dev/null +++ b/examples/agent/a2ui_agent/samples/client/lit/shell/configs/contacts.ts @@ -0,0 +1,346 @@ +/* + Copyright 2025 Google LLC + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import { cloneDefaultTheme } from "../theme/clone-default-theme.js"; +import { AppConfig } from "./types.js"; +import { v0_8 } from "@a2ui/lit"; + +/** Elements */ + +const a = { + "typography-f-sf": true, + "typography-fs-n": true, + "typography-w-500": true, + "layout-as-n": true, + "layout-dis-iflx": true, + "layout-al-c": true, +}; + +const heading = { + "typography-f-sf": true, + "typography-fs-n": true, + "typography-w-500": true, + "layout-mt-0": true, + "layout-mb-2": true, + "color-c-n10": true, +}; + +const orderedList = { + "typography-f-s": true, + "typography-fs-n": true, + "typography-w-400": true, + "layout-m-0": true, + "typography-sz-bm": true, + "layout-as-n": true, +}; + +const unorderedList = { + "typography-f-s": true, + "typography-fs-n": true, + "typography-w-400": true, + "layout-m-0": true, + "typography-sz-bm": true, + "layout-as-n": true, +}; + +const listItem = { + "typography-f-s": true, + "typography-fs-n": true, + "typography-w-400": true, + "layout-m-0": true, + "typography-sz-bm": true, + "layout-as-n": true, +}; + +const theme: v0_8.Types.Theme = { + ...cloneDefaultTheme(), + additionalStyles: { + Card: { + "min-width": "320px", + "max-width": "400px", + margin: "0 auto", + background: + "linear-gradient(135deg, light-dark(#ffffff99, #ffffff44) 0%, light-dark(#ffffff, #ffffff04) 100%)", + border: "1px solid light-dark(transparent, #ffffff35)", + boxShadow: + "inset 0 20px 48px light-dark(rgba(0, 0, 0, 0.02), rgba(255, 255, 255, 0.08))", + }, + Button: { + "--p-70": "light-dark(var(--p-60), var(--n-10))", + "--n-60": "light-dark(var(--n-100), var(--n-0))", + }, + Image: { + "max-width": "120px", + "max-height": "120px", + marginLeft: "auto", + marginRight: "auto", + }, + Text: { + "--n-40": "light-dark(var(--p-60), var(--n-90))", + }, + }, + components: { + AudioPlayer: {}, + Button: { + "layout-pt-2": true, + "layout-pb-2": true, + "layout-pl-5": true, + "layout-pr-5": true, + "border-br-2": true, + "border-bw-0": true, + "border-bs-s": true, + "color-bgc-p30": true, + "color-c-n100": true, + "behavior-ho-70": true, + }, + Card: { + "border-br-4": true, + "color-bgc-p100": true, + "layout-pt-10": true, + "layout-pb-10": true, + "layout-pl-4": true, + "layout-pr-4": true, + }, + CheckBox: { + element: { + "layout-m-0": true, + "layout-mr-2": true, + "layout-p-2": true, + "border-br-12": true, + "border-bw-1": true, + "border-bs-s": true, + "color-bgc-p100": true, + "color-bc-p60": true, + "color-c-n30": true, + "color-c-p30": true, + }, + label: { + "color-c-p30": true, + "typography-f-sf": true, + "typography-v-r": true, + "typography-w-400": true, + "layout-flx-1": true, + "typography-sz-ll": true, + }, + container: { + "layout-dsp-iflex": true, + "layout-al-c": true, + }, + }, + Column: {}, + DateTimeInput: { + container: {}, + label: {}, + element: { + "layout-pt-2": true, + "layout-pb-2": true, + "layout-pl-3": true, + "layout-pr-3": true, + "border-br-12": true, + "border-bw-1": true, + "border-bs-s": true, + "color-bgc-p100": true, + "color-bc-p60": true, + "color-c-n30": true, + }, + }, + Divider: { + "color-bgc-n90": true, + "layout-mt-6": true, + "layout-mb-6": true, + }, + Image: { + all: { + "border-br-50pc": true, + "layout-el-cv": true, + "layout-w-100": true, + "layout-h-100": true, + "layout-dsp-flexhor": true, + "layout-al-c": true, + "layout-sp-c": true, + "layout-mb-3": true, + }, + avatar: {}, + header: {}, + icon: {}, + largeFeature: {}, + mediumFeature: {}, + smallFeature: {}, + }, + Icon: { + "border-br-1": true, + "layout-p-2": true, + "color-bgc-n98": true, + "layout-dsp-flexhor": true, + "layout-al-c": true, + "layout-sp-c": true, + }, + List: { + "layout-g-4": true, + "layout-p-2": true, + }, + Modal: { + backdrop: { "color-bbgc-p60_20": true }, + element: { + "border-br-2": true, + "color-bgc-p100": true, + "layout-p-4": true, + "border-bw-1": true, + "border-bs-s": true, + "color-bc-p80": true, + }, + }, + MultipleChoice: { + container: {}, + label: {}, + element: {}, + }, + Row: { + "layout-g-4": true, + "layout-mb-3": true, + }, + Slider: { + container: {}, + label: {}, + element: {}, + }, + Tabs: { + container: {}, + controls: { all: {}, selected: {} }, + element: {}, + }, + Text: { + all: { + "layout-w-100": true, + "layout-g-2": true, + "color-c-p30": true, + }, + h1: { + "typography-f-sf": true, + "typography-ta-c": true, + "typography-v-r": true, + "typography-w-500": true, + "layout-mt-0": true, + "layout-mr-0": true, + "layout-ml-0": true, + "layout-mb-2": true, + "layout-p-0": true, + "typography-sz-tl": true, + }, + h2: { + "typography-f-sf": true, + "typography-ta-c": true, + "typography-v-r": true, + "typography-w-500": true, + "layout-mt-0": true, + "layout-mr-0": true, + "layout-ml-0": true, + "layout-mb-2": true, + "layout-p-0": true, + "typography-sz-tl": true, + }, + h3: { + "typography-f-sf": true, + "typography-ta-c": true, + "typography-v-r": true, + "typography-w-500": true, + "layout-mt-0": true, + "layout-mr-0": true, + "layout-ml-0": true, + "layout-mb-0": true, + "layout-p-0": true, + "typography-sz-ts": true, + }, + h4: { + "typography-f-sf": true, + "typography-ta-c": true, + "typography-v-r": true, + "typography-w-500": true, + "layout-mt-0": true, + "layout-mr-0": true, + "layout-ml-0": true, + "layout-mb-0": true, + "layout-p-0": true, + "typography-sz-bl": true, + }, + h5: { + "typography-f-sf": true, + "typography-ta-c": true, + "typography-v-r": true, + "typography-w-500": true, + "layout-mt-0": true, + "layout-mr-0": true, + "layout-ml-0": true, + "layout-mb-0": true, + "layout-p-0": true, + "color-c-n30": true, + "typography-sz-bm": true, + "layout-mb-1": true, + }, + body: {}, + caption: {}, + }, + TextField: { + container: { + "typography-sz-bm": true, + "layout-w-100": true, + "layout-g-2": true, + "layout-dsp-flexhor": true, + "layout-al-c": true, + }, + label: { + "layout-flx-0": true, + }, + element: { + "typography-sz-bm": true, + "layout-pt-2": true, + "layout-pb-2": true, + "layout-pl-3": true, + "layout-pr-3": true, + "border-br-12": true, + "border-bw-1": true, + "border-bs-s": true, + "color-bgc-p100": true, + "color-bc-p60": true, + "color-c-n30": true, + "color-c-p30": true, + }, + }, + Video: { + "border-br-5": true, + "layout-el-cv": true, + }, + }, +}; + +export const config: AppConfig = { + key: "contacts", + title: "Contact Manager", + background: `radial-gradient(at 0% 0%, light-dark(rgba(45, 212, 191, 0.4), rgba(20, 184, 166, 0.2)) 0px, transparent 50%), + radial-gradient(at 100% 0%, light-dark(rgba(56, 189, 248, 0.4), rgba(14, 165, 233, 0.2)) 0px, transparent 50%), + radial-gradient(at 100% 100%, light-dark(rgba(163, 230, 53, 0.4), rgba(132, 204, 22, 0.2)) 0px, transparent 50%), + radial-gradient(at 0% 100%, light-dark(rgba(52, 211, 153, 0.4), rgba(16, 185, 129, 0.2)) 0px, transparent 50%), + linear-gradient(120deg, light-dark(#f0fdf4, #022c22) 0%, light-dark(#dcfce7, #064e3b) 100%)`, + placeholder: "Alex Jordan", + loadingText: [ + "Searching contacts...", + "Looking up details...", + "Verifying information...", + "Just a moment...", + ], + serverUrl: "http://localhost:10003", + theme, +}; diff --git a/examples/agent/a2ui_agent/samples/client/lit/shell/configs/restaurant.ts b/examples/agent/a2ui_agent/samples/client/lit/shell/configs/restaurant.ts new file mode 100644 index 0000000000..5c4fb30e8e --- /dev/null +++ b/examples/agent/a2ui_agent/samples/client/lit/shell/configs/restaurant.ts @@ -0,0 +1,56 @@ +/* + Copyright 2025 Google LLC + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import { AppConfig } from "./types.js"; + +export const config: AppConfig = { + key: "restaurant", + title: "A2UI Agent", + heroImage: "/hero.png", + heroImageDark: "/hero-dark.png", + background: `radial-gradient( + at 0% 0%, + light-dark(rgba(161, 196, 253, 0.3), rgba(6, 182, 212, 0.15)) 0px, + transparent 50% + ), + radial-gradient( + at 100% 0%, + light-dark(rgba(255, 226, 226, 0.3), rgba(59, 130, 246, 0.15)) 0px, + transparent 50% + ), + radial-gradient( + at 100% 100%, + light-dark(rgba(162, 210, 255, 0.3), rgba(20, 184, 166, 0.15)) 0px, + transparent 50% + ), + radial-gradient( + at 0% 100%, + light-dark(rgba(255, 200, 221, 0.3), rgba(99, 102, 241, 0.15)) 0px, + transparent 50% + ), + linear-gradient( + 120deg, + light-dark(#f0f4f8, #0f172a) 0%, + light-dark(#e2e8f0, #1e293b) 100% + )`, + placeholder: "Help me test my MBTI.", + loadingText: [ + "Finding the infor for you...", + "Analyzing your information...", + "Almost there...", + ], + serverUrl: "http://localhost:10002", +}; diff --git a/examples/agent/a2ui_agent/samples/client/lit/shell/configs/types.ts b/examples/agent/a2ui_agent/samples/client/lit/shell/configs/types.ts new file mode 100644 index 0000000000..7f1c6b842b --- /dev/null +++ b/examples/agent/a2ui_agent/samples/client/lit/shell/configs/types.ts @@ -0,0 +1,41 @@ +/* + Copyright 2025 Google LLC + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import { v0_8 } from "@a2ui/lit"; + +/** + * Configuration interface for the Universal App Shell. + */ +export interface AppConfig { + /** Unique key for the app (e.g., 'restaurant', 'contacts') */ + key: string; + /** Display title of the application */ + title: string; + /** The background for the page */ + background?: string; + /** Path to the hero image */ + heroImage?: string; + /** Path to the hero image */ + heroImageDark?: string; + /** Placeholder text for the input field */ + placeholder: string; + /** Text to display while loading (optional). Can be a single string or an array of strings to rotate. */ + loadingText?: string | string[]; + /** Optional server URL for the agent (e.g., http://localhost:10003) */ + serverUrl?: string; + /** Theme overrides (CSS Variables) */ + theme?: v0_8.Types.Theme; +} diff --git a/examples/agent/a2ui_agent/samples/client/lit/shell/events/events.ts b/examples/agent/a2ui_agent/samples/client/lit/shell/events/events.ts new file mode 100644 index 0000000000..0ab66c3b76 --- /dev/null +++ b/examples/agent/a2ui_agent/samples/client/lit/shell/events/events.ts @@ -0,0 +1,35 @@ +/* + Copyright 2025 Google LLC + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import { HTMLTemplateResult } from "lit"; + +const eventInit = { + bubbles: true, + cancelable: true, + composed: true, +}; + +export class SnackbarActionEvent extends Event { + static eventName = "snackbaraction"; + + constructor( + public readonly action: string, + public readonly value?: HTMLTemplateResult | string, + public readonly callback?: () => void + ) { + super(SnackbarActionEvent.eventName, { ...eventInit }); + } +} diff --git a/examples/agent/a2ui_agent/samples/client/lit/shell/index.html b/examples/agent/a2ui_agent/samples/client/lit/shell/index.html new file mode 100644 index 0000000000..7cdfee444e --- /dev/null +++ b/examples/agent/a2ui_agent/samples/client/lit/shell/index.html @@ -0,0 +1,246 @@ + + + + + + + + Loading... + + + + + + + + + + + + diff --git a/examples/agent/a2ui_agent/samples/client/lit/shell/middleware/a2a.ts b/examples/agent/a2ui_agent/samples/client/lit/shell/middleware/a2a.ts new file mode 100644 index 0000000000..fc71c7b5f6 --- /dev/null +++ b/examples/agent/a2ui_agent/samples/client/lit/shell/middleware/a2a.ts @@ -0,0 +1,153 @@ +/* + Copyright 2025 Google LLC + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import { IncomingMessage, ServerResponse } from "http"; +import { Plugin, ViteDevServer } from "vite"; +import { A2AClient } from "@a2a-js/sdk/client"; +import { + MessageSendParams, + Part, + SendMessageSuccessResponse, + Task, +} from "@a2a-js/sdk"; +import { v4 as uuidv4 } from "uuid"; + +const A2AUI_MIME_TYPE = "application/json+a2aui"; + +const fetchWithCustomHeader: typeof fetch = async (url, init) => { + const headers = new Headers(init?.headers); + headers.set("X-A2A-Extensions", "https://a2ui.org/a2a-extension/a2ui/v0.8"); + + const newInit = { ...init, headers }; + return fetch(url, newInit); +}; + +const isJson = (str: string) => { + try { + const parsed = JSON.parse(str); + return ( + typeof parsed === "object" && parsed !== null && !Array.isArray(parsed) + ); + } catch (err) { + console.warn(err); + return false; + } +}; + +let client: A2AClient | null = null; +const createOrGetClient = async () => { + if (!client) { + // Create a client pointing to the agent's Agent Card URL. + client = await A2AClient.fromCardUrl( + "http://localhost:10002/.well-known/agent-card.json", + { fetchImpl: fetchWithCustomHeader } + ); + } + + return client; +}; + +export const plugin = (): Plugin => { + return { + name: "a2a-handler", + configureServer(server: ViteDevServer) { + server.middlewares.use( + "/a2a", + async (req: IncomingMessage, res: ServerResponse, next: () => void) => { + if (req.method === "POST") { + let originalBody = ""; + + req.on("data", (chunk) => { + originalBody += chunk.toString(); + }); + + req.on("end", async () => { + let sendParams: MessageSendParams; + + if (isJson(originalBody)) { + console.log( + "[a2a-middleware] Received JSON UI event:", + originalBody + ); + + const clientEvent = JSON.parse(originalBody); + sendParams = { + message: { + messageId: uuidv4(), + role: "user", + parts: [ + { + kind: "data", + data: clientEvent, + metadata: { 'mimeType': A2AUI_MIME_TYPE }, + } as Part, + ], + kind: "message", + }, + }; + } else { + console.log( + "[a2a-middleware] Received text query:", + originalBody + ); + sendParams = { + message: { + messageId: uuidv4(), + role: "user", + parts: [ + { + kind: "text", + text: originalBody, + }, + ], + kind: "message", + }, + }; + } + + const client = await createOrGetClient(); + const response = await client.sendMessage(sendParams); + if ("error" in response) { + console.error("Error:", response.error.message); + res.statusCode = 500; + res.setHeader("Content-Type", "application/json"); + res.end(JSON.stringify({ error: response.error.message })); + return; + } else { + const result = (response as SendMessageSuccessResponse) + .result as Task; + if (result.kind === "task") { + res.statusCode = 200; + res.setHeader("Content-Type", "application/json"); + res.end(JSON.stringify(result.status.message?.parts)); + return; + } + } + + res.statusCode = 200; + res.setHeader("Content-Type", "application/json"); + res.end(JSON.stringify([])); + }); + + return; + } else { + next(); + } + } + ); + }, + }; +}; diff --git a/examples/agent/a2ui_agent/samples/client/lit/shell/middleware/index.ts b/examples/agent/a2ui_agent/samples/client/lit/shell/middleware/index.ts new file mode 100644 index 0000000000..4ecec532c4 --- /dev/null +++ b/examples/agent/a2ui_agent/samples/client/lit/shell/middleware/index.ts @@ -0,0 +1,17 @@ +/* + Copyright 2025 Google LLC + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +export * as A2AMiddleware from "./a2a.js"; diff --git a/examples/agent/a2ui_agent/samples/client/lit/shell/package-lock.json b/examples/agent/a2ui_agent/samples/client/lit/shell/package-lock.json new file mode 100644 index 0000000000..6070b3eb54 --- /dev/null +++ b/examples/agent/a2ui_agent/samples/client/lit/shell/package-lock.json @@ -0,0 +1,2543 @@ +{ + "name": "@a2ui/shell", + "version": "0.8.1", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "@a2ui/shell", + "version": "0.8.1", + "license": "Apache-2.0", + "dependencies": { + "@a2a-js/sdk": "^0.3.4", + "@a2ui/lit": "file:../../../../renderers/lit", + "@google/genai": "^1.22.0", + "@lit-labs/signals": "^0.1.3", + "@lit/context": "^1.1.4", + "@types/node": "^24.7.1", + "lit": "^3.3.1" + }, + "devDependencies": { + "dotenv": "^17.2.3", + "typescript": "^5.8.3", + "uuid": "^13.0.0", + "vite": "^7.1.11", + "wireit": "^0.15.0-pre.2" + } + }, + "../../../../renderers/lit": { + "name": "@a2ui/lit", + "version": "0.8.1", + "license": "Apache-2.0", + "dependencies": { + "@lit-labs/signals": "^0.1.3", + "@lit/context": "^1.1.4", + "lit": "^3.3.1", + "markdown-it": "^14.1.0", + "signal-utils": "^0.21.1" + }, + "devDependencies": { + "@types/markdown-it": "^14.1.2", + "@types/node": "^24.10.1", + "typescript": "^5.8.3", + "wireit": "^0.15.0-pre.2" + } + }, + "node_modules/@a2a-js/sdk": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@a2a-js/sdk/-/sdk-0.3.5.tgz", + "integrity": "sha512-6xAApkiss2aCbJXmXLC845tifcbYJ/R4Dj22kQsOaanMbf9bvkYhebDEuYPAIu3aaR5MWaBqG7OCK3IF8dqZZQ==", + "dependencies": { + "uuid": "^11.1.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "express": "^4.21.2 || ^5.1.0" + }, + "peerDependenciesMeta": { + "express": { + "optional": true + } + } + }, + "node_modules/@a2a-js/sdk/node_modules/uuid": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz", + "integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/esm/bin/uuid" + } + }, + "node_modules/@a2ui/lit": { + "resolved": "../../../../renderers/lit", + "link": true + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", + "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz", + "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz", + "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz", + "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz", + "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz", + "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz", + "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz", + "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz", + "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz", + "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz", + "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz", + "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz", + "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz", + "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz", + "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz", + "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz", + "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz", + "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz", + "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz", + "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz", + "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz", + "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz", + "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz", + "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz", + "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz", + "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@google/genai": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/@google/genai/-/genai-1.32.0.tgz", + "integrity": "sha512-46vaEaHAThIBlqWFTti1fo3xYU6DwCOwnIIotLhYUbNha90wk5cZL79zdf+NoAfKVsx4DPmjCtXvbQNNVPl5ZQ==", + "license": "Apache-2.0", + "dependencies": { + "google-auth-library": "^10.3.0", + "ws": "^8.18.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "@modelcontextprotocol/sdk": "^1.24.0" + }, + "peerDependenciesMeta": { + "@modelcontextprotocol/sdk": { + "optional": true + } + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@lit-labs/signals": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@lit-labs/signals/-/signals-0.1.3.tgz", + "integrity": "sha512-P0yWgH5blwVyEwBg+WFspLzeu1i0ypJP1QB0l1Omr9qZLIPsUu0p4Fy2jshOg7oQyha5n163K3GJGeUhQQ682Q==", + "license": "BSD-3-Clause", + "dependencies": { + "lit": "^2.0.0 || ^3.0.0", + "signal-polyfill": "^0.2.0" + } + }, + "node_modules/@lit-labs/ssr-dom-shim": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@lit-labs/ssr-dom-shim/-/ssr-dom-shim-1.4.0.tgz", + "integrity": "sha512-ficsEARKnmmW5njugNYKipTm4SFnbik7CXtoencDZzmzo/dQ+2Q0bgkzJuoJP20Aj0F+izzJjOqsnkd6F/o1bw==", + "license": "BSD-3-Clause" + }, + "node_modules/@lit/context": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@lit/context/-/context-1.1.6.tgz", + "integrity": "sha512-M26qDE6UkQbZA2mQ3RjJ3Gzd8TxP+/0obMgE5HfkfLhEEyYE3Bui4A5XHiGPjy0MUGAyxB3QgVuw2ciS0kHn6A==", + "license": "BSD-3-Clause", + "dependencies": { + "@lit/reactive-element": "^1.6.2 || ^2.1.0" + } + }, + "node_modules/@lit/reactive-element": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@lit/reactive-element/-/reactive-element-2.1.1.tgz", + "integrity": "sha512-N+dm5PAYdQ8e6UlywyyrgI2t++wFGXfHx+dSJ1oBrg6FAxUj40jId++EaRm80MKX5JnlH1sBsyZ5h0bcZKemCg==", + "license": "BSD-3-Clause", + "dependencies": { + "@lit-labs/ssr-dom-shim": "^1.4.0" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.53.3.tgz", + "integrity": "sha512-mRSi+4cBjrRLoaal2PnqH82Wqyb+d3HsPUN/W+WslCXsZsyHa9ZeQQX/pQsZaVIWDkPcpV6jJ+3KLbTbgnwv8w==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.53.3.tgz", + "integrity": "sha512-CbDGaMpdE9sh7sCmTrTUyllhrg65t6SwhjlMJsLr+J8YjFuPmCEjbBSx4Z/e4SmDyH3aB5hGaJUP2ltV/vcs4w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.53.3.tgz", + "integrity": "sha512-Nr7SlQeqIBpOV6BHHGZgYBuSdanCXuw09hon14MGOLGmXAFYjx1wNvquVPmpZnl0tLjg25dEdr4IQ6GgyToCUA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.53.3.tgz", + "integrity": "sha512-DZ8N4CSNfl965CmPktJ8oBnfYr3F8dTTNBQkRlffnUarJ2ohudQD17sZBa097J8xhQ26AwhHJ5mvUyQW8ddTsQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.53.3.tgz", + "integrity": "sha512-yMTrCrK92aGyi7GuDNtGn2sNW+Gdb4vErx4t3Gv/Tr+1zRb8ax4z8GWVRfr3Jw8zJWvpGHNpss3vVlbF58DZ4w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.53.3.tgz", + "integrity": "sha512-lMfF8X7QhdQzseM6XaX0vbno2m3hlyZFhwcndRMw8fbAGUGL3WFMBdK0hbUBIUYcEcMhVLr1SIamDeuLBnXS+Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.53.3.tgz", + "integrity": "sha512-k9oD15soC/Ln6d2Wv/JOFPzZXIAIFLp6B+i14KhxAfnq76ajt0EhYc5YPeX6W1xJkAdItcVT+JhKl1QZh44/qw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.53.3.tgz", + "integrity": "sha512-vTNlKq+N6CK/8UktsrFuc+/7NlEYVxgaEgRXVUVK258Z5ymho29skzW1sutgYjqNnquGwVUObAaxae8rZ6YMhg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.53.3.tgz", + "integrity": "sha512-RGrFLWgMhSxRs/EWJMIFM1O5Mzuz3Xy3/mnxJp/5cVhZ2XoCAxJnmNsEyeMJtpK+wu0FJFWz+QF4mjCA7AUQ3w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.53.3.tgz", + "integrity": "sha512-kASyvfBEWYPEwe0Qv4nfu6pNkITLTb32p4yTgzFCocHnJLAHs+9LjUu9ONIhvfT/5lv4YS5muBHyuV84epBo/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.53.3.tgz", + "integrity": "sha512-JiuKcp2teLJwQ7vkJ95EwESWkNRFJD7TQgYmCnrPtlu50b4XvT5MOmurWNrCj3IFdyjBQ5p9vnrX4JM6I8OE7g==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.53.3.tgz", + "integrity": "sha512-EoGSa8nd6d3T7zLuqdojxC20oBfNT8nexBbB/rkxgKj5T5vhpAQKKnD+h3UkoMuTyXkP5jTjK/ccNRmQrPNDuw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.53.3.tgz", + "integrity": "sha512-4s+Wped2IHXHPnAEbIB0YWBv7SDohqxobiiPA1FIWZpX+w9o2i4LezzH/NkFUl8LRci/8udci6cLq+jJQlh+0g==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.53.3.tgz", + "integrity": "sha512-68k2g7+0vs2u9CxDt5ktXTngsxOQkSEV/xBbwlqYcUrAVh6P9EgMZvFsnHy4SEiUl46Xf0IObWVbMvPrr2gw8A==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.53.3.tgz", + "integrity": "sha512-VYsFMpULAz87ZW6BVYw3I6sWesGpsP9OPcyKe8ofdg9LHxSbRMd7zrVrr5xi/3kMZtpWL/wC+UIJWJYVX5uTKg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.53.3.tgz", + "integrity": "sha512-3EhFi1FU6YL8HTUJZ51imGJWEX//ajQPfqWLI3BQq4TlvHy4X0MOr5q3D2Zof/ka0d5FNdPwZXm3Yyib/UEd+w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.53.3.tgz", + "integrity": "sha512-eoROhjcc6HbZCJr+tvVT8X4fW3/5g/WkGvvmwz/88sDtSJzO7r/blvoBDgISDiCjDRZmHpwud7h+6Q9JxFwq1Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.53.3.tgz", + "integrity": "sha512-OueLAWgrNSPGAdUdIjSWXw+u/02BRTcnfw9PN41D2vq/JSEPnJnVuBgw18VkN8wcd4fjUs+jFHVM4t9+kBSNLw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.53.3.tgz", + "integrity": "sha512-GOFuKpsxR/whszbF/bzydebLiXIHSgsEUp6M0JI8dWvi+fFa1TD6YQa4aSZHtpmh2/uAlj/Dy+nmby3TJ3pkTw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.53.3.tgz", + "integrity": "sha512-iah+THLcBJdpfZ1TstDFbKNznlzoxa8fmnFYK4V67HvmuNYkVdAywJSoteUszvBQ9/HqN2+9AZghbajMsFT+oA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.53.3.tgz", + "integrity": "sha512-J9QDiOIZlZLdcot5NXEepDkstocktoVjkaKUtqzgzpt2yWjGlbYiKyp05rWwk4nypbYUNoFAztEgixoLaSETkg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.53.3.tgz", + "integrity": "sha512-UhTd8u31dXadv0MopwGgNOBpUVROFKWVQgAg5N1ESyCz8AuBcMqm4AuTjrwgQKGDfoFuz02EuMRHQIw/frmYKQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "24.10.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.2.tgz", + "integrity": "sha512-WOhQTZ4G8xZ1tjJTvKOpyEVSGgOTvJAfDK3FNFgELyaTpzhdgHVHeqW8V+UJvzF5BT+/B54T/1S2K6gd9c7bbA==", + "license": "MIT", + "peer": true, + "dependencies": { + "undici-types": "~7.16.0" + } + }, + "node_modules/@types/trusted-types": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", + "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", + "license": "MIT" + }, + "node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/anymatch/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "license": "MIT" + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/bignumber.js": { + "version": "9.3.1", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.3.1.tgz", + "integrity": "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==", + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", + "license": "BSD-3-Clause" + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/data-uri-to-buffer": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", + "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/dotenv": { + "version": "17.2.3", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.3.tgz", + "integrity": "sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "license": "MIT" + }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "license": "MIT" + }, + "node_modules/esbuild": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz", + "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.12", + "@esbuild/android-arm": "0.25.12", + "@esbuild/android-arm64": "0.25.12", + "@esbuild/android-x64": "0.25.12", + "@esbuild/darwin-arm64": "0.25.12", + "@esbuild/darwin-x64": "0.25.12", + "@esbuild/freebsd-arm64": "0.25.12", + "@esbuild/freebsd-x64": "0.25.12", + "@esbuild/linux-arm": "0.25.12", + "@esbuild/linux-arm64": "0.25.12", + "@esbuild/linux-ia32": "0.25.12", + "@esbuild/linux-loong64": "0.25.12", + "@esbuild/linux-mips64el": "0.25.12", + "@esbuild/linux-ppc64": "0.25.12", + "@esbuild/linux-riscv64": "0.25.12", + "@esbuild/linux-s390x": "0.25.12", + "@esbuild/linux-x64": "0.25.12", + "@esbuild/netbsd-arm64": "0.25.12", + "@esbuild/netbsd-x64": "0.25.12", + "@esbuild/openbsd-arm64": "0.25.12", + "@esbuild/openbsd-x64": "0.25.12", + "@esbuild/openharmony-arm64": "0.25.12", + "@esbuild/sunos-x64": "0.25.12", + "@esbuild/win32-arm64": "0.25.12", + "@esbuild/win32-ia32": "0.25.12", + "@esbuild/win32-x64": "0.25.12" + } + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "license": "MIT" + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fastq": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", + "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/fetch-blob": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", + "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "paypal", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "dependencies": { + "node-domexception": "^1.0.0", + "web-streams-polyfill": "^3.0.3" + }, + "engines": { + "node": "^12.20 || >= 14.13" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/formdata-polyfill": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", + "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", + "license": "MIT", + "dependencies": { + "fetch-blob": "^3.1.2" + }, + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/gaxios": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-7.1.3.tgz", + "integrity": "sha512-YGGyuEdVIjqxkxVH1pUTMY/XtmmsApXrCVv5EU25iX6inEPbV+VakJfLealkBtJN69AQmh1eGOdCl9Sm1UP6XQ==", + "license": "Apache-2.0", + "dependencies": { + "extend": "^3.0.2", + "https-proxy-agent": "^7.0.1", + "node-fetch": "^3.3.2", + "rimraf": "^5.0.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/gcp-metadata": { + "version": "8.1.2", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-8.1.2.tgz", + "integrity": "sha512-zV/5HKTfCeKWnxG0Dmrw51hEWFGfcF2xiXqcA3+J90WDuP0SvoiSO5ORvcBsifmx/FoIjgQN3oNOGaQ5PhLFkg==", + "license": "Apache-2.0", + "dependencies": { + "gaxios": "^7.0.0", + "google-logging-utils": "^1.0.0", + "json-bigint": "^1.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/google-auth-library": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-10.5.0.tgz", + "integrity": "sha512-7ABviyMOlX5hIVD60YOfHw4/CxOfBhyduaYB+wbFWCWoni4N7SLcV46hrVRktuBbZjFC9ONyqamZITN7q3n32w==", + "license": "Apache-2.0", + "dependencies": { + "base64-js": "^1.3.0", + "ecdsa-sig-formatter": "^1.0.11", + "gaxios": "^7.0.0", + "gcp-metadata": "^8.0.0", + "google-logging-utils": "^1.0.0", + "gtoken": "^8.0.0", + "jws": "^4.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/google-logging-utils": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/google-logging-utils/-/google-logging-utils-1.1.3.tgz", + "integrity": "sha512-eAmLkjDjAFCVXg7A1unxHsLf961m6y17QFqXqAXGj/gVkKFrEICfStRfwUlGNfeCEjNRa32JEWOUTlYXPyyKvA==", + "license": "Apache-2.0", + "engines": { + "node": ">=14" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/gtoken": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-8.0.0.tgz", + "integrity": "sha512-+CqsMbHPiSTdtSO14O51eMNlrp9N79gmeqmXeouJOhfucAedHw9noVe/n5uJk3tbKE6a+6ZCQg3RPhVhHByAIw==", + "license": "MIT", + "dependencies": { + "gaxios": "^7.0.0", + "jws": "^4.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "license": "ISC" + }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/json-bigint": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz", + "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==", + "license": "MIT", + "dependencies": { + "bignumber.js": "^9.0.0" + } + }, + "node_modules/jsonc-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", + "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/jwa": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz", + "integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==", + "license": "MIT", + "dependencies": { + "buffer-equal-constant-time": "^1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.1.tgz", + "integrity": "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==", + "license": "MIT", + "dependencies": { + "jwa": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/lit": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/lit/-/lit-3.3.1.tgz", + "integrity": "sha512-Ksr/8L3PTapbdXJCk+EJVB78jDodUMaP54gD24W186zGRARvwrsPfS60wae/SSCTCNZVPd1chXqio1qHQmu4NA==", + "license": "BSD-3-Clause", + "dependencies": { + "@lit/reactive-element": "^2.1.0", + "lit-element": "^4.2.0", + "lit-html": "^3.3.0" + } + }, + "node_modules/lit-element": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/lit-element/-/lit-element-4.2.1.tgz", + "integrity": "sha512-WGAWRGzirAgyphK2urmYOV72tlvnxw7YfyLDgQ+OZnM9vQQBQnumQ7jUJe6unEzwGU3ahFOjuz1iz1jjrpCPuw==", + "license": "BSD-3-Clause", + "dependencies": { + "@lit-labs/ssr-dom-shim": "^1.4.0", + "@lit/reactive-element": "^2.1.0", + "lit-html": "^3.3.0" + } + }, + "node_modules/lit-html": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/lit-html/-/lit-html-3.3.1.tgz", + "integrity": "sha512-S9hbyDu/vs1qNrithiNyeyv64c9yqiW9l+DBgI18fL+MTvOtWoFR0FWiyq1TxaYef5wNlpEmzlXoBlZEO+WjoA==", + "license": "BSD-3-Clause", + "dependencies": { + "@types/trusted-types": "^2.0.2" + } + }, + "node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "license": "ISC" + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/micromatch/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", + "deprecated": "Use your platform's native DOMException instead", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "github", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "engines": { + "node": ">=10.5.0" + } + }, + "node_modules/node-fetch": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", + "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", + "license": "MIT", + "dependencies": { + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/node-fetch" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "license": "BlueOak-1.0.0" + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/proper-lockfile": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/proper-lockfile/-/proper-lockfile-4.1.2.tgz", + "integrity": "sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "retry": "^0.12.0", + "signal-exit": "^3.0.2" + } + }, + "node_modules/proper-lockfile/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/readdirp/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "5.0.10", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.10.tgz", + "integrity": "sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ==", + "license": "ISC", + "dependencies": { + "glob": "^10.3.7" + }, + "bin": { + "rimraf": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rollup": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.53.3.tgz", + "integrity": "sha512-w8GmOxZfBmKknvdXU1sdM9NHcoQejwF/4mNgj2JuEEdRaHwwF12K7e9eXn1nLZ07ad+du76mkVsyeb2rKGllsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.53.3", + "@rollup/rollup-android-arm64": "4.53.3", + "@rollup/rollup-darwin-arm64": "4.53.3", + "@rollup/rollup-darwin-x64": "4.53.3", + "@rollup/rollup-freebsd-arm64": "4.53.3", + "@rollup/rollup-freebsd-x64": "4.53.3", + "@rollup/rollup-linux-arm-gnueabihf": "4.53.3", + "@rollup/rollup-linux-arm-musleabihf": "4.53.3", + "@rollup/rollup-linux-arm64-gnu": "4.53.3", + "@rollup/rollup-linux-arm64-musl": "4.53.3", + "@rollup/rollup-linux-loong64-gnu": "4.53.3", + "@rollup/rollup-linux-ppc64-gnu": "4.53.3", + "@rollup/rollup-linux-riscv64-gnu": "4.53.3", + "@rollup/rollup-linux-riscv64-musl": "4.53.3", + "@rollup/rollup-linux-s390x-gnu": "4.53.3", + "@rollup/rollup-linux-x64-gnu": "4.53.3", + "@rollup/rollup-linux-x64-musl": "4.53.3", + "@rollup/rollup-openharmony-arm64": "4.53.3", + "@rollup/rollup-win32-arm64-msvc": "4.53.3", + "@rollup/rollup-win32-ia32-msvc": "4.53.3", + "@rollup/rollup-win32-x64-gnu": "4.53.3", + "@rollup/rollup-win32-x64-msvc": "4.53.3", + "fsevents": "~2.3.2" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/signal-polyfill": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/signal-polyfill/-/signal-polyfill-0.2.2.tgz", + "integrity": "sha512-p63Y4Er5/eMQ9RHg0M0Y64NlsQKpiu6MDdhBXpyywRuWiPywhJTpKJ1iB5K2hJEbFZ0BnDS7ZkJ+0AfTuL37Rg==", + "license": "Apache-2.0" + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/string-width-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", + "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "license": "MIT" + }, + "node_modules/uuid": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-13.0.0.tgz", + "integrity": "sha512-XQegIaBTVUjSHliKqcnFqYypAd4S+WCYt5NIeRs6w/UAry7z8Y9j5ZwRRL4kzq9U3sD6v+85er9FvkEaBpji2w==", + "dev": true, + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist-node/bin/uuid" + } + }, + "node_modules/vite": { + "version": "7.2.7", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.2.7.tgz", + "integrity": "sha512-ITcnkFeR3+fI8P1wMgItjGrR10170d8auB4EpMLPqmx6uxElH3a/hHGQabSHKdqd4FXWO1nFIp9rRn7JQ34ACQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.25.0", + "fdir": "^6.5.0", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.43.0", + "tinyglobby": "^0.2.15" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "lightningcss": "^1.21.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/web-streams-polyfill": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", + "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wireit": { + "version": "0.15.0-pre.2", + "resolved": "https://registry.npmjs.org/wireit/-/wireit-0.15.0-pre.2.tgz", + "integrity": "sha512-pXOTR56btrL7STFOPQgtq8MjAFWagSqs188E2FflCgcxk5uc0Xbn8CuLIR9FbqK97U3Jw6AK8zDEu/M/9ENqgA==", + "dev": true, + "license": "Apache-2.0", + "workspaces": [ + "vscode-extension", + "website" + ], + "dependencies": { + "brace-expansion": "^4.0.0", + "chokidar": "^3.5.3", + "fast-glob": "^3.2.11", + "jsonc-parser": "^3.0.0", + "proper-lockfile": "^4.1.2" + }, + "bin": { + "wireit": "bin/wireit.js" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/wireit/node_modules/balanced-match": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-3.0.1.tgz", + "integrity": "sha512-vjtV3hiLqYDNRoiAv0zC4QaGAMPomEoq83PRmYIofPswwZurCeWR5LByXm7SyoL0Zh5+2z0+HC7jG8gSZJUh0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 16" + } + }, + "node_modules/wireit/node_modules/brace-expansion": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-4.0.1.tgz", + "integrity": "sha512-YClrbvTCXGe70pU2JiEiPLYXO9gQkyxYeKpJIQHVS/gOs6EWMQP2RYBwjFLNT322Ji8TOC3IMPfsYCedNpzKfA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^3.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ws": { + "version": "8.18.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", + "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + } + } +} diff --git a/examples/agent/a2ui_agent/samples/client/lit/shell/package.json b/examples/agent/a2ui_agent/samples/client/lit/shell/package.json new file mode 100644 index 0000000000..0264f58661 --- /dev/null +++ b/examples/agent/a2ui_agent/samples/client/lit/shell/package.json @@ -0,0 +1,86 @@ +{ + "name": "@a2ui/shell", + "private": true, + "version": "0.8.1", + "description": "A2UI Universal Shell", + "main": "./dist/shell.js", + "types": "./dist/shell.d.ts", + "type": "module", + "scripts": { + "prepack": "npm run build", + "build": "wireit", + "build:tsc": "wireit", + "dev": "npm run serve --watch", + "test": "wireit", + "serve": "wireit" + }, + "wireit": { + "serve": { + "command": "vite dev", + "dependencies": [ + "build" + ], + "service": true + }, + "test": { + "command": "node --test --enable-source-maps --test-reporter spec dist/src/0.8/tests/**/*.test.js", + "dependencies": [ + "build" + ] + }, + "build": { + "dependencies": [ + "build:tsc" + ] + }, + "build:tsc": { + "command": "tsc -b --pretty", + "env": { + "FORCE_COLOR": "1" + }, + "dependencies": [ + "../../../../renderers/lit:build:tsc" + ], + "files": [ + "**/*.ts", + "tsconfig.json" + ], + "output": [ + "dist/", + "!dist/**/*.min.js{,.map}" + ], + "clean": "if-file-deleted" + } + }, + "repository": { + "directory": "samples/client/lit/shell", + "type": "git", + "url": "git+https://github.com/google/A2UI.git" + }, + "files": [ + "dist" + ], + "keywords": [], + "author": "Google", + "license": "Apache-2.0", + "bugs": { + "url": "https://github.com/google/A2UI/issues" + }, + "homepage": "https://github.com/google/A2UI/tree/main/web#readme", + "devDependencies": { + "dotenv": "^17.2.3", + "typescript": "^5.8.3", + "uuid": "^13.0.0", + "vite": "^7.1.11", + "wireit": "^0.15.0-pre.2" + }, + "dependencies": { + "@a2a-js/sdk": "^0.3.4", + "@a2ui/lit": "file:../../../../renderers/lit", + "@google/genai": "^1.22.0", + "@lit-labs/signals": "^0.1.3", + "@lit/context": "^1.1.4", + "@types/node": "^24.7.1", + "lit": "^3.3.1" + } +} \ No newline at end of file diff --git a/examples/agent/a2ui_agent/samples/client/lit/shell/public/hero-dark.png b/examples/agent/a2ui_agent/samples/client/lit/shell/public/hero-dark.png new file mode 100644 index 0000000000..7ea83066d8 Binary files /dev/null and b/examples/agent/a2ui_agent/samples/client/lit/shell/public/hero-dark.png differ diff --git a/examples/agent/a2ui_agent/samples/client/lit/shell/public/hero.png b/examples/agent/a2ui_agent/samples/client/lit/shell/public/hero.png new file mode 100644 index 0000000000..419414c99c Binary files /dev/null and b/examples/agent/a2ui_agent/samples/client/lit/shell/public/hero.png differ diff --git a/examples/agent/a2ui_agent/samples/client/lit/shell/public/sample/city_skyline.jpg b/examples/agent/a2ui_agent/samples/client/lit/shell/public/sample/city_skyline.jpg new file mode 100644 index 0000000000..9e21f47868 Binary files /dev/null and b/examples/agent/a2ui_agent/samples/client/lit/shell/public/sample/city_skyline.jpg differ diff --git a/examples/agent/a2ui_agent/samples/client/lit/shell/public/sample/forest_path.jpg b/examples/agent/a2ui_agent/samples/client/lit/shell/public/sample/forest_path.jpg new file mode 100644 index 0000000000..039862b8d3 Binary files /dev/null and b/examples/agent/a2ui_agent/samples/client/lit/shell/public/sample/forest_path.jpg differ diff --git a/examples/agent/a2ui_agent/samples/client/lit/shell/public/sample/scenic_view.jpg b/examples/agent/a2ui_agent/samples/client/lit/shell/public/sample/scenic_view.jpg new file mode 100644 index 0000000000..4c6c8bd658 Binary files /dev/null and b/examples/agent/a2ui_agent/samples/client/lit/shell/public/sample/scenic_view.jpg differ diff --git a/examples/agent/a2ui_agent/samples/client/lit/shell/theme/clone-default-theme.ts b/examples/agent/a2ui_agent/samples/client/lit/shell/theme/clone-default-theme.ts new file mode 100644 index 0000000000..8b60e7eed4 --- /dev/null +++ b/examples/agent/a2ui_agent/samples/client/lit/shell/theme/clone-default-theme.ts @@ -0,0 +1,22 @@ +/* + Copyright 2025 Google LLC + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import { theme } from "./default-theme.js"; +import { v0_8 } from "@a2ui/lit"; + +export function cloneDefaultTheme(): v0_8.Types.Theme { + return structuredClone(theme); +} diff --git a/examples/agent/a2ui_agent/samples/client/lit/shell/theme/default-theme.ts b/examples/agent/a2ui_agent/samples/client/lit/shell/theme/default-theme.ts new file mode 100644 index 0000000000..34996dffcc --- /dev/null +++ b/examples/agent/a2ui_agent/samples/client/lit/shell/theme/default-theme.ts @@ -0,0 +1,442 @@ +/* + Copyright 2025 Google LLC + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import { v0_8 } from "@a2ui/lit"; + +/** Elements */ + +const a = { + "typography-f-sf": true, + "typography-fs-n": true, + "typography-w-500": true, + "layout-as-n": true, + "layout-dis-iflx": true, + "layout-al-c": true, + "typography-td-none": true, + "color-c-p40": true, +}; + +const audio = { + "layout-w-100": true, +}; + +const body = { + "typography-f-s": true, + "typography-fs-n": true, + "typography-w-400": true, + "layout-mt-0": true, + "layout-mb-2": true, + "typography-sz-bm": true, + "color-c-n10": true, +}; + +const button = { + "typography-f-sf": true, + "typography-fs-n": true, + "typography-w-500": true, + "layout-pt-3": true, + "layout-pb-3": true, + "layout-pl-5": true, + "layout-pr-5": true, + "layout-mb-1": true, + "border-br-16": true, + "border-bw-0": true, + "border-c-n70": true, + "border-bs-s": true, + "color-bgc-s30": true, + "behavior-ho-80": true, +}; + +const heading = { + "typography-f-sf": true, + "typography-fs-n": true, + "typography-w-500": true, + "layout-mt-0": true, + "layout-mb-2": true, +}; + +const iframe = { + "behavior-sw-n": true, +}; + +const input = { + "typography-f-sf": true, + "typography-fs-n": true, + "typography-w-400": true, + "layout-pl-4": true, + "layout-pr-4": true, + "layout-pt-2": true, + "layout-pb-2": true, + "border-br-6": true, + "border-bw-1": true, + "color-bc-s70": true, + "border-bs-s": true, + "layout-as-n": true, + "color-c-n10": true, +}; + +const p = { + "typography-f-s": true, + "typography-fs-n": true, + "typography-w-400": true, + "layout-m-0": true, + "typography-sz-bm": true, + "layout-as-n": true, + "color-c-n10": true, +}; + +const orderedList = { + "typography-f-s": true, + "typography-fs-n": true, + "typography-w-400": true, + "layout-m-0": true, + "typography-sz-bm": true, + "layout-as-n": true, + "color-c-n10": true, +}; + +const unorderedList = { + "typography-f-s": true, + "typography-fs-n": true, + "typography-w-400": true, + "layout-m-0": true, + "typography-sz-bm": true, + "layout-as-n": true, + "color-c-n10": true, +}; + +const listItem = { + "typography-f-s": true, + "typography-fs-n": true, + "typography-w-400": true, + "layout-m-0": true, + "typography-sz-bm": true, + "layout-as-n": true, + "color-c-n10": true, +}; + +const pre = { + "typography-f-c": true, + "typography-fs-n": true, + "typography-w-400": true, + "typography-sz-bm": true, + "typography-ws-p": true, + "layout-as-n": true, +}; + +const textarea = { + ...input, + "layout-r-none": true, + "layout-fs-c": true, +}; + +const video = { + "layout-el-cv": true, +}; + +const aLight = v0_8.Styles.merge(a, {}); +const inputLight = v0_8.Styles.merge(input, {}); +const textareaLight = v0_8.Styles.merge(textarea, {}); +const buttonLight = v0_8.Styles.merge(button, {}); +const bodyLight = v0_8.Styles.merge(body, {}); +const pLight = v0_8.Styles.merge(p, {}); +const preLight = v0_8.Styles.merge(pre, {}); +const orderedListLight = v0_8.Styles.merge(orderedList, {}); +const unorderedListLight = v0_8.Styles.merge(unorderedList, {}); +const listItemLight = v0_8.Styles.merge(listItem, {}); + +export const theme: v0_8.Types.Theme = { + additionalStyles: { + Button: { + "--n-35": "var(--n-100)", + "--n-10": "var(--n-0)", + background: + "linear-gradient(135deg, light-dark(#818cf8, #06b6d4) 0%, light-dark(#a78bfa, #3b82f6) 100%)", + boxShadow: "0 4px 15px rgba(102, 126, 234, 0.4)", + padding: "12px 28px", + textTransform: "uppercase", + }, + Text: { + h1: { + color: "transparent", + background: + "linear-gradient(135deg, light-dark(#818cf8, #06b6d4) 0%, light-dark(#a78bfa, #3b82f6) 100%)", + "-webkit-background-clip": "text", + "background-clip": "text", + "-webkit-text-fill-color": "transparent", + }, + h2: { + color: "transparent", + background: + "linear-gradient(135deg, light-dark(#818cf8, #06b6d4) 0%, light-dark(#a78bfa, #3b82f6) 100%)", + "-webkit-background-clip": "text", + "background-clip": "text", + "-webkit-text-fill-color": "transparent", + }, + h3: { + color: "transparent", + background: + "linear-gradient(135deg, light-dark(#818cf8, #06b6d4) 0%, light-dark(#a78bfa, #3b82f6) 100%)", + "-webkit-background-clip": "text", + "background-clip": "text", + "-webkit-text-fill-color": "transparent", + }, + h4: {}, + h5: {}, + body: {}, + caption: {}, + }, + Card: { + background: + "radial-gradient(circle at top left, light-dark(transparent, rgba(6, 182, 212, 0.15)), transparent 40%), radial-gradient(circle at bottom right, light-dark(transparent, rgba(139, 92, 246, 0.15)), transparent 40%), linear-gradient(135deg, light-dark(rgba(255, 255, 255, 0.7), rgba(30, 41, 59, 0.7)), light-dark(rgba(255, 255, 255, 0.7), rgba(15, 23, 42, 0.8)))", + }, + TextField: { + "--p-0": "light-dark(var(--n-0), #1e293b)", + }, + }, + components: { + AudioPlayer: {}, + Button: { + "layout-pt-2": true, + "layout-pb-2": true, + "layout-pl-3": true, + "layout-pr-3": true, + "border-br-12": true, + "border-bw-0": true, + "border-bs-s": true, + "color-bgc-p30": true, + "behavior-ho-70": true, + "typography-w-400": true, + }, + Card: { "border-br-9": true, "layout-p-4": true, "color-bgc-n100": true }, + CheckBox: { + element: { + "layout-m-0": true, + "layout-mr-2": true, + "layout-p-2": true, + "border-br-12": true, + "border-bw-1": true, + "border-bs-s": true, + "color-bgc-p100": true, + "color-bc-p60": true, + "color-c-n30": true, + "color-c-p30": true, + }, + label: { + "color-c-p30": true, + "typography-f-sf": true, + "typography-v-r": true, + "typography-w-400": true, + "layout-flx-1": true, + "typography-sz-ll": true, + }, + container: { + "layout-dsp-iflex": true, + "layout-al-c": true, + }, + }, + Column: { + "layout-g-2": true, + }, + DateTimeInput: { + container: { + "typography-sz-bm": true, + "layout-w-100": true, + "layout-g-2": true, + "layout-dsp-flexhor": true, + "layout-al-c": true, + "typography-ws-nw": true, + }, + label: { + "color-c-p30": true, + "typography-sz-bm": true, + }, + element: { + "layout-pt-2": true, + "layout-pb-2": true, + "layout-pl-3": true, + "layout-pr-3": true, + "border-br-2": true, + "border-bw-1": true, + "border-bs-s": true, + "color-bgc-p100": true, + "color-bc-p60": true, + "color-c-n30": true, + "color-c-p30": true, + }, + }, + Divider: {}, + Image: { + all: { + "border-br-5": true, + "layout-el-cv": true, + "layout-w-100": true, + "layout-h-100": true, + }, + avatar: { "is-avatar": true }, + header: {}, + icon: {}, + largeFeature: {}, + mediumFeature: {}, + smallFeature: {}, + }, + Icon: {}, + List: { + "layout-g-4": true, + "layout-p-2": true, + }, + Modal: { + backdrop: { "color-bbgc-p60_20": true }, + element: { + "border-br-2": true, + "color-bgc-p100": true, + "layout-p-4": true, + "border-bw-1": true, + "border-bs-s": true, + "color-bc-p80": true, + }, + }, + MultipleChoice: { + container: {}, + label: {}, + element: {}, + }, + Row: { + "layout-g-4": true, + }, + Slider: { + container: {}, + label: {}, + element: {}, + }, + Tabs: { + container: {}, + controls: { all: {}, selected: {} }, + element: {}, + }, + Text: { + all: { + "layout-w-100": true, + "layout-g-2": true, + }, + h1: { + "typography-f-sf": true, + "typography-v-r": true, + "typography-w-400": true, + "layout-m-0": true, + "layout-p-0": true, + "typography-sz-hs": true, + }, + h2: { + "typography-f-sf": true, + "typography-v-r": true, + "typography-w-400": true, + "layout-m-0": true, + "layout-p-0": true, + "typography-sz-tl": true, + }, + h3: { + "typography-f-sf": true, + "typography-v-r": true, + "typography-w-400": true, + "layout-m-0": true, + "layout-p-0": true, + "typography-sz-tl": true, + }, + h4: { + "typography-f-sf": true, + "typography-v-r": true, + "typography-w-400": true, + "layout-m-0": true, + "layout-p-0": true, + "typography-sz-bl": true, + }, + h5: { + "typography-f-sf": true, + "typography-v-r": true, + "typography-w-400": true, + "layout-m-0": true, + "layout-p-0": true, + "typography-sz-bm": true, + }, + body: {}, + caption: {}, + }, + TextField: { + container: { + "typography-sz-bm": true, + "layout-w-100": true, + "layout-g-2": true, + "layout-dsp-flexhor": true, + "layout-al-c": true, + "typography-ws-nw": true, + }, + label: { + "layout-flx-0": true, + "color-c-p30": true, + }, + element: { + "typography-sz-bm": true, + "layout-pt-2": true, + "layout-pb-2": true, + "layout-pl-3": true, + "layout-pr-3": true, + "border-br-2": true, + "border-bw-1": true, + "border-bs-s": true, + "color-bgc-p100": true, + "color-bc-p60": true, + "color-c-n30": true, + "color-c-p30": true, + }, + }, + Video: { + "border-br-5": true, + "layout-el-cv": true, + }, + }, + elements: { + a: aLight, + audio, + body: bodyLight, + button: buttonLight, + h1: heading, + h2: heading, + h3: heading, + h4: heading, + h5: heading, + iframe, + input: inputLight, + p: pLight, + pre: preLight, + textarea: textareaLight, + video, + }, + markdown: { + p: [...Object.keys(pLight)], + h1: [...Object.keys(heading)], + h2: [...Object.keys(heading)], + h3: [...Object.keys(heading)], + h4: [...Object.keys(heading)], + h5: [...Object.keys(heading)], + ul: [...Object.keys(unorderedListLight)], + ol: [...Object.keys(orderedListLight)], + li: [...Object.keys(listItemLight)], + a: [...Object.keys(aLight)], + strong: [], + em: [], + }, +}; diff --git a/examples/agent/a2ui_agent/samples/client/lit/shell/tsconfig.json b/examples/agent/a2ui_agent/samples/client/lit/shell/tsconfig.json new file mode 100644 index 0000000000..dd157690fe --- /dev/null +++ b/examples/agent/a2ui_agent/samples/client/lit/shell/tsconfig.json @@ -0,0 +1,30 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + + "compilerOptions": { + "composite": false, + "declaration": true, + "declarationMap": true, + "incremental": true, + "forceConsistentCasingInFileNames": true, + "inlineSources": false, + "preserveWatchOutput": true, + "sourceMap": true, + "target": "es2022", + "module": "es2022", + "lib": ["ESNext", "DOM", "DOM.Iterable"], + "skipLibCheck": true, + "useDefineForClassFields": false, + "rootDir": ".", + "outDir": "dist", + "tsBuildInfoFile": "dist/.tsbuildinfo", + "moduleResolution": "bundler", + "resolveJsonModule": true, + "isolatedModules": true, + "strict": false, + "noUnusedLocals": false, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true + }, + "references": [{ "path": "../../../../renderers/lit" }] +} diff --git a/examples/agent/a2ui_agent/samples/client/lit/shell/types/types.ts b/examples/agent/a2ui_agent/samples/client/lit/shell/types/types.ts new file mode 100644 index 0000000000..90644ce08d --- /dev/null +++ b/examples/agent/a2ui_agent/samples/client/lit/shell/types/types.ts @@ -0,0 +1,42 @@ +/* + Copyright 2025 Google LLC + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import { HTMLTemplateResult } from "lit"; + +export enum SnackType { + NONE = "none", + INFORMATION = "information", + WARNING = "warning", + ERROR = "error", + PENDING = "pending", +} + +export type SnackbarUUID = ReturnType; + +export type SnackbarAction = { + title: string; + action: string; + value?: HTMLTemplateResult | string; + callback?: () => void; +}; + +export type SnackbarMessage = { + id: SnackbarUUID; + type: SnackType; + persistent: boolean; + message: string | HTMLTemplateResult; + actions?: SnackbarAction[]; +}; diff --git a/examples/agent/a2ui_agent/samples/client/lit/shell/ui/snackbar.ts b/examples/agent/a2ui_agent/samples/client/lit/shell/ui/snackbar.ts new file mode 100644 index 0000000000..91f2f036dc --- /dev/null +++ b/examples/agent/a2ui_agent/samples/client/lit/shell/ui/snackbar.ts @@ -0,0 +1,299 @@ +/* + Copyright 2025 Google LLC + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ +import { LitElement, html, css, nothing, unsafeCSS } from "lit"; +import { customElement, property } from "lit/decorators.js"; +import { SnackbarMessage, SnackbarUUID, SnackType } from "../types/types"; +import { repeat } from "lit/directives/repeat.js"; +import { SnackbarActionEvent } from "../events/events"; +import { classMap } from "lit/directives/class-map.js"; +import { v0_8 } from "@a2ui/lit"; + +const DEFAULT_TIMEOUT = 8000; + +@customElement("ui-snackbar") +export class Snackbar extends LitElement { + @property({ reflect: true, type: Boolean }) + accessor active = false; + + @property({ reflect: true, type: Boolean }) + accessor error = false; + + @property() + accessor timeout = DEFAULT_TIMEOUT; + + #messages: SnackbarMessage[] = []; + #timeout = 0; + + static styles = [ + unsafeCSS(v0_8.Styles.structuralStyles), + css` + :host { + --text-color: var(--n-0); + --bb-body-medium: 16px; + --bb-body-line-height-medium: 24px; + + display: flex; + align-items: center; + position: fixed; + bottom: var(--bb-grid-size-7); + left: 50%; + translate: -50% 0; + opacity: 0; + pointer-events: none; + border-radius: var(--bb-grid-size-2); + background: var(--n-90); + padding: var(--bb-grid-size-3) var(--bb-grid-size-6); + width: 60svw; + max-width: 720px; + z-index: 1800; + scrollbar-width: none; + overflow-x: scroll; + font: 400 var(--bb-body-medium) / var(--bb-body-line-height-medium) + var(--bb-font-family); + } + + :host([active]) { + transition: opacity 0.3s cubic-bezier(0, 0, 0.3, 1) 0.2s; + opacity: 1; + pointer-events: auto; + } + + :host([error]) { + background: var(--e-90); + --text-color: var(--e-40); + } + + .g-icon { + flex: 0 0 auto; + color: var(--text-color); + margin-right: var(--bb-grid-size-4); + + &.rotate { + animation: 1s linear 0s infinite normal forwards running rotate; + } + } + + #messages { + color: var(--text-color); + flex: 1 1 auto; + margin-right: var(--bb-grid-size-11); + + a, + a:visited { + color: var(--bb-ui-600); + text-decoration: none; + + &:hover { + color: var(--bb-ui-500); + text-decoration: underline; + } + } + } + + #actions { + flex: 0 1 auto; + width: fit-content; + margin-right: var(--bb-grid-size-3); + + & button { + font: 500 var(--bb-body-medium) / var(--bb-body-line-height-medium) + var(--bb-font-family); + padding: 0; + background: transparent; + border: none; + margin: 0 var(--bb-grid-size-4); + color: var(--text-color); + opacity: 0.7; + transition: opacity 0.2s cubic-bezier(0, 0, 0.3, 1); + + &:not([disabled]) { + cursor: pointer; + + &:hover, + &:focus { + opacity: 1; + } + } + } + } + + #close { + display: flex; + align-items: center; + padding: 0; + color: var(--text-color); + background: transparent; + border: none; + margin: 0 0 0 var(--bb-grid-size-2); + opacity: 0.7; + transition: opacity 0.2s cubic-bezier(0, 0, 0.3, 1); + + .g-icon { + margin-right: 0; + } + + &:not([disabled]) { + cursor: pointer; + + &:hover, + &:focus { + opacity: 1; + } + } + } + + @keyframes rotate { + from { + rotate: 0deg; + } + + to { + rotate: 360deg; + } + } + `, + ]; + + show(message: SnackbarMessage, replaceAll = false) { + const existingMessage = this.#messages.findIndex( + (msg) => msg.id === message.id + ); + if (existingMessage === -1) { + if (replaceAll) { + this.#messages.length = 0; + } + + this.#messages.push(message); + } else { + this.#messages[existingMessage] = message; + } + + window.clearTimeout(this.#timeout); + if (!this.#messages.every((msg) => msg.persistent)) { + this.#timeout = window.setTimeout(() => { + this.hide(); + }, this.timeout); + } + + this.error = this.#messages.some((msg) => msg.type === SnackType.ERROR); + this.active = true; + this.requestUpdate(); + + return message.id; + } + + hide(id?: SnackbarUUID) { + if (id) { + const idx = this.#messages.findIndex((msg) => msg.id === id); + if (idx !== -1) { + this.#messages.splice(idx, 1); + } + } else { + this.#messages.length = 0; + } + + this.active = this.#messages.length !== 0; + this.updateComplete.then((avoidedUpdate) => { + if (!avoidedUpdate) { + return; + } + + this.requestUpdate(); + }); + } + + render() { + let rotate = false; + let icon = ""; + for (let i = this.#messages.length - 1; i >= 0; i--) { + if ( + !this.#messages[i].type || + this.#messages[i].type === SnackType.NONE + ) { + continue; + } + + icon = this.#messages[i].type; + if (this.#messages[i].type === SnackType.PENDING) { + icon = "progress_activity"; + rotate = true; + } + break; + } + + return html` ${icon + ? html`${icon}` + : nothing} +
+ ${repeat( + this.#messages, + (message) => message.id, + (message) => { + return html`
${message.message}
`; + } + )} +
+
+ ${repeat( + this.#messages, + (message) => message.id, + (message) => { + if (!message.actions) { + return nothing; + } + + return html`${repeat( + message.actions, + (action) => action.value, + (action) => { + return html``; + } + )}`; + } + )} +
+ `; + } +} diff --git a/examples/agent/a2ui_agent/samples/client/lit/shell/ui/ui.ts b/examples/agent/a2ui_agent/samples/client/lit/shell/ui/ui.ts new file mode 100644 index 0000000000..7726f29d73 --- /dev/null +++ b/examples/agent/a2ui_agent/samples/client/lit/shell/ui/ui.ts @@ -0,0 +1,17 @@ +/* + Copyright 2025 Google LLC + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +export { Snackbar } from "./snackbar"; diff --git a/examples/agent/a2ui_agent/samples/client/lit/shell/vite.config.ts b/examples/agent/a2ui_agent/samples/client/lit/shell/vite.config.ts new file mode 100644 index 0000000000..4f0892ec2f --- /dev/null +++ b/examples/agent/a2ui_agent/samples/client/lit/shell/vite.config.ts @@ -0,0 +1,45 @@ +/* + Copyright 2025 Google LLC + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import { config } from "dotenv"; +import { UserConfig } from "vite"; +import * as Middleware from "./middleware"; +import { dirname, resolve } from "node:path"; +import { fileURLToPath } from "node:url"; + +const __dirname = dirname(fileURLToPath(import.meta.url)); + +export default async () => { + config(); + + const entry: Record = { + shell: resolve(__dirname, "index.html"), + }; + + return { + plugins: [Middleware.A2AMiddleware.plugin()], + build: { + rollupOptions: { + input: entry, + }, + target: "esnext", + }, + define: {}, + resolve: { + dedupe: ["lit"], + }, + } satisfies UserConfig; +};