diff --git a/logistics-manager-app/.gemini/settings.json b/logistics-manager-app/.gemini/settings.json new file mode 100644 index 0000000..148e8cf --- /dev/null +++ b/logistics-manager-app/.gemini/settings.json @@ -0,0 +1,22 @@ +{ + "mcpServers": { + "angular-cli": { + "command": "npx", + "args": [ + "-y", + "@angular/cli", + "mcp" + ], + "disabledTools": [ + "ai_tutor" + ] + }, + "chrome-devtools": { + "command": "npx", + "args": [ + "--yes", + "chrome-devtools-mcp@1.10.1" + ] + } + } +} diff --git a/logistics-manager-app/.gitignore b/logistics-manager-app/.gitignore new file mode 100644 index 0000000..a9c469c --- /dev/null +++ b/logistics-manager-app/.gitignore @@ -0,0 +1,47 @@ +# See https://docs.github.com/get-started/getting-started-with-git/ignoring-files for more about ignoring files. + +# Compiled output +/dist +/tmp +/out-tsc +/bazel-out + +# Node +/node_modules +npm-debug.log +yarn-error.log + +# IDEs and editors +.idea/ +.project +.classpath +.c9/ +*.launch +.settings/ +*.sublime-workspace +.agents +.vscode + +# Visual Studio Code +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +!.vscode/mcp.json +.history/* + +# Miscellaneous +/.angular/cache +.sass-cache/ +/connect.lock +/coverage +/libpeerconnection.log +testem.log +/typings +__screenshots__/ +.env + +# System files +.DS_Store +Thumbs.db diff --git a/logistics-manager-app/.postcssrc.json b/logistics-manager-app/.postcssrc.json new file mode 100644 index 0000000..72f908d --- /dev/null +++ b/logistics-manager-app/.postcssrc.json @@ -0,0 +1,5 @@ +{ + "plugins": { + "@tailwindcss/postcss": {} + } +} \ No newline at end of file diff --git a/logistics-manager-app/AGENTS.md b/logistics-manager-app/AGENTS.md new file mode 100644 index 0000000..b11ef7a --- /dev/null +++ b/logistics-manager-app/AGENTS.md @@ -0,0 +1,27 @@ +# Project Context & Agent Configuration + +**System Directive:** For all tasks within this project workspace, you must prioritize the environment-configured Agent Skills, Antigravity Plugins, and MCP Servers listed below. Do not rely on generalized knowledge if a specialized skill or server can execute the task. + +Verify your work using the proper tools available to you. + +## 1. Agent Skills & Plugins +Apply the following specialized profiles based on the task domain: + +* **`angular-developer`**: Engage this skill for all frontend architecture, component creation, best practices and modern Angular. +* **`developing-genkit-js`**: Engage this skill when writing, configuring, or troubleshooting Genkit AI integrations, agents (`ai.defineAgent`), flows (`ai.defineFlow`), and tools (`ai.defineTool`). + * **Express v5 Compatibility Note**: Do **NOT** install or import `@genkit-ai/express` (such as `expressHandler`), because Angular 21 SSR uses Express v5 (`^5.1.0`) while `@genkit-ai/express` requires Express v4 (`^4.21.1`). Invoke Genkit agents/flows directly inside standard Express route handlers in `src/server.ts`. +* **`chrome-devtools` (`chrome-devtools-plugin`)**: Installed via `/plugin install chrome-devtools-plugin` in Antigravity CLI. Engage the `chrome-devtools` skill to automate browser interactions, inspect DOM states, capture console logs, evaluate network payloads, and verify UI changes (`navigate_page`, `wait_for`, `take_snapshot`, `click`, `fill`). + +## 2. MCP Servers +Route operations through these Model Context Protocol servers to interact with the local development environment: + +* **angular-cli MCP Server**: Invoke and use tools like the following to control the development server. + * **`devserver.start`** - starts the Angular dev server + * **`devserver.stop`** - stops the Angular dev server + * **`devserver.wait_for_build`** - Returns the output logs of the most recent build in a running development server started by `devserver.start` +* **Chrome DevTools Plugin (`chrome-devtools-plugin`)**: Installed via `/plugin install chrome-devtools-plugin`. Provides the `chrome-devtools` MCP tools and skills for browser verification and debugging. + +## 3. Execution Boundaries +* **Sequential Execution**: Follow multi-step prompts in exact sequence. Review and implement the code in `src/server.ts` first before running verification. +* **Environment & Credentials**: `.env` is valid and is already loaded at the top of `src/server.ts` (overriding any stale shell variables). Do **NOT** run standalone pre-flight `node -e` scripts to test API keys, and do **NOT** invoke external cloud/auth CLI tools (`gcloud`, `aws`, etc.). Test against the running application server (use `curl` only when asked to test the backend API directly; use `chrome-devtools` browser tools—not `curl`—when verifying UI and end-to-end flows). +* **MCP Tool Usage**: Do **NOT** invoke the interactive `ai_tutor` tool from the `angular-cli` MCP server. diff --git a/logistics-manager-app/GEMINI.md b/logistics-manager-app/GEMINI.md deleted file mode 100644 index 6ee069f..0000000 --- a/logistics-manager-app/GEMINI.md +++ /dev/null @@ -1,21 +0,0 @@ -# Project Context & Agent Configuration - -**System Directive:** For all tasks within this project workspace, you must prioritize the environment-configured Agent Skills and MCP Servers listed below. Do not rely on generalized knowledge if a specialized skill or server can execute the task. - -Verify your work using the proper tools available to you. - -## 1. Agent Skills -Apply the following specialized profiles based on the task domain: - -* **`angular-developer`**: Engage this skill for all frontend architecture, component creation, best practices and modern Angular. -* **`gemini-api-dev`**: Engage this skill when writing, configuring, or troubleshooting standard Gemini API integrations. -* **`gemini-interactions-api`**: Engage this skill specifically for building multi-turn conversations, tool calling, and complex interactive flows. - -## 2. MCP Servers -Route operations through these Model Context Protocol servers to interact with the local development environment: - -* **angular-cli MCP Server**: Invoke and use tools like the following to control the development server. - * **`devserver.start`** - starts the Angular dev server - * **`devserver.stop`** - stops the Angular dev server - * **`devserver.wait_for_build`** - Returns the output logs of the most recent build in a running development server started by `devserver.start` -* **chrome-devtools MCP Server**: Invoke to capture console logs, inspect DOM states, evaluate network payloads, and debug browser-side runtime errors and more. \ No newline at end of file diff --git a/logistics-manager-app/README.md b/logistics-manager-app/README.md index 29e2d1b..7b51670 100644 --- a/logistics-manager-app/README.md +++ b/logistics-manager-app/README.md @@ -1,59 +1,78 @@ -# LogisticsManagerApp +# Obsidian Logistics Manager — Angular + Genkit Agentic Demo -This project was generated using [Angular CLI](https://github.com/angular/angular-cli) version 21.1.3. +An interactive fleet and service-queue management web application built with **Angular 21** (Signals, `resource`, `linkedSignal`, Angular Aria, SSR with Express) and **Genkit** (`genkit` + `@genkit-ai/google-genai` powered by `gemini-3-flash-preview`). -## Development server +This repository accompanies the live stage demo showing how to pair-program with an AI coding agent using **Agent Skills**, **MCP Servers**, and **Antigravity Plugins** (`chrome-devtools-plugin`). -To start a local development server, run: +--- -```bash -ng serve -``` +## Branches & Demo Steps -Once the server is running, open your browser and navigate to `http://localhost:4200/`. The application will automatically reload whenever you modify any of the source files. +You can either start on **`main`** and run each prompt in [`content/prompts/`](./content/prompts/) with your coding agent, or check out any of the completed checkpoint branches directly: -## Code scaffolding +| Branch | Prompt | Description | +| :--- | :--- | :--- | +| **`main`** | — | Clean starter baseline with fleet map, inventory list, service queue, and static chat shell. | +| **`step-1`** | [`content/prompts/prompt-1.md`](./content/prompts/prompt-1.md) | Initializes Genkit with `googleAI()` (`gemini-3-flash-preview`) and adds the `POST /api/chat` endpoint in `src/server.ts`. | +| **`step-2`** | [`content/prompts/prompt-2.md`](./content/prompts/prompt-2.md) | Connects the client chat UI (`src/app/components/chat/chat.ts`) to `/api/chat` using the Angular `resource` API and `afterRenderEffect` auto-scrolling. | +| **`step-3`** | [`content/prompts/prompt-3.md`](./content/prompts/prompt-3.md) | Defines 7 Genkit fleet & service-queue tools (`ai.defineTool`) in `src/server.ts` and synchronizes UI state using Angular `resource`, `linkedSignal`, and `.reload()`. | -Angular CLI includes powerful code scaffolding tools. To generate a new component, run: +To jump straight to the completed application with full Genkit tool calling: ```bash -ng generate component component-name +git checkout step-3 ``` -For a complete list of available schematics (such as `components`, `directives`, or `pipes`), run: +--- + +## Getting Started + +### 1. Install Dependencies ```bash -ng generate --help +npm install ``` -## Building +### 2. Configure Environment Variables -To build the project run: +Create a `.env` file in the project root: -```bash -ng build +```env +GEMINI_API_KEY="your_gemini_api_key_here" ``` -This will compile your project and store the build artifacts in the `dist/` directory. By default, the production build optimizes your application for performance and speed. +### 3. Install Agent Skills & Plugins (For Running the Prompts) -## Running unit tests +If you want to run the prompts in [`content/prompts/`](./content/prompts/) with an AI coding agent from `main`: -To execute unit tests with the [Vitest](https://vitest.dev/) test runner, use the following command: +1. **Install the Angular & Genkit Agent Skills**: + ```bash + npx skills add https://github.com/angular/skills --skill angular-developer + npx skills add firebase/genkit --skill developing-genkit-js + ``` -```bash -ng test -``` +2. **Install the Chrome DevTools Plugin** (in Antigravity CLI): + ```text + /plugin install chrome-devtools-plugin + ``` + +3. **MCP Configuration**: + - Project MCP settings are included in [`.gemini/settings.json`](./.gemini/settings.json), and agent workspace guidelines are defined in [`AGENTS.md`](./AGENTS.md). + +--- -## Running end-to-end tests +## Development Server & Building -For end-to-end (e2e) testing, run: +To start the local development server: ```bash -ng e2e +npm start ``` -Angular CLI does not come with an end-to-end testing framework by default. You can choose one that suits your needs. +Open `http://localhost:4200/` in your browser. -## Additional Resources +To build the project for production: -For more information on using the Angular CLI, including detailed command references, visit the [Angular CLI Overview and Command Reference](https://angular.dev/tools/cli) page. +```bash +npm run build +``` diff --git a/logistics-manager-app/codelab.md b/logistics-manager-app/codelab.md index d2803fe..4f39303 100644 --- a/logistics-manager-app/codelab.md +++ b/logistics-manager-app/codelab.md @@ -24,32 +24,36 @@ cd logistics-manager-app npm install ``` -### 2. Install Gemini CLI Skills -Skills provide the agent with specialized knowledge. You'll add the `angular-developer`, `gemini-sdk`, and `gemini-interactions-api` skills to help with code generation and API integration. +### 2. Install Agent Skills +Skills provide the agent with specialized knowledge. You'll add the `angular-developer` and `developing-genkit-js` skills to help with Angular code generation and Genkit AI integration. ```bash # Add Angular expertise -npx skills install https://github.com/angular/skills/ --skill angular-developer +npx skills add https://github.com/angular/skills/ --skill angular-developer -# Add Gemini SDK expertise -npx skills add https://github.com/google-gemini/gemini-skills --skill gemini-api-dev -npx skills add https://github.com/google-gemini/gemini-skills --skill gemini-interactions-api +# Add Genkit JS expertise +npx skills add firebase/genkit --skill developing-genkit-js ``` -Confirm that the agent skills are installed by using the `/skills` commands in Gemini CLI. This may require restarting Gemini CLI. +Confirm that the agent skills are installed by using the `/skills` commands in your CLI. This may require restarting the CLI. -_Note: You do not have to use the `skills` npm package, you are welcome to use whichever agent skills manager you prefer. +_Note: You do not have to use the `skills` npm package, you are welcome to use whichever agent skills manager you prefer._ -### 3. Configure MCP Servers -You can use Model Context Protocol (MCP) to enable your AI tools to interact with your running application and the browser. +### 3. Configure Plugins & MCP Servers +You can use Model Context Protocol (MCP) and plugins to enable your AI tools to interact with your running application and the browser. #### Angular MCP Server -This server allows the AI tooling to understand your Angular project structure, components,services and more. Use the Angular CLI to get the configuration for the MCP server. +This server allows the AI tooling to understand your Angular project structure, components, services, and control the development server (`devserver.start`, `devserver.wait_for_build`, `devserver.stop`). -#### Chrome DevTools MCP Server +#### Chrome DevTools Plugin (`chrome-devtools-plugin`) This allows the AI to inspect the running application in your browser for debugging and UI analysis. -Add the following configuration to your environment, for example, `.gemini/setting.json`: +In Antigravity CLI, install the plugin: +```text +/plugin install chrome-devtools-plugin +``` + +Add the following configuration to `.gemini/settings.json`: ```json { @@ -59,15 +63,17 @@ Add the following configuration to your environment, for example, `.gemini/setti "args": [ "-y", "@angular/cli", - "mcp", - "-E", - "devserver" + "mcp" + ], + "disabledTools": [ + "ai_tutor" ] }, "chrome-devtools": { "command": "npx", "args": [ - "chrome-devtools-mcp@latest" + "--yes", + "chrome-devtools-mcp@1.10.1" ] } } diff --git a/logistics-manager-app/content/prompts/prompt-1.md b/logistics-manager-app/content/prompts/prompt-1.md index 6e0909f..5607d43 100644 --- a/logistics-manager-app/content/prompts/prompt-1.md +++ b/logistics-manager-app/content/prompts/prompt-1.md @@ -1,13 +1,13 @@ -# Add Gemini support to my Angular project's backend (Node.js). +# Add Genkit support to my Angular project's backend (Node.js). Technical Stack: -* Package: @google/genai (the new unified SDK) -* Model: gemini-3-flash-preview +* Packages: `genkit` and `@genkit-ai/google-genai` +* Model: `gemini-3-flash-preview` ## Task: -1. Review my current server entry point and suggest how to initialize the GoogleGenAI client. +1. Review my current server entry point (`src/server.ts`) and suggest how to initialize Genkit. -2. Outline a plan for a new POST endpoint that accepts a prompt and returns a generated response using the interactions API method. Ensure the plan explains how the interaction_id will be stored or passed back to the Angular client to maintain session state. +2. Implement a new POST endpoint (`/api/chat`) that accepts a `prompt` and returns a generated response (`{ text }`) using Genkit. -3. Verify that endpoint is working by building the app, and use `curl` to ensure that the endpoint is working. \ No newline at end of file +3. Verify that the endpoint is working by building the app, running the server, and using `curl` to test `/api/chat`. diff --git a/logistics-manager-app/content/prompts/prompt-2.md b/logistics-manager-app/content/prompts/prompt-2.md index 9748c02..16b3b1a 100644 --- a/logistics-manager-app/content/prompts/prompt-2.md +++ b/logistics-manager-app/content/prompts/prompt-2.md @@ -1,11 +1,12 @@ # Add communication from the client side app to the AI chat endpoint via the chat window in the web app. Task: -1. Review the endpoint and chat window code to understand where to integrate. +1. Review the `/api/chat` endpoint and chat window code (`src/app/components/chat/chat.ts`) to understand where to integrate. -2. Outline a plan to implement the communication between the client and the server. - - Use the **`@google/genai` interactions API**. Extract the generated message from the first text object in `interaction.outputs`. Refer to the documention if you get stuck: https://ai.google.dev/gemini-api/docs/interactions.md.txt +2. Outline a plan to implement the communication between the client and the server: + - Use the Angular **`resource`** API for the client requests to `/api/chat`. + - Send the user's `prompt` and display the user and AI messages in the chat window, automatically scrolling the messages container to the bottom when new messages arrive. 3. For verification: - - build the app and confirm that the build is error free. Use the Angular MCP server tools. - - you may also use the ChromeDevTools MCP server to preview the build in a browser + - Build the app and confirm that the build is error free. Use the Angular MCP server tools. + - Use the `chrome-devtools` skill (`chrome-devtools-plugin`) in the browser (do not use `curl`) to preview and verify the chat UI end-to-end. diff --git a/logistics-manager-app/content/prompts/prompt-3.md b/logistics-manager-app/content/prompts/prompt-3.md index 80f179e..e77758c 100644 --- a/logistics-manager-app/content/prompts/prompt-3.md +++ b/logistics-manager-app/content/prompts/prompt-3.md @@ -1,14 +1,12 @@ -# Add function/tool calling support for our fleet by the interactions SDK so that natural language chats with the model can become actions. +# Add function/tool calling support for our fleet using Genkit so that natural language chats with the model can become actions. ## Task 1. **Review** the fleet-related functions in `src/server.ts` to identify candidates for tool calling. -2. **Implement Multi-Turn Tool Support**: - - Use the **Interactions API** stateful mode with `previous_interaction_id`. - - **Flat Tool Definition**: Ensure the `tools` array contains flat objects (sibling `type`, `name`, `description`, and `parameters`). - - **Mandatory Schema**: Every tool MUST have a `parameters` schema, even if it is an empty object: `parameters: { type: 'object', properties: {} }`. - - **Function Results**: For subsequent turns, provide `input` as an array of objects where `type: 'function_result'`, using `call_id` (matching the model's output `id`), and a JSON-serializable `result`. +2. **Implement Genkit Tool Calling**: + - Define tools using `ai.defineTool()` with `z` schemas for `name`, `description`, `inputSchema`, and `outputSchema`. + - Pass the defined `tools` to `ai.generate()` in `/api/chat` so Genkit automatically handles multi-turn tool execution. 3. **UI State Synchronization**: - Design a pattern using Angular **`resource`** to fetch data. @@ -17,44 +15,4 @@ 4. **Verification**: - Build the app and confirm it is error-free using Angular MCP tools. - - Use ChromeDevTools MCP to verify end-to-end functionality (e.g., adding a ticket via chat and seeing it appear in the UI). - -## Implementation Reference (Ground Truth) - -```typescript -import { GoogleGenAI } from '@google/genai'; -const client = new GoogleGenAI({}); - -// 1. Tool Definition (Flat structure + Mandatory parameters) -const tool = { - type: 'function', - name: 'get_status', - description: 'Get current system status.', - parameters: { type: 'object', properties: {} } -}; - -// 2. Initial Request -let interaction = await client.interactions.create({ - model: 'gemini-3-flash-preview', - input: 'Status check.', - tools: [tool] -}); - -// 3. Multi-turn Orchestration Loop -while (interaction.status === 'requires_action') { - const outputs = interaction.outputs.filter(o => o.type === 'function_call'); - const results = outputs.map(call => ({ - type: 'function_result', - name: call.name, - call_id: call.id, // Critical mapping - result: { data: 'All systems green' } // Must be JSON serializable - })); - - interaction = await client.interactions.create({ - model: 'gemini-3-flash-preview', - previous_interaction_id: interaction.id, - input: results, - tools: [tool] // Re-specify tools on every turn - }); -} -``` + - Use the `chrome-devtools` skill (`chrome-devtools-plugin`) in the browser (do not use `curl`) to verify end-to-end functionality directly in the UI (e.g., adding a ticket via the chat window and seeing it appear in the UI). diff --git a/logistics-manager-app/content/prompts/versions/prompt-1a.md b/logistics-manager-app/content/prompts/versions/prompt-1a.md index cc2c71a..1a34109 100644 --- a/logistics-manager-app/content/prompts/versions/prompt-1a.md +++ b/logistics-manager-app/content/prompts/versions/prompt-1a.md @@ -1,3 +1,3 @@ -# I want to add support for gemini sdk to this angular app. Please update the server code to include the necessary changes. Once completed, pause for further instructions. +# I want to add support for Genkit to this Angular app. Please update the server code to include the necessary changes. Once completed, pause for further instructions. -## Show me your plan before you make any changes so we can be on the same page \ No newline at end of file +## Show me your plan before you make any changes so we can be on the same page diff --git a/logistics-manager-app/content/prompts/versions/prompt-1b.md b/logistics-manager-app/content/prompts/versions/prompt-1b.md index 6e0909f..5607d43 100644 --- a/logistics-manager-app/content/prompts/versions/prompt-1b.md +++ b/logistics-manager-app/content/prompts/versions/prompt-1b.md @@ -1,13 +1,13 @@ -# Add Gemini support to my Angular project's backend (Node.js). +# Add Genkit support to my Angular project's backend (Node.js). Technical Stack: -* Package: @google/genai (the new unified SDK) -* Model: gemini-3-flash-preview +* Packages: `genkit` and `@genkit-ai/google-genai` +* Model: `gemini-3-flash-preview` ## Task: -1. Review my current server entry point and suggest how to initialize the GoogleGenAI client. +1. Review my current server entry point (`src/server.ts`) and suggest how to initialize Genkit. -2. Outline a plan for a new POST endpoint that accepts a prompt and returns a generated response using the interactions API method. Ensure the plan explains how the interaction_id will be stored or passed back to the Angular client to maintain session state. +2. Implement a new POST endpoint (`/api/chat`) that accepts a `prompt` and returns a generated response (`{ text }`) using Genkit. -3. Verify that endpoint is working by building the app, and use `curl` to ensure that the endpoint is working. \ No newline at end of file +3. Verify that the endpoint is working by building the app, running the server, and using `curl` to test `/api/chat`. diff --git a/logistics-manager-app/content/prompts/versions/prompt-2b.md b/logistics-manager-app/content/prompts/versions/prompt-2b.md index b84fa7b..870302a 100644 --- a/logistics-manager-app/content/prompts/versions/prompt-2b.md +++ b/logistics-manager-app/content/prompts/versions/prompt-2b.md @@ -1,14 +1,14 @@ # I want to communicate from the client to the AI endpoint via the chat window in the web app. -* Tools: Angular SKills, Angular MCP, Google GenAI Skills +* Tools: Angular Skills, Angular MCP, `chrome-devtools` plugin (`chrome-devtools-plugin`) Task: 1. Review the endpoint and chat window code to understand where to integrate. 2. Outline a plan to implement the communication between the client and the server. - - Prefer the resource API for the client requests to the server. - - Prefer the @google/genai interactions API on the server. Refer to the documention if you get stuck: https://ai.google.dev/gemini-api/docs/interactions.md.txt + - Prefer the Angular `resource` API for the client requests to the server. + - Send the `prompt` to the Genkit `/api/chat` endpoint on the server and display the response in the chat window, automatically scrolling to the bottom when new messages arrive. 3. For verification: - build the app and confirm that the build is error free. Use the Angular MCP server tools. - - you may also use the ChromeDevTools MCP server to preview the build in a browser + - use the `chrome-devtools` skill (`chrome-devtools-plugin`) in the browser (do not use `curl`) to preview and verify the chat UI end-to-end diff --git a/logistics-manager-app/content/prompts/versions/prompt-2c.md b/logistics-manager-app/content/prompts/versions/prompt-2c.md index 9748c02..16b3b1a 100644 --- a/logistics-manager-app/content/prompts/versions/prompt-2c.md +++ b/logistics-manager-app/content/prompts/versions/prompt-2c.md @@ -1,11 +1,12 @@ # Add communication from the client side app to the AI chat endpoint via the chat window in the web app. Task: -1. Review the endpoint and chat window code to understand where to integrate. +1. Review the `/api/chat` endpoint and chat window code (`src/app/components/chat/chat.ts`) to understand where to integrate. -2. Outline a plan to implement the communication between the client and the server. - - Use the **`@google/genai` interactions API**. Extract the generated message from the first text object in `interaction.outputs`. Refer to the documention if you get stuck: https://ai.google.dev/gemini-api/docs/interactions.md.txt +2. Outline a plan to implement the communication between the client and the server: + - Use the Angular **`resource`** API for the client requests to `/api/chat`. + - Send the user's `prompt` and display the user and AI messages in the chat window, automatically scrolling the messages container to the bottom when new messages arrive. 3. For verification: - - build the app and confirm that the build is error free. Use the Angular MCP server tools. - - you may also use the ChromeDevTools MCP server to preview the build in a browser + - Build the app and confirm that the build is error free. Use the Angular MCP server tools. + - Use the `chrome-devtools` skill (`chrome-devtools-plugin`) in the browser (do not use `curl`) to preview and verify the chat UI end-to-end. diff --git a/logistics-manager-app/content/prompts/versions/prompt-3a.md b/logistics-manager-app/content/prompts/versions/prompt-3a.md index e8064bd..3b8d557 100644 --- a/logistics-manager-app/content/prompts/versions/prompt-3a.md +++ b/logistics-manager-app/content/prompts/versions/prompt-3a.md @@ -1,3 +1,3 @@ -# Let's add tool calling support for our fleet. Have a look at the functions in the Server file to know what can be called and how. Be sure to handle recursive tool calls and responses from Gemini. Use the Gemini SDK skills for help on syntax and best practices where needed. +# Let's add tool calling support for our fleet using Genkit. Have a look at the functions in the Server file to know what can be called and how. Use `ai.defineTool` with Zod schemas and pass the tools to `ai.generate`. -## Show me your plan before you make any changes so we can be on the same page \ No newline at end of file +## Show me your plan before you make any changes so we can be on the same page diff --git a/logistics-manager-app/content/prompts/versions/prompt-3b.md b/logistics-manager-app/content/prompts/versions/prompt-3b.md index cfc909f..3c30ab4 100644 --- a/logistics-manager-app/content/prompts/versions/prompt-3b.md +++ b/logistics-manager-app/content/prompts/versions/prompt-3b.md @@ -1,27 +1,21 @@ -# I want to add function/tool calling support for our fleet by the interactions SDK so that natural language chats with the model can become actions. +# I want to add function/tool calling support for our fleet using Genkit so that natural language chats with the model can become actions. * Tools: - - Agent Skills: + - Agent Skills & Plugins: - `angular-developer` - - `gemini-api-dev` - - `gemini-interactions-api` + - `developing-genkit-js` + - `chrome-devtools` (`chrome-devtools-plugin`) - MCP Servers: - Angular MCP - - ChromeDevTools MCP ## Task 1. Review the Fleet related functions in the `server.ts` file to know what can be called and how to call them. 2. Outline a plan to implement this feature. - - Be sure to handle multi-turn tool calls and responses from the interactions API. Use the Gemini SDK skills for help on syntax and best practices where needed. Refer to the documention if you get stuck: https://ai.google.dev/gemini-api/docs/interactions.md.txt - - - Make a plan for a communication pattern with the UI so that the response from a tool call can create a "state" update on the UI. For example, if the user requests to add vehicles to the repair queue, the server will have an updated state, but the UI will be out of sync. Design a pattern using Angular resource to fetch data and a linkedSignal to manage the local UI state. When a tool call (e.g., add_to_repair_queue) succeeds, the linkedSignal must automatically reflect the updated fleet status from the server response. - - - Ensure the **`tools` array** uses the new format (an array of objects with `type: 'function'`). - - - **CRITICAL FIX**: For the **`input` on subsequent turns**, provide an array of objects where `type: 'function_result'` and the `result` field is always a **JSON-serializable object** (wrap arrays in `{ data: [...] }`). Do NOT wrap the array in a `role`/`parts` object. + - Define tools using `ai.defineTool()` with `z` schemas and pass them to `ai.generate()` in `/api/chat` so Genkit automatically handles multi-turn tool calls. + - Make a plan for a communication pattern with the UI so that the response from a tool call can create a "state" update on the UI. For example, if the user requests to add vehicles to the repair queue, the server will have an updated state, but the UI will be out of sync. Design a pattern using Angular `resource` to fetch data and a `linkedSignal` to manage the local UI state. When a chat turn completes, trigger resource reloads so the UI reflects the updated fleet and service queue status. 3. For verification: - build the app and confirm that the build is error free. Use the Angular MCP server tools. - - you may also use the ChromeDevTools MCP server to preview the build in a browser \ No newline at end of file + - use the `chrome-devtools` skill (`chrome-devtools-plugin`) in the browser (do not use `curl`) to verify end-to-end functionality directly in the UI diff --git a/logistics-manager-app/content/prompts/versions/prompt-3c.md b/logistics-manager-app/content/prompts/versions/prompt-3c.md index 80f179e..e77758c 100644 --- a/logistics-manager-app/content/prompts/versions/prompt-3c.md +++ b/logistics-manager-app/content/prompts/versions/prompt-3c.md @@ -1,14 +1,12 @@ -# Add function/tool calling support for our fleet by the interactions SDK so that natural language chats with the model can become actions. +# Add function/tool calling support for our fleet using Genkit so that natural language chats with the model can become actions. ## Task 1. **Review** the fleet-related functions in `src/server.ts` to identify candidates for tool calling. -2. **Implement Multi-Turn Tool Support**: - - Use the **Interactions API** stateful mode with `previous_interaction_id`. - - **Flat Tool Definition**: Ensure the `tools` array contains flat objects (sibling `type`, `name`, `description`, and `parameters`). - - **Mandatory Schema**: Every tool MUST have a `parameters` schema, even if it is an empty object: `parameters: { type: 'object', properties: {} }`. - - **Function Results**: For subsequent turns, provide `input` as an array of objects where `type: 'function_result'`, using `call_id` (matching the model's output `id`), and a JSON-serializable `result`. +2. **Implement Genkit Tool Calling**: + - Define tools using `ai.defineTool()` with `z` schemas for `name`, `description`, `inputSchema`, and `outputSchema`. + - Pass the defined `tools` to `ai.generate()` in `/api/chat` so Genkit automatically handles multi-turn tool execution. 3. **UI State Synchronization**: - Design a pattern using Angular **`resource`** to fetch data. @@ -17,44 +15,4 @@ 4. **Verification**: - Build the app and confirm it is error-free using Angular MCP tools. - - Use ChromeDevTools MCP to verify end-to-end functionality (e.g., adding a ticket via chat and seeing it appear in the UI). - -## Implementation Reference (Ground Truth) - -```typescript -import { GoogleGenAI } from '@google/genai'; -const client = new GoogleGenAI({}); - -// 1. Tool Definition (Flat structure + Mandatory parameters) -const tool = { - type: 'function', - name: 'get_status', - description: 'Get current system status.', - parameters: { type: 'object', properties: {} } -}; - -// 2. Initial Request -let interaction = await client.interactions.create({ - model: 'gemini-3-flash-preview', - input: 'Status check.', - tools: [tool] -}); - -// 3. Multi-turn Orchestration Loop -while (interaction.status === 'requires_action') { - const outputs = interaction.outputs.filter(o => o.type === 'function_call'); - const results = outputs.map(call => ({ - type: 'function_result', - name: call.name, - call_id: call.id, // Critical mapping - result: { data: 'All systems green' } // Must be JSON serializable - })); - - interaction = await client.interactions.create({ - model: 'gemini-3-flash-preview', - previous_interaction_id: interaction.id, - input: results, - tools: [tool] // Re-specify tools on every turn - }); -} -``` + - Use the `chrome-devtools` skill (`chrome-devtools-plugin`) in the browser (do not use `curl`) to verify end-to-end functionality directly in the UI (e.g., adding a ticket via the chat window and seeing it appear in the UI). diff --git a/logistics-manager-app/content/prompts/versions/prompt-4.md b/logistics-manager-app/content/prompts/versions/prompt-4.md index 3d6ff96..f134600 100644 --- a/logistics-manager-app/content/prompts/versions/prompt-4.md +++ b/logistics-manager-app/content/prompts/versions/prompt-4.md @@ -1,13 +1,9 @@ -# I need to fix the Interactions API tool calling loop in `src/server.ts`. I am getting a `"Missing key 'type' in input 'content'"` error when passing the results of local tools back to the model. - -## Please review the `gemini-interactions-api` and `gemini-api-dev` skills to understand how to correctly format the `input` on subsequent turns when using the `@google/genai` (v1.33+) Interactions API in Node.js. +# I need to verify and refine the Genkit tool calling setup in `src/server.ts`. **Key Requirements:** -1. Use **`ai.interactions.create`** with the **`gemini-3-flash-preview`** model. -2. Pass the conversation state via **`previous_interaction_id`**. -3. Implement a **manual `while` loop** (since Node.js doesn't support automatic calling for local tools). -4. Ensure the **`tools` array** uses the new format (an array of objects with `type: 'function'`). -5. **CRITICAL FIX**: For the **`input` on subsequent turns**, provide an array of objects where `type: 'function_result'` and the `result` field is always a **JSON-serializable object** (wrap arrays in `{ data: [...] }`). Do NOT wrap the array in a `role`/`parts` object. -6. Map the model's **`id`** to the response's **`call_id`** exactly. +1. Initialize Genkit with `genkit({ plugins: [googleAI()], model: googleAI.model('gemini-3-flash-preview') })`. +2. Define each fleet and service-queue tool using `ai.defineTool({ name, description, inputSchema, outputSchema }, handler)` with `z` imported from `'genkit'`. +3. Pass all defined tools in the `tools` array of `ai.generate({ prompt, tools })`. +4. Do **not** use `@genkit-ai/express` (Express v5 is used by Angular SSR). -## Show me your plan before you make any changes so we can be on the same page \ No newline at end of file +## Show me your plan before you make any changes so we can be on the same page diff --git a/logistics-manager-app/content/prompts/versions/prompt-4a.md b/logistics-manager-app/content/prompts/versions/prompt-4a.md index f3db489..7989438 100644 --- a/logistics-manager-app/content/prompts/versions/prompt-4a.md +++ b/logistics-manager-app/content/prompts/versions/prompt-4a.md @@ -1,88 +1,13 @@ -# Optimized Gemini Interactions Tool-Calling Prompt +# Optimized Genkit Tool-Calling Reference Prompt -Use this prompt to implement or fix tool-calling logic using the unified Interactions API. It captures the specific requirements for state management, result formatting, and error prevention. +Use this prompt to implement or refine tool-calling logic using Genkit in `src/server.ts`. --- -**Task:** Implement a multi-turn tool-calling loop using the `@google/genai` (v1.33+) Interactions API in Node.js. +**Task:** Implement tool-calling using `genkit` and `@genkit-ai/google-genai` in Node.js (`src/server.ts`). **Technical Requirements:** -1. **Model:** Use `gemini-3-flash-preview`. -2. **State:** Maintain conversation state using `previous_interaction_id`. -3. **Tool Definition:** Use a **flat** `tools` array. Each tool must be an object with sibling `type: 'function'`, `name`, `description`, and a **mandatory** `parameters` schema (use `{ type: 'object', properties: {} }` for no-arg functions). -4. **Orchestration Loop:** - - Implement a manual `while (interaction.status === 'requires_action')` loop. - - Re-specify the `tools` array on **every** turn inside the loop. -5. **Input Formatting (Subsequent Turns):** - - The `input` for tool-result turns must be an `Array` of objects. - - Each object must have `type: 'function_result'` (use `as const` in TS). - - The `call_id` must map exactly to the `id` provided by the model's `function_call` output. -6. **Result Wrapping (The "Data Wrapper" Pattern):** - - The `result` field must **always** be a JSON-serializable **Object**. - - **CRITICAL:** If a tool returns an Array, wrap it: `{ data: my_array }`. - - **CRITICAL:** The Gemini API throws a 400 error if any field in the result is empty. If an array is empty, return a fallback: `{ data: 'No records found' }` or `{ success: true }`. - -**Goal:** Ensure the model can successfully chain multiple tool calls and provide a final natural language response to the user. - ---- - -## Implementation Reference (Ground Truth) - -```typescript -import { GoogleGenAI } from '@google/genai'; -const ai = new GoogleGenAI({ apiKey: process.env['GEMINI_API_KEY'] }); - -const tools = [ - { - type: 'function', - name: 'get_data', - description: 'Retrieve system data.', - parameters: { type: 'object', properties: {} } - } -] as const; - -// 1. Initial interaction -let interaction = await ai.interactions.create({ - model: 'gemini-3-flash-preview', - input: 'Run a status check.', - tools: tools as any -}); - -// 2. Multi-turn Orchestration Loop -while (interaction.status === 'requires_action') { - const functionCalls = (interaction.outputs || []).filter(o => o.type === 'function_call'); - - const results = await Promise.all(functionCalls.map(async (call: any) => { - // Execute your local tool logic here - const rawResult = await myLocalToolHandler(call.name, call.arguments); - - // CRITICAL: Format result as a non-empty Object - let result: any; - if (Array.isArray(rawResult)) { - result = { data: rawResult.length > 0 ? rawResult : 'No records found' }; - } else if (!rawResult) { - result = { success: true }; - } else { - result = typeof rawResult === 'object' ? rawResult : { value: rawResult }; - } - - return { - type: 'function_result' as const, - name: call.name, - call_id: call.id, // Must match model's output id - result - }; - })); - - // 3. Send results back to the model - interaction = await ai.interactions.create({ - model: 'gemini-3-flash-preview', - previous_interaction_id: interaction.id, - input: results, - tools: tools as any // Re-specify tools - }); -} - -// 4. Final output -console.log(interaction.outputs?.find(o => o.type === 'text')?.text); -``` +1. **Model:** Use `googleAI.model('gemini-3-flash-preview')`. +2. **Tool Definition:** Use `ai.defineTool({ name, description, inputSchema, outputSchema }, handler)` with `z` imported from `'genkit'`. +3. **Automatic Orchestration:** Pass `tools: [...]` to `ai.generate({ prompt, tools })`—Genkit automatically executes tool calls across multiple turns and returns the final natural language response in `response.text`. +4. **Express v5 Compatibility:** Invoke `ai.generate()` directly inside `app.post('/api/chat', ...)`; do not import `@genkit-ai/express`. diff --git a/logistics-manager-app/package.json b/logistics-manager-app/package.json index f4299a4..5c975f1 100644 --- a/logistics-manager-app/package.json +++ b/logistics-manager-app/package.json @@ -32,7 +32,9 @@ "@angular/platform-server": "^21.1.0", "@angular/router": "^21.1.0", "@angular/ssr": "^21.1.3", + "@genkit-ai/google-genai": "^1.42.0", "express": "^5.1.0", + "genkit": "^1.14.0", "leaflet": "^1.9.4", "rxjs": "~7.8.0", "tslib": "^2.3.0" @@ -45,9 +47,11 @@ "@types/express": "^5.0.1", "@types/leaflet": "^1.9.21", "@types/node": "^20.17.19", + "genkit-cli": "^1.13.0", "jsdom": "^27.1.0", "postcss": "^8.5.3", "tailwindcss": "^4.1.12", + "tsx": "^4.20.3", "typescript": "~5.9.2", "vitest": "^4.0.8" } diff --git a/logistics-manager-app/src/app/components/fleet-list/fleet-list.ts b/logistics-manager-app/src/app/components/fleet-list/fleet-list.ts index 9f8fdb0..8cd4d23 100644 --- a/logistics-manager-app/src/app/components/fleet-list/fleet-list.ts +++ b/logistics-manager-app/src/app/components/fleet-list/fleet-list.ts @@ -84,11 +84,11 @@ import { FleetDetailModal } from '../fleet-detail-modal/fleet-detail-modal'; @for (unit of filteredUnits(); track unit.id) { + (click)="selectUnit(unit)">
- {{ unit.id }} + {{ unit.id }}
@@ -133,7 +133,7 @@ import { FleetDetailModal } from '../fleet-detail-modal/fleet-detail-modal'; `, host: { - 'class': 'block flex-1 overflow-hidden' + 'class': 'flex flex-col flex-1 overflow-hidden' } }) export class FleetList { @@ -212,4 +212,9 @@ export class FleetList { if (val < 50) return 'bg-warning text-warning'; // Assuming warning utility if available return 'bg-primary'; } + + selectUnit(unit: FleetUnit) { + this.selectedUnit.set(unit); + this.fleetService.setActiveUnit(unit.id); + } } diff --git a/logistics-manager-app/src/app/components/map/map.ts b/logistics-manager-app/src/app/components/map/map.ts index a3e990c..bbf199d 100644 --- a/logistics-manager-app/src/app/components/map/map.ts +++ b/logistics-manager-app/src/app/components/map/map.ts @@ -9,19 +9,7 @@ import { FleetService } from '../../services/fleet.service'; 'class': 'flex-1 relative bg-surface-container-low flex' }, template: ` -
- -
-
- Active Nodes - 1,204 -
-
- Uptime - 99.998% -
-
-
+
` }) export class Map { @@ -31,6 +19,7 @@ export class Map { private tileLayer: LType.TileLayer | undefined; private markers: { [id: string]: LType.Marker } = {}; private L: typeof LType | undefined; + private lastFocusedUnitId: string | null = null; fleetService = inject(FleetService); @@ -39,7 +28,7 @@ export class Map { import('leaflet').then((leaflet) => { this.L = leaflet; this.initMap(); - this.updateMarkers(); + this.updateMarkers(this.fleetService.activeUnitId()); }); }); @@ -52,6 +41,14 @@ export class Map { this.updateTileLayer(isDark); this.updateMarkers(activeUnitId); + + if (activeUnitId && activeUnitId !== this.lastFocusedUnitId) { + const activeUnit = currentVehicles.find(u => u.id === activeUnitId); + if (activeUnit) { + this.lastFocusedUnitId = activeUnitId; + this.map.flyTo([activeUnit.lat, activeUnit.lng], 7, { duration: 1.2 }); + } + } }); } @@ -59,9 +56,8 @@ export class Map { if (!this.map || !this.L) return; const L = this.L; - const url = isDark - ? 'https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}{r}.png' - : 'https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png'; + const style = isDark ? 'dark_all' : 'voyager'; + const url = `/api/tiles/${style}/{z}/{x}/{y}{r}.png`; if (this.tileLayer) { this.tileLayer.setUrl(url); @@ -91,12 +87,15 @@ export class Map { const pingClass = vehicle.needsService ? 'animate-ping bg-error/50' : 'bg-primary/20'; const colorClass = vehicle.needsService ? 'text-error' : 'text-primary'; const scaleClass = isActive ? 'scale-125' : ''; - const shadowClass = isActive ? 'shadow-[0_0_15px_rgba(255,182,144,0.8)]' : ''; + const shadowClass = isActive + ? 'ring-2 ring-emerald-400 shadow-[0_0_15px_rgba(52,211,153,0.85)]' + : ''; + const svgStrokeAttrs = isActive ? 'stroke="#34d399" stroke-width="1.75"' : ''; const markerHtml = `
- +
diff --git a/logistics-manager-app/src/app/components/service-queue/service-queue.ts b/logistics-manager-app/src/app/components/service-queue/service-queue.ts index 0c5577c..407df32 100644 --- a/logistics-manager-app/src/app/components/service-queue/service-queue.ts +++ b/logistics-manager-app/src/app/components/service-queue/service-queue.ts @@ -213,7 +213,7 @@ import { ServiceTicket } from '../../../fleet-db'; } `, host: { - 'class': 'block flex-1 overflow-hidden' + 'class': 'flex flex-col flex-1 overflow-hidden' } }) export class ServiceQueue { diff --git a/logistics-manager-app/src/app/components/sidebar/sidebar.ts b/logistics-manager-app/src/app/components/sidebar/sidebar.ts index b3370ed..c25035d 100644 --- a/logistics-manager-app/src/app/components/sidebar/sidebar.ts +++ b/logistics-manager-app/src/app/components/sidebar/sidebar.ts @@ -1,4 +1,4 @@ -import { Component, inject, effect, afterNextRender, Injector } from '@angular/core'; +import { Component, inject, effect, afterNextRender, Injector, signal, computed } from '@angular/core'; import { FleetService, FleetUnit } from '../../services/fleet.service'; @Component({ @@ -19,18 +19,31 @@ import { FleetService, FleetUnit } from '../../services/fleet.service';
- -
-
- CRITICAL ALERT - warning -
-

V-UNIT 04_ALPHA

-

POWER CELL DEPLETION CRITICAL: 4%

-
-
+ + @if (criticalUnits().length > 0) { + @let current = currentAlert(); +
+
+
+ CRITICAL ALERT + {{ currentAlertIndex() + 1 }} / {{ criticalUnits().length }} +
+
+ + + warning +
+
+

+ {{ current?.id }} +

+

POWER CELL DEPLETION CRITICAL: {{ current?.battery }}

+
+
+
-
+ }
@@ -49,7 +62,7 @@ import { FleetService, FleetUnit } from '../../services/fleet.service'; [class.bg-opacity-10]="unit.active" (click)="selectUnit(unit)">
- {{ unit.id }} + {{ unit.id }} {{ unit.status }} @@ -75,6 +88,25 @@ export class Sidebar { fleetService = inject(FleetService); injector = inject(Injector); + criticalUnits = computed(() => + this.fleetService.units().filter(u => u.status === 'CRITICAL' || (parseFloat(u.battery) < 20)) + ); + + currentAlertIndex = signal(0); + + currentAlert = computed(() => { + const units = this.criticalUnits(); + return units.length > 0 ? units[this.currentAlertIndex() % units.length] : null; + }); + + nextAlert() { + this.currentAlertIndex.update(i => (i + 1) % this.criticalUnits().length); + } + + prevAlert() { + this.currentAlertIndex.update(i => (i - 1 + this.criticalUnits().length) % this.criticalUnits().length); + } + constructor() { effect(() => { const activeId = this.fleetService.activeUnitId(); diff --git a/logistics-manager-app/src/app/components/sidenav/sidenav.ts b/logistics-manager-app/src/app/components/sidenav/sidenav.ts index 8efd0e5..da16b95 100644 --- a/logistics-manager-app/src/app/components/sidenav/sidenav.ts +++ b/logistics-manager-app/src/app/components/sidenav/sidenav.ts @@ -8,19 +8,6 @@ import { Component } from '@angular/core'; - - - - - ` }) diff --git a/logistics-manager-app/src/app/services/fleet.service.ts b/logistics-manager-app/src/app/services/fleet.service.ts index c9ae4af..9cf99cd 100644 --- a/logistics-manager-app/src/app/services/fleet.service.ts +++ b/logistics-manager-app/src/app/services/fleet.service.ts @@ -24,7 +24,7 @@ export class FleetService { readonly units = computed(() => this.unitsResource.value() ?? []); readonly serviceQueue = computed(() => this.serviceQueueResource.value() ?? []); - readonly activeUnitId = signal('V-UNIT 01_BETA'); + readonly activeUnitId = signal(null); readonly isDarkMode = signal(true); readonly isChatOpen = signal(false); diff --git a/logistics-manager-app/src/server.ts b/logistics-manager-app/src/server.ts index fe49a1c..7aa77dc 100644 --- a/logistics-manager-app/src/server.ts +++ b/logistics-manager-app/src/server.ts @@ -5,10 +5,14 @@ import { writeResponseToNodeResponse, } from '@angular/ssr/node'; import express from 'express'; +import { existsSync, readFileSync } from 'node:fs'; import { join } from 'node:path'; -import { loadEnvFile } from 'node:process'; +import { parseEnv } from 'node:util'; import { fleetDb, FleetUnit, ServiceTicket } from './fleet-db'; -loadEnvFile(); + +if (existsSync('.env')) { + Object.assign(process.env, parseEnv(readFileSync('.env', 'utf-8'))); +} const browserDistFolder = join(import.meta.dirname, '../browser'); const app = express(); @@ -50,6 +54,28 @@ export function deleteServiceTicket(id: string): boolean { } // --- REST API ENDPOINTS --- +app.get('/api/tiles/:style/:z/:x/:y', async (req, res) => { + try { + const { style, z, x, y } = req.params; + const key = process.env['CARTO_API_KEY'] ?? ''; + const basePath = style === 'voyager' ? 'rastertiles/voyager' : style; + const cartoUrl = `https://basemaps.cartocdn.com/${basePath}/${z}/${x}/${y}?key=${key}`; + + const response = await fetch(cartoUrl); + if (!response.ok) { + res.status(response.status).end(); + return; + } + + res.setHeader('Content-Type', response.headers.get('content-type') || 'image/png'); + res.setHeader('Cache-Control', 'public, max-age=86400'); + const buffer = Buffer.from(await response.arrayBuffer()); + res.send(buffer); + } catch { + res.status(500).end(); + } +}); + app.get('/api/fleet', (req, res) => { res.json(getAllFleetUnits()); }); @@ -149,4 +175,4 @@ if (isMainModule(import.meta.url) || process.env['pm_id']) { /** * Request handler used by the Angular CLI (for dev-server and during build) or Firebase Cloud Functions. */ -export const reqHandler = createNodeRequestHandler(app); +export const reqHandler = createNodeRequestHandler(app); \ No newline at end of file