├── .github └── workflows │ └── deploy-website.yml ├── .gitignore ├── Dockerfile ├── LICENSE ├── README.md ├── app ├── index.html ├── package-lock.json ├── package.json ├── postcss.config.js ├── public │ ├── agent-activity_tracking_agent.yaml │ ├── agent-command_tracking_agent.yaml │ ├── agent-documentation_agent.yaml │ ├── agent-focus_tracker.yaml │ ├── agent-memory_summarization.yaml │ └── eye-logo-black.svg ├── src-tauri │ ├── .gitignore │ ├── Cargo.lock │ ├── Cargo.toml │ ├── build.rs │ ├── capabilities │ │ └── default.json │ ├── icons │ │ ├── 128x128.png │ │ ├── 128x128@2x.png │ │ ├── 32x32.png │ │ ├── Square107x107Logo.png │ │ ├── Square142x142Logo.png │ │ ├── Square150x150Logo.png │ │ ├── Square284x284Logo.png │ │ ├── Square30x30Logo.png │ │ ├── Square310x310Logo.png │ │ ├── Square44x44Logo.png │ │ ├── Square71x71Logo.png │ │ ├── Square89x89Logo.png │ │ ├── StoreLogo.png │ │ ├── icon.icns │ │ ├── icon.ico │ │ └── icon.png │ ├── src │ │ ├── lib.rs │ │ └── main.rs │ └── tauri.conf.json ├── src │ ├── components │ │ ├── AgentCard.tsx │ │ ├── AgentImportHandler.tsx │ │ ├── AgentLogViewer.tsx │ │ ├── AppHeader.tsx │ │ ├── AvailableModels.tsx │ │ ├── CommunityTab.tsx │ │ ├── EditAgent │ │ │ ├── EditAgentModal.tsx │ │ │ ├── Modal.tsx │ │ │ └── useEditAgentModalLogic.ts │ │ ├── ErrorDisplay.tsx │ │ ├── GenerateAgent.tsx │ │ ├── GenerateAgentModal.tsx │ │ ├── GetStarted.tsx │ │ ├── GlobalLogsViewer.tsx │ │ ├── JupyterServerModal.tsx │ │ ├── LocalServerSetupDialog.tsx │ │ ├── MemoryManager.tsx │ │ ├── ScheduleAgentModal.tsx │ │ ├── SidebarMenu.tsx │ │ ├── StartupDialogs.tsx │ │ ├── TextBubble.tsx │ │ └── debug │ │ │ └── Auth0Debug.tsx │ ├── desktop │ │ └── .gitkeep │ ├── env.d.ts │ ├── index.css │ ├── utils │ │ ├── agent-output.ts │ │ ├── agent_database.ts │ │ ├── handlers │ │ │ ├── JupyterConfig.ts │ │ │ ├── javascript.ts │ │ │ ├── python.ts │ │ │ └── utils.ts │ │ ├── logging.ts │ │ ├── main_loop.ts │ │ ├── ollamaServer.ts │ │ ├── post-processor.ts │ │ ├── pre-processor.ts │ │ ├── python_system_prompt.ts │ │ ├── screenCapture.ts │ │ ├── sendApi.ts │ │ ├── speechInputManager.ts │ │ ├── streamApi.ts │ │ ├── system_prompt.ts │ │ └── test_system_prompt.ts │ └── web │ │ ├── App.tsx │ │ ├── index.tsx │ │ └── main.tsx ├── stats.html ├── tailwind.config.js ├── tsconfig.json ├── tsconfig.node.json └── vite.config.ts ├── assets └── ObserverAgent.png ├── community ├── api.py ├── api_handlers.py ├── compute.py ├── fireworks_handler.py ├── gemini_handler.py ├── marketplace.py ├── openrouter_handler.py └── requirements.txt ├── docker-compose.yml ├── observer-ollama ├── LICENSE ├── observer_ollama │ ├── __init__.py │ ├── handle_ollama.py │ ├── main.py │ └── ssl_utils.py ├── pyproject.toml └── test.py ├── print_info.sh ├── supervisord.conf └── website ├── .github └── workflows │ └── deploy-website.yml ├── .gitignore ├── README.md ├── eslint.config.js ├── index.html ├── package-lock.json ├── package.json ├── postcss.config.js ├── public ├── CNAME ├── eye-logo-black.svg ├── eye-logo-white.svg └── vite.svg ├── src ├── App.css ├── App.tsx ├── ObserverLanding.tsx ├── assets │ └── react.svg ├── index.css ├── main.tsx └── vite-env.d.ts ├── tailwind.config.js ├── tsconfig.app.json ├── tsconfig.json ├── tsconfig.node.json └── vite.config.ts /.github/workflows/deploy-website.yml: -------------------------------------------------------------------------------- 1 | name: Deploy Website 2 | 3 | on: 4 | push: 5 | branches: [ "main" ] 6 | paths: 7 | - 'website/**' # Only trigger on changes to website directory 8 | 9 | permissions: 10 | contents: read 11 | pages: write 12 | id-token: write 13 | 14 | jobs: 15 | build: 16 | runs-on: ubuntu-latest 17 | steps: 18 | - uses: actions/checkout@v4 19 | 20 | - name: Setup Node 21 | uses: actions/setup-node@v4 22 | with: 23 | node-version: '20' 24 | 25 | - name: Install Dependencies 26 | working-directory: ./website 27 | run: npm install 28 | 29 | - name: Build 30 | working-directory: ./website 31 | run: npm run build 32 | 33 | - name: Upload artifact 34 | uses: actions/upload-pages-artifact@v3 35 | with: 36 | path: website/dist 37 | 38 | deploy: 39 | needs: build 40 | runs-on: ubuntu-latest 41 | environment: 42 | name: github-pages 43 | url: ${{ steps.deployment.outputs.page_url }} 44 | steps: 45 | - name: Deploy to GitHub Pages 46 | id: deployment 47 | uses: actions/deploy-pages@v4 48 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | Ob-server/certs 2 | 3 | website/dist 4 | website/build 5 | 6 | website/src-tauri/target 7 | website/src-tauri/Cargo.lock 8 | 9 | website/node_modules 10 | website/.pnp 11 | 12 | app/node_modules 13 | app/dist 14 | app/.env 15 | 16 | desktop/node_modules 17 | desktop/public/assets/ 18 | desktop/.pnp 19 | 20 | desktop/dist 21 | desktop/build 22 | 23 | desktop/src-tauri/target 24 | desktop/src-tauri/Cargo.lock 25 | 26 | observer_ollama/dist 27 | observer_ollama/observer_ollama.egg-info 28 | **/*.rs.bk 29 | 30 | .pnp.js 31 | 32 | # Python 33 | __pycache__/ 34 | *.py[cod] 35 | *$py.class 36 | *.so 37 | .Python 38 | /python/**/data/ 39 | /python/**/*.log 40 | /python/**/activity_log.json 41 | 42 | # Environment 43 | .env 44 | .env.local 45 | .env.development.local 46 | .env.test.local 47 | .env.production.local 48 | .venv 49 | env/ 50 | venv/ 51 | 52 | # Logs 53 | *.log 54 | npm-debug.log* 55 | yarn-debug.log* 56 | yarn-error.log* 57 | 58 | # Editor directories and files 59 | .idea 60 | .vscode 61 | *.swp 62 | *.swo 63 | .DS_Store 64 | 65 | # Cache directories 66 | .cache 67 | .pytest_cache/ 68 | .mypy_cache/ 69 | .coverage 70 | coverage/ 71 | .eslintcache 72 | 73 | # Misc 74 | *.pem 75 | .DS_Store 76 | /node_modules 77 | /.pnp 78 | .pnp.js 79 | 80 | # Production build 81 | /dist 82 | /build 83 | 84 | # Tauri 85 | /src-tauri/target 86 | /src-tauri/Cargo.lock 87 | **/*.rs.bk 88 | 89 | # Python 90 | __pycache__/ 91 | *.py[cod] 92 | *$py.class 93 | *.so 94 | .Python 95 | /python/**/data/ 96 | /python/**/*.log 97 | /python/**/activity_log.json 98 | 99 | # Environment 100 | .env 101 | .env.local 102 | .env.development.local 103 | .env.test.local 104 | .env.production.local 105 | .venv 106 | env/ 107 | venv/ 108 | 109 | # Logs 110 | *.log 111 | npm-debug.log* 112 | yarn-debug.log* 113 | yarn-error.log* 114 | 115 | # Editor directories and files 116 | .idea 117 | .vscode 118 | *.swp 119 | *.swo 120 | .DS_Store 121 | 122 | # Cache directories 123 | .cache 124 | .pytest_cache/ 125 | .mypy_cache/ 126 | .coverage 127 | coverage/ 128 | .eslintcache 129 | 130 | # Misc 131 | *.pem 132 | .DS_Storeompiled / optimized / DLL files 133 | __pycache__/ 134 | *.py[cod] 135 | *$py.class 136 | 137 | # C extensions 138 | *.so 139 | 140 | # Distribution / packaging 141 | .Python 142 | build/ 143 | develop-eggs/ 144 | dist/ 145 | downloads/ 146 | eggs/ 147 | .eggs/ 148 | lib/ 149 | lib64/ 150 | parts/ 151 | sdist/ 152 | var/ 153 | wheels/ 154 | share/python-wheels/ 155 | *.egg-info/ 156 | .installed.cfg 157 | *.egg 158 | MANIFEST 159 | 160 | # PyInstaller 161 | # Usually these files are written by a python script from a template 162 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 163 | *.manifest 164 | *.spec 165 | 166 | # Installer logs 167 | pip-log.txt 168 | pip-delete-this-directory.txt 169 | 170 | # Unit test / coverage reports 171 | htmlcov/ 172 | .tox/ 173 | .nox/ 174 | .coverage 175 | .coverage.* 176 | .cache 177 | nosetests.xml 178 | coverage.xml 179 | *.cover 180 | *.py,cover 181 | .hypothesis/ 182 | .pytest_cache/ 183 | cover/ 184 | 185 | # Translations 186 | *.mo 187 | *.pot 188 | 189 | # Django stuff: 190 | *.log 191 | local_settings.py 192 | db.sqlite3 193 | db.sqlite3-journal 194 | 195 | # Flask stuff: 196 | instance/ 197 | .webassets-cache 198 | 199 | # Scrapy stuff: 200 | .scrapy 201 | 202 | # Sphinx documentation 203 | docs/_build/ 204 | 205 | # PyBuilder 206 | .pybuilder/ 207 | target/ 208 | 209 | # Jupyter Notebook 210 | .ipynb_checkpoints 211 | 212 | # IPython 213 | profile_default/ 214 | ipython_config.py 215 | 216 | # pyenv 217 | # For a library or package, you might want to ignore these files since the code is 218 | # intended to run in multiple environments; otherwise, check them in: 219 | # .python-version 220 | 221 | # pipenv 222 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 223 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 224 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 225 | # install all needed dependencies. 226 | #Pipfile.lock 227 | 228 | # UV 229 | # Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control. 230 | # This is especially recommended for binary packages to ensure reproducibility, and is more 231 | # commonly ignored for libraries. 232 | #uv.lock 233 | 234 | # poetry 235 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. 236 | # This is especially recommended for binary packages to ensure reproducibility, and is more 237 | # commonly ignored for libraries. 238 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control 239 | #poetry.lock 240 | 241 | # pdm 242 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. 243 | #pdm.lock 244 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it 245 | # in version control. 246 | # https://pdm.fming.dev/latest/usage/project/#working-with-version-control 247 | .pdm.toml 248 | .pdm-python 249 | .pdm-build/ 250 | 251 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm 252 | __pypackages__/ 253 | 254 | # Celery stuff 255 | celerybeat-schedule 256 | celerybeat.pid 257 | 258 | # SageMath parsed files 259 | *.sage.py 260 | 261 | # Environments 262 | .env 263 | .venv 264 | env/ 265 | venv/ 266 | ENV/ 267 | env.bak/ 268 | venv.bak/ 269 | 270 | # Spyder project settings 271 | .spyderproject 272 | .spyproject 273 | 274 | # Rope project settings 275 | .ropeproject 276 | 277 | # mkdocs documentation 278 | /site 279 | 280 | # mypy 281 | .mypy_cache/ 282 | .dmypy.json 283 | dmypy.json 284 | 285 | # Pyre type checker 286 | .pyre/ 287 | 288 | # pytype static type analyzer 289 | .pytype/ 290 | 291 | # Cython debug symbols 292 | cython_debug/ 293 | 294 | # PyCharm 295 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can 296 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 297 | # and can be added to the global gitignore or merged into this file. For a more nuclear 298 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. 299 | #.idea/ 300 | 301 | # PyPI configuration file 302 | .pypirc 303 | 304 | 305 | .DS_Store 306 | .DS_Store? 307 | desktop/src-tauri/target/* 308 | desktop/src-tauri/python/python-bundle 309 | community/marketplace.db 310 | community/marketplace.db 311 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # Stage 1: Build the Vite application 2 | FROM node:20-alpine as builder 3 | 4 | WORKDIR /app 5 | COPY app/package.json app/package-lock.json* ./ 6 | RUN npm install 7 | COPY app/ . 8 | RUN npm run build 9 | 10 | # Stage 2: Nginx + Python Proxy ONLY 11 | FROM nginx:alpine 12 | 13 | # Install Python, pip, openssl (for cert generation), supervisor 14 | # NO curl, NO gcompat, NO Ollama installation here 15 | RUN apk add --no-cache python3 py3-pip openssl supervisor 16 | 17 | # --- Nginx Setup --- 18 | COPY --from=builder /app/dist /usr/share/nginx/html 19 | EXPOSE 80 20 | 21 | # --- Python Proxy Setup --- 22 | RUN mkdir -p /opt/observer-ollama /var/log/supervisor 23 | COPY ./observer-ollama /opt/observer-ollama/ 24 | WORKDIR /opt/observer-ollama 25 | RUN pip3 install --break-system-packages . 26 | EXPOSE 3838 27 | 28 | # --- Tell user webpage is available --- 29 | COPY ./print_info.sh /usr/local/bin/print_info.sh 30 | RUN chmod +x /usr/local/bin/print_info.sh 31 | 32 | # --- Supervisor Setup --- 33 | COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf 34 | WORKDIR / 35 | 36 | # Command to run Supervisor 37 | CMD ["/usr/bin/supervisord", "-c", "/etc/supervisor/conf.d/supervisord.conf"] 38 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 Roy Medina 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Observer AI 👁️ 2 | 3 | [Observer App Link](https://app.observer-ai.com/) 4 | 5 | - [Support me and the project!](https://buymeacoffee.com/roy3838) 6 | 7 | An open-source platform for running local AI agents that enhance your computing experience while preserving privacy. 8 | 9 | 10 | [![GitHub Pages](https://img.shields.io/badge/GitHub%20Pages-Deployed-success)](https://roy3838.github.io/observer-ai) 11 | [![License](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE) 12 | 13 | Demo: 14 | 15 | https://github.com/user-attachments/assets/def0cba9-c8c3-41d3-bd03-a507744e6ade 16 | 17 | 18 | 19 | 20 | 30 | 33 | 34 |
21 |

Key Features

22 |
    23 |
  • 🔒 Privacy First: All processing happens locally on your machine
  • 24 |
  • 💻 Resource Efficient: Take advantage of unused consumer-grade hardware
  • 25 |
  • 🔌 Extensible: Easy-to-use framework for creating and sharing custom agents
  • 26 |
  • 🤝 Community Driven: Growing ecosystem of community-created agents
  • 27 |
  • 🐍 Jupyter Server Support: Run Python agents with system-level access
  • 28 |
29 |
31 | ObserverAI Agent Diagram 32 |
35 | 36 | ## 🚀 Getting Started with Local Inference 37 | 38 | We need to wrap Ollama to use https instead of http so that the browser can connect to it. This is done with self-signed SSL certificates. 39 | 40 | ```bash 41 | # Make sure to have [Ollama](https://ollama.com) installed 42 | 43 | # For local inference run observer-ollama 44 | pip install observer-ollama 45 | 46 | # Click on the link provided so that your browser accepts self signed CERTS (signed by your computer) 47 | 48 | # OLLAMA-PROXY ready 49 | # ➜ Local: https://localhost:3838/ 50 | # ➜ Network: https://10.0.0.138:3838/ 51 | 52 | # Click on proceed to localhost (unsafe), if "Ollama is running" shows up, you're done! 53 | 54 | # Go to webapp: 55 | app.observer-ai.com 56 | 57 | # Enter your inference IP (localhost:3838) on the app header. 58 | ``` 59 | 60 | # 🏗️ Building Your Own Agent 61 | 62 | Creating your own Observer AI agent is simple and accessible to both beginners and experienced developers. 63 | 64 | ## Quick Start 65 | 66 | 1. Navigate to the Agent Dashboard and click "Create New Agent" 67 | 2. Fill in the "Configuration" tab with basic details (name, description, model, loop interval) 68 | 3. Use a system prompt with input variables! The current input variables that exist are: 69 | * **Screen OCR** ($SCREEN_OCR) Captures screen content as text via OCR (english only for now) 70 | * **Screenshot** ($SCREEN_64) Captures screen as an image for multimodal models 71 | * **Agent Memory** ($MEMORY@agent_id) Accesses agents' stored information 72 | * **Microphone** ($MICROPHONE) Captures the microphone and adds a transcription (english only for now) 73 | 74 | ## Code Tab 75 | 76 | The "Code" tab now offers a notebook-style coding experience where you can choose between JavaScript or Python execution: 77 | 78 | ### JavaScript (Browser-based) 79 | 80 | JavaScript agents run in the browser sandbox, making them ideal for passive monitoring and notifications: 81 | 82 | ```javascript 83 | // Remove Think tags for deepseek model 84 | const cleanedResponse = response.replace(/[\s\S]*?<\/think>/g, '').trim(); 85 | 86 | // Preserve previous memory 87 | const prevMemory = await getMemory(); 88 | 89 | // Get time 90 | const time = time(); 91 | 92 | // Update memory with timestamp 93 | appendMemory(`[${time}] ${cleanedResponse}`); 94 | ``` 95 | 96 | > **Note:** any function marked with `*` takes an `agentId` argument. 97 | > If you omit `agentId`, it defaults to the agent that’s running the code. 98 | 99 | Available utilities include: 100 | 101 | * `time()` – Get the current timestamp 102 | * `pushNotification(title, options)` – Send notifications 103 | * `getMemory(agentId)*` – Retrieve stored memory (defaults to current agent) 104 | * `setMemory(agentId, content)*` – Replace stored memory 105 | * `appendMemory(agentId, content)*` – Add to existing memory 106 | * `startAgent(agentId)*` – Starts an agent 107 | * `stopAgent(agentId)*` – Stops an agent 108 | 109 | 110 | ### Python (Jupyter Server) 111 | 112 | Python agents run on a Jupyter server with system-level access, enabling them to interact directly with your computer: 113 | 114 | ```python 115 | #python <-- don't remove this! 116 | print("Hello World!", response, agentId) 117 | 118 | # Example: Analyze screen content and take action 119 | if "SHUTOFF" in response: 120 | # System level commands can be executed here 121 | import os 122 | # os.system("command") # Be careful with system commands! 123 | ``` 124 | 125 | The Python environment receives: 126 | * `response` - The model's output 127 | * `agentId` - The current agent's ID 128 | 129 | ## Example: Command Tracking Agent 130 | 131 | A simple agent that responds to specific commands in the model's output: 132 | 133 | ```javascript 134 | //Clean response 135 | const cleanedResponse = response.replace(/[\s\S]*?<\/think>/g, '').trim(); 136 | 137 | //Command Format 138 | if (cleanedResponse.includes("COMMAND")) { 139 | const withoutcommand = cleanedResponse.replace(/COMMAND:/g, ''); 140 | setMemory(`${await getMemory()} \n[${time()}] ${withoutcommand}`); 141 | } 142 | ``` 143 | 144 | ## Jupyter Server Configuration 145 | 146 | To use Python agents: 147 | 148 | 1. Run a Jupyter server on your machine 149 | 2. Configure the connection in the Observer AI interface: 150 | * Host: The server address (e.g., 127.0.0.1) 151 | * Port: The server port (e.g., 8888) 152 | * Token: Your Jupyter server authentication token 153 | 3. Test the connection using the "Test Connection" button 154 | 4. Switch to the Python tab in the code editor to write Python-based agents 155 | 156 | ## Deploy & Share 157 | 158 | Save your agent, test it from the dashboard, and export the configuration to share with others! 159 | 160 | ## 🤝 Contributing 161 | 162 | We welcome contributions from the community! Here's how you can help: 163 | 164 | 1. Fork the repository 165 | 2. Create your feature branch (`git checkout -b feature/amazing-feature`) 166 | 3. Commit your changes (`git commit -m 'feat: add amazing feature'`) 167 | 4. Push to the branch (`git push origin feature/amazing-feature`) 168 | 5. Open a Pull Request 169 | 170 | ## 📄 License 171 | 172 | This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details. 173 | 174 | ## 🔗 Links 175 | 176 | - [Website](https://observer-ai.com) 177 | - [GitHub Repository](https://github.com/Roy3838/Observer) 178 | - [twitter](https://x.com/AppObserverAI) 179 | 180 | ## 📧 Contact 181 | 182 | - GitHub: [@Roy3838](https://github.com/Roy3838) 183 | - Project Link: [https://github.com/Roy3838/observer-ai](https://github.com/Roy3838/Observer) 184 | 185 | --- 186 | 187 | Built with ❤️ by the Observer AI Community 188 | -------------------------------------------------------------------------------- /app/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Observer Web 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@observer/webapp", 3 | "private": true, 4 | "version": "0.1.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "tsc && vite build", 9 | "preview": "vite preview", 10 | "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", 11 | "format": "prettier --write .", 12 | "tauri": "tauri", 13 | "tauri:dev": "tauri dev", 14 | "tauri:build": "tauri build" 15 | }, 16 | "dependencies": { 17 | "@auth0/auth0-react": "^2.3.0", 18 | "@codemirror/lang-javascript": "^6.2.3", 19 | "@codemirror/lang-python": "^6.1.7", 20 | "@jupyterlab/services": "^7.3.6", 21 | "@lexical/history": "^0.25.0", 22 | "@lexical/list": "^0.25.0", 23 | "@lexical/react": "^0.25.0", 24 | "@lexical/rich-text": "^0.25.0", 25 | "@lexical/selection": "^0.25.0", 26 | "@tauri-apps/api": "^2.3.0", 27 | "@tinymce/tinymce-react": "^6.0.0", 28 | "@uiw/codemirror-theme-vscode": "^4.23.8", 29 | "@uiw/react-codemirror": "^4.23.8", 30 | "fs": "^0.0.1-security", 31 | "js-yaml": "^4.1.0", 32 | "lexical": "^0.25.0", 33 | "lucide-react": "^0.476.0", 34 | "path": "^0.12.7", 35 | "react": "^18.2.0", 36 | "react-dom": "^18.2.0", 37 | "react-router-dom": "^6.22.1", 38 | "slate": "^0.112.0", 39 | "slate-history": "^0.110.3", 40 | "slate-react": "^0.112.1", 41 | "tesseract.js": "^6.0.0", 42 | "ts-node": "^10.9.2", 43 | "yaml": "^2.7.0" 44 | }, 45 | "devDependencies": { 46 | "@tauri-apps/cli": "^2.3.0", 47 | "@types/dom-speech-recognition": "^0.0.6", 48 | "@types/js-yaml": "^4.0.9", 49 | "@types/react": "^18.2.55", 50 | "@types/react-dom": "^18.2.19", 51 | "@typescript-eslint/eslint-plugin": "^6.21.0", 52 | "@typescript-eslint/parser": "^6.21.0", 53 | "@vitejs/plugin-react": "^4.2.1", 54 | "autoprefixer": "^10.4.17", 55 | "esbuild": "0.21.5", 56 | "eslint": "^8.56.0", 57 | "eslint-plugin-react-hooks": "^4.6.0", 58 | "eslint-plugin-react-refresh": "^0.4.5", 59 | "postcss": "^8.4.35", 60 | "prettier": "^3.2.5", 61 | "rollup-plugin-visualizer": "^5.14.0", 62 | "tailwindcss": "^3.4.1", 63 | "typescript": "~5.3.3", 64 | "vite": "^5.1.0" 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /app/postcss.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /app/public/agent-activity_tracking_agent.yaml: -------------------------------------------------------------------------------- 1 | id: activity_tracking_agent 2 | name: Activity Tracking Agent 3 | description: This agent tracks your activity. 4 | status: stopped 5 | model_name: deepseek-r1:8b 6 | loop_interval_seconds: 25 7 | system_prompt: | 8 | You are an activity tracking agent, watch the screen and respond with what the user is doing. 9 | Just respond with one sentence as the following: 10 | 11 | ACTIVITY: activity the user is doing 12 | 13 | 14 | $SCREEN_OCR 15 | 16 | 17 | 18 | Just respond with that one sentence. 19 | 20 | ACTIVITY: say what the user is doing 21 | code: | 22 | //ACTIVITY 23 | function(line) { 24 | const currentMemory = utilities.getAgentMemory(agentId); 25 | const time = utilities.getCurrentTime(); 26 | currentMemory.then(memory => { 27 | utilities.updateAgentMemory(agentId, memory + "\n" + "[" + time + "] " + line); 28 | }); 29 | } 30 | memory: | 31 | 32 | [ 10:27 pm ]The user is engaged in multiple activities involving screen sharing, code documentation, focus tracking, and memory summarization. 33 | [ 10:28 pm ]The user is configuring their network settings or checking their IP address via related applications. 34 | [ 10:28 pm ]The user is repeatedly sending POST requests to "/api/generate" and also made a GET request to "/api/tags". 35 | [ 10:29 pm ]interact with tracking agents via commands and monitor system logs. 36 | [ 10:29 pm ]sending repeated POST requests to "/api/generate" and making a GET request to "/api/tags". 37 | [ 10:30 pm ]The user is working on a project, running Git commands, and setting up a screen share for a presentation or demo. 38 | [ 10:30 pm ]The user is running `npm run build`, compiling TypeScript files, and updating a GitHub repository while setting up a remote screen session for development work. 39 | [ 10:30 pm ]Compiling a TypeScript web application using Vite and running a build command. 40 | [ 10:31 pm ]Running npm build command in the app directory and starting a screen session for development. 41 | [ 10:31 pm ]The user is building a React application and performing remote development work. 42 | [ 10:32 pm ]The user is developing a web-based application using React and TypeScript, managing code changes with Git, and running builds with Vite. 43 | [ 10:32 pm ]The user is running a build process for their web application using npm, TypeScript, and Vite. 44 | [ 10:32 pm ]Running npm commands to build a web application and managing screen sessions. 45 | [ 10:33 pm ]The user is running build commands and working on a web application using Vite. 46 | [ 10:33 pm ]Building the application and starting a screen session to serve it remotely. 47 | [ 10:34 pm ]Compiling code and running build commands, then starting a screen session. 48 | [ 10:34 pm ]Updating Git repository, running build command, and starting a screen session. 49 | [ 10:35 pm ]The user is running commands related to building and serving a web application, including Git operations and Vite compilation. 50 | [ 10:35 pm ]View system logs using a network monitoring tool. 51 | [ 10:36 pm ]configure network settings and view system logs 52 | [ 10:36 pm ]the user is interacting with system logs via commands. 53 | [ 10:37 pm ]The user is checking their public IP address using a command in the terminal or command line interface. 54 | [ 10:37 pm ]using multiple applications such as Chrome, Discord, and a terminal where they are typing commands like "Show System Logs." 55 | [ 10:38 pm ]The user is accessing network settings and viewing system logs on their device. 56 | [ 10:38 pm ]viewing system logs and managing network settings. 57 | [2:37 pm] Updating application code to handle server connections and configuration changes. 58 | [2:38 pm] The user is running build commands, updating the Git repository, configuring network settings, and viewing system logs as they work on a web application using Vite. 59 | [4:56 pm] The user is interacting with multiple AI tracking agents and attempting to manage screen sharing settings. 60 | [4:56 pm] The user is actively engaged on Telegram, managing multiple groups, interacting with others, and sharing various content including personal updates and YouTube links. 61 | [4:57 pm] The user is communicating via Telegram with others, sending messages, and engaging in conversations. 62 | [4:57 pm] Using Telegram to communicate with others, including sending messages and joining groups. 63 | [4:57 pm] Contributing to an open-source project on GitHub by forking repositories and setting up feature branches. 64 | [4:58 pm] launching system logs viewer to inspect log files and monitor system activity. 65 | [5:02 pm] using various software applications and tracking tools, possibly for design, documentation, and automation purposes. 66 | [5:02 pm] Configuring activity tracking systems using various agents and applications. 67 | [9:44 pm] The user is working on code and interacting with activity tracking agents that assist in managing focus and summarizing tasks. 68 | [9:44 pm] The user is actively working on a web application using Vite, managing server connections, configuration changes, and Git repositories, while also interacting with various AI tracking agents and communication tools like Telegram. -------------------------------------------------------------------------------- /app/public/agent-command_tracking_agent.yaml: -------------------------------------------------------------------------------- 1 | id: command_tracking_agent 2 | name: Command Tracking Agent 3 | description: This agent looks at the screen and tracks all the commands you use. 4 | status: stopped 5 | model_name: deepseek-r1:8b 6 | loop_interval_seconds: 30 7 | system_prompt: | 8 | You are a command tracking assistant. Monitor the screen and identify any commands being run by the user. 9 | 10 | Look for terminal/console windows and command prompts. 11 | 12 | Simply respond with: 13 | 14 | COMMAND: the command that was executed 15 | 16 | Examples: 17 | 18 | COMMAND: git push origin main 19 | 20 | COMMAND: npm install react 21 | 22 | COMMAND: python script.py 23 | 24 | 25 | Only report when you see a new command being executed. 26 | 27 | Ignore repeated commands and command output. 28 | 29 | Focus on actual commands, not general terminal text or prompts. 30 | code: | 31 | //COMMAND 32 | function(line) { 33 | const currentMemory = utilities.getAgentMemory(agentId); 34 | const time = utilities.getCurrentTime(); 35 | currentMemory.then(memory => { 36 | utilities.updateAgentMemory(agentId, memory + "\n" + "[ " + time + " ]" + line); 37 | }); 38 | } 39 | memory: "" 40 | -------------------------------------------------------------------------------- /app/public/agent-documentation_agent.yaml: -------------------------------------------------------------------------------- 1 | id: documentation_agent 2 | name: Code Documentation Agent 3 | description: This Agent watches your screen and if there is code, he will document code in the background 4 | status: stopped 5 | model_name: deepseek-r1:8b 6 | loop_interval_seconds: 60 7 | system_prompt: | 8 | You are a Documentation Generator agent that observes code being written and automatically drafts documentation. 9 | 10 | When you see code on screen, analyze it and generate appropriate documentation in a concise, professional style. 11 | 12 | Focus on: 13 | 1. Function purpose and behavior 14 | 2. Parameters and return values 15 | 3. Dependencies and side effects 16 | 4. Usage examples when helpful 17 | 18 | Respond only with the following format: 19 | DOCGEN: [Brief description of what documentation was generated] 20 | 21 | Existing Documentation: 22 | $MEMORY@documentation_agent 23 | 24 | 25 | $SCREEN_OCR 26 | 27 | 28 | DOCGEN: [Function/class name]: [1-2 sentence description of purpose] 29 | code: | 30 | //DOCGEN 31 | function(line) { 32 | const currentMemory = utilities.getAgentMemory(agentId); 33 | currentMemory.then(memory => { 34 | utilities.updateAgentMemory(agentId, memory + "\n" + line); 35 | }); 36 | } 37 | memory: | 38 | 39 | **documentCodeInBackground** 40 | documentCodeInBackground - This function monitors network activity, specifically tracking an IP address and data usage via a Synology router, gathering information from endpoints like "app.observer-ai.com" and compiling statistics over 300 seconds using the deepseek-r1:8b AI model. -------------------------------------------------------------------------------- /app/public/agent-focus_tracker.yaml: -------------------------------------------------------------------------------- 1 | id: focus_tracker 2 | name: Focus Tracking Assistant 3 | description: This assistant compiles a list of time used in certain applications 4 | status: stopped 5 | model_name: deepseek-r1:8b 6 | loop_interval_seconds: 60 7 | system_prompt: | 8 | You are a Focus Tracker agent that monitors application switching frequency and duration. 9 | Your job is to observe the screen, identify which applications are being used, and register use time of applications. 10 | Respond only with the following format: 11 | FOCUSSTAT: [Brief description of new activity] 12 | 13 | This is the user's previous activities, if the last activity is similar to the current activity. Don't respond with the command. 14 | 15 | $MEMORY@focus_tracker 16 | 17 | 18 | $SCREEN_OCR 19 | 20 | 21 | FOCUSSTAT: [Concise observation about new activity. 22 | Remember only use the command when the activity is new. 23 | code: | 24 | //FOCUSSTAT 25 | function(line) { 26 | const currentMemory = utilities.getAgentMemory(agentId); 27 | const time = utilities.getCurrentTime(); 28 | currentMemory.then(memory => { 29 | utilities.updateAgentMemory(agentId, memory + "\n" + time + line); 30 | }); 31 | } 32 | memory: "" 33 | -------------------------------------------------------------------------------- /app/public/agent-memory_summarization.yaml: -------------------------------------------------------------------------------- 1 | id: memory_summarization 2 | name: Memory Summarization Agent 3 | description: This agent reads another agent's memories and summarizes them so that context window length doesn't become a problem. 4 | status: stopped 5 | model_name: deepseek-r1:8b 6 | loop_interval_seconds: 300 7 | system_prompt: | 8 | Hi deepseek please summarize the text below: 9 | 10 | $MEMORY@activity_tracking_agent 11 | 12 | respond with 13 | SUMMARY: summary of text you saw 14 | code: | 15 | //SUMMARY 16 | function(line) { 17 | utilities.updateAgentMemory(agentId, line); 18 | } 19 | memory: | 20 | The user is actively developing a React application using TypeScript and Vite, engaging in remote work by screen sharing, setting up build commands with npm, updating GitHub repositories, and configuring network settings. They are interacting with AI tracking agents for task management, communicating via Telegram, contributing to open-source projects, and monitoring system logs while working on their web-based application. -------------------------------------------------------------------------------- /app/public/eye-logo-black.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src-tauri/.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | /gen/schemas 5 | -------------------------------------------------------------------------------- /app/src-tauri/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "app" 3 | version = "0.1.0" 4 | description = "A Tauri App" 5 | authors = ["you"] 6 | license = "" 7 | repository = "" 8 | edition = "2021" 9 | rust-version = "1.77.2" 10 | 11 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 12 | 13 | [lib] 14 | name = "app_lib" 15 | crate-type = ["staticlib", "cdylib", "rlib"] 16 | 17 | [build-dependencies] 18 | tauri-build = { version = "2.0.5", features = [] } 19 | 20 | [dependencies] 21 | serde_json = "1.0" 22 | serde = { version = "1.0", features = ["derive"] } 23 | log = "0.4" 24 | tauri = { version = "2.3.0", features = [] } 25 | tauri-plugin-log = "2.0.0-rc" 26 | 27 | screenshots = "0.8.5" 28 | base64 = "0.21.0" 29 | image = "0.24.6" 30 | -------------------------------------------------------------------------------- /app/src-tauri/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | tauri_build::build() 3 | } 4 | -------------------------------------------------------------------------------- /app/src-tauri/capabilities/default.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "../gen/schemas/desktop-schema.json", 3 | "identifier": "default", 4 | "description": "enables the default permissions", 5 | "windows": [ 6 | "main" 7 | ], 8 | "permissions": [ 9 | "core:default" 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /app/src-tauri/icons/128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Roy3838/Observer/8e724b0df38b73bbfdf25986a9ff19b9fcd5dc0c/app/src-tauri/icons/128x128.png -------------------------------------------------------------------------------- /app/src-tauri/icons/128x128@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Roy3838/Observer/8e724b0df38b73bbfdf25986a9ff19b9fcd5dc0c/app/src-tauri/icons/128x128@2x.png -------------------------------------------------------------------------------- /app/src-tauri/icons/32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Roy3838/Observer/8e724b0df38b73bbfdf25986a9ff19b9fcd5dc0c/app/src-tauri/icons/32x32.png -------------------------------------------------------------------------------- /app/src-tauri/icons/Square107x107Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Roy3838/Observer/8e724b0df38b73bbfdf25986a9ff19b9fcd5dc0c/app/src-tauri/icons/Square107x107Logo.png -------------------------------------------------------------------------------- /app/src-tauri/icons/Square142x142Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Roy3838/Observer/8e724b0df38b73bbfdf25986a9ff19b9fcd5dc0c/app/src-tauri/icons/Square142x142Logo.png -------------------------------------------------------------------------------- /app/src-tauri/icons/Square150x150Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Roy3838/Observer/8e724b0df38b73bbfdf25986a9ff19b9fcd5dc0c/app/src-tauri/icons/Square150x150Logo.png -------------------------------------------------------------------------------- /app/src-tauri/icons/Square284x284Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Roy3838/Observer/8e724b0df38b73bbfdf25986a9ff19b9fcd5dc0c/app/src-tauri/icons/Square284x284Logo.png -------------------------------------------------------------------------------- /app/src-tauri/icons/Square30x30Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Roy3838/Observer/8e724b0df38b73bbfdf25986a9ff19b9fcd5dc0c/app/src-tauri/icons/Square30x30Logo.png -------------------------------------------------------------------------------- /app/src-tauri/icons/Square310x310Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Roy3838/Observer/8e724b0df38b73bbfdf25986a9ff19b9fcd5dc0c/app/src-tauri/icons/Square310x310Logo.png -------------------------------------------------------------------------------- /app/src-tauri/icons/Square44x44Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Roy3838/Observer/8e724b0df38b73bbfdf25986a9ff19b9fcd5dc0c/app/src-tauri/icons/Square44x44Logo.png -------------------------------------------------------------------------------- /app/src-tauri/icons/Square71x71Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Roy3838/Observer/8e724b0df38b73bbfdf25986a9ff19b9fcd5dc0c/app/src-tauri/icons/Square71x71Logo.png -------------------------------------------------------------------------------- /app/src-tauri/icons/Square89x89Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Roy3838/Observer/8e724b0df38b73bbfdf25986a9ff19b9fcd5dc0c/app/src-tauri/icons/Square89x89Logo.png -------------------------------------------------------------------------------- /app/src-tauri/icons/StoreLogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Roy3838/Observer/8e724b0df38b73bbfdf25986a9ff19b9fcd5dc0c/app/src-tauri/icons/StoreLogo.png -------------------------------------------------------------------------------- /app/src-tauri/icons/icon.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Roy3838/Observer/8e724b0df38b73bbfdf25986a9ff19b9fcd5dc0c/app/src-tauri/icons/icon.icns -------------------------------------------------------------------------------- /app/src-tauri/icons/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Roy3838/Observer/8e724b0df38b73bbfdf25986a9ff19b9fcd5dc0c/app/src-tauri/icons/icon.ico -------------------------------------------------------------------------------- /app/src-tauri/icons/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Roy3838/Observer/8e724b0df38b73bbfdf25986a9ff19b9fcd5dc0c/app/src-tauri/icons/icon.png -------------------------------------------------------------------------------- /app/src-tauri/src/lib.rs: -------------------------------------------------------------------------------- 1 | use screenshots::Screen; 2 | use std::io::Cursor; 3 | use base64::{Engine as _, engine::general_purpose}; 4 | use image::ImageEncoder; 5 | #[cfg_attr(mobile, tauri::mobile_entry_point)] 6 | pub fn run() { 7 | tauri::Builder::default() 8 | .setup(|app| { 9 | if cfg!(debug_assertions) { 10 | app.handle().plugin( 11 | tauri_plugin_log::Builder::default() 12 | .level(log::LevelFilter::Info) 13 | .build(), 14 | )?; 15 | } 16 | Ok(()) 17 | }) 18 | .invoke_handler(tauri::generate_handler![capture_screen]) 19 | .run(tauri::generate_context!()) 20 | .expect("error while running tauri application"); 21 | } 22 | 23 | #[tauri::command] 24 | fn capture_screen() -> Result { 25 | // Get all screens 26 | let screens = Screen::all().map_err(|e| e.to_string())?; 27 | if screens.is_empty() { 28 | return Err("No screens found".into()); 29 | } 30 | 31 | // Capture the first screen 32 | let screen = &screens[0]; 33 | let image = screen.capture().map_err(|e| e.to_string())?; 34 | 35 | // Convert to bytes and then to base64 36 | let mut buffer = Vec::new(); 37 | image.write_to(&mut Cursor::new(&mut buffer), image::ImageFormat::Png) 38 | .map_err(|e| e.to_string())?; 39 | 40 | let base64_img = general_purpose::STANDARD.encode(&buffer); 41 | Ok(base64_img) 42 | } 43 | -------------------------------------------------------------------------------- /app/src-tauri/src/main.rs: -------------------------------------------------------------------------------- 1 | // Prevents additional console window on Windows in release, DO NOT REMOVE!! 2 | #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] 3 | 4 | fn main() { 5 | app_lib::run(); 6 | } 7 | -------------------------------------------------------------------------------- /app/src-tauri/tauri.conf.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "../node_modules/@tauri-apps/cli/config.schema.json", 3 | "productName": "Observer", 4 | "version": "0.1.0", 5 | "identifier": "com.tauri.dev", 6 | "build": { 7 | "frontendDist": "../dist", 8 | "devUrl": "http://localhost:3001", 9 | "beforeDevCommand": "npm run dev", 10 | "beforeBuildCommand": "npm run build" 11 | }, 12 | "app": { 13 | "windows": [ 14 | { 15 | "title": "Observer", 16 | "width": 800, 17 | "height": 600, 18 | "resizable": true, 19 | "fullscreen": false 20 | } 21 | ], 22 | "security": { 23 | "csp": null 24 | } 25 | }, 26 | "bundle": { 27 | "active": true, 28 | "targets": "all", 29 | "icon": [ 30 | "icons/32x32.png", 31 | "icons/128x128.png", 32 | "icons/128x128@2x.png", 33 | "icons/icon.icns", 34 | "icons/icon.ico" 35 | ] 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /app/src/components/AgentImportHandler.tsx: -------------------------------------------------------------------------------- 1 | import { useState } from 'react'; 2 | import { PlusCircle, RotateCw, Sparkles } from 'lucide-react'; 3 | import { CompleteAgent } from '@utils/agent_database'; // Fixed import path 4 | import GenerateAgentModal from './GenerateAgentModal'; 5 | 6 | interface ImportResult { 7 | filename: string; 8 | success: boolean; 9 | agent?: CompleteAgent; 10 | error?: string; 11 | } 12 | 13 | interface AgentImportHandlerProps { 14 | onAddAgent: () => void; 15 | agentCount: number; 16 | activeAgentCount: number; 17 | isRefreshing: boolean; 18 | onRefresh: () => void; 19 | } 20 | 21 | const AgentImportHandler = ({ 22 | onAddAgent, 23 | agentCount, 24 | activeAgentCount, 25 | isRefreshing, 26 | onRefresh 27 | }: AgentImportHandlerProps) => { 28 | const [importStatus] = useState<{ inProgress: boolean; results: ImportResult[] }>({ 29 | inProgress: false, 30 | results: [] 31 | }); 32 | const [isModalOpen, setIsModalOpen] = useState(false); 33 | // Unused variable removed 34 | 35 | // MAKES EDITING AGENTS UNUSABLE 36 | //useEffect(() => { 37 | // const interval = setInterval(() => { 38 | // if (!isRefreshing) { 39 | // onRefresh(); 40 | // } 41 | // }, 1000); // Refresh every second 42 | // return () => clearInterval(interval); // Cleanup on unmount 43 | //}, [isRefreshing, onRefresh]); // Dependencies 44 | 45 | return ( 46 | <> 47 |
48 |
49 | 52 |

Active: {activeAgentCount} / Total: {agentCount}

53 |
54 | 55 |
56 | 65 | 66 | 73 | 74 |
75 |
76 | 77 | {importStatus.results.length > 0 && ( 78 |
79 |

Import Results:

80 |
    81 | {importStatus.results.map((result, index) => ( 82 |
  • 83 | {result.filename}: {result.success ? 'Success' : `Failed - ${result.error}`} 84 |
  • 85 | ))} 86 |
87 |
88 | )} 89 | 90 | setIsModalOpen(false)} /> 91 | 92 | ); 93 | }; 94 | 95 | export default AgentImportHandler; 96 | -------------------------------------------------------------------------------- /app/src/components/AvailableModels.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react'; 2 | import { listModels, Model } from '@utils/ollamaServer'; // Import updated Model interface 3 | import { Cpu, RefreshCw, Eye } from 'lucide-react'; // <-- Import Eye icon 4 | import { Logger } from '@utils/logging'; 5 | import { getOllamaServerAddress } from '@utils/main_loop'; 6 | 7 | // No need to redefine Model interface here if imported correctly 8 | 9 | const AvailableModels: React.FC = () => { 10 | const [models, setModels] = useState([]); 11 | const [loading, setLoading] = useState(true); 12 | const [error, setError] = useState(null); 13 | const [refreshing, setRefreshing] = useState(false); 14 | 15 | const fetchModels = async () => { 16 | setLoading(true); 17 | setError(null); 18 | 19 | try { 20 | Logger.info('MODELS', 'Fetching available models from server'); 21 | const { host, port } = getOllamaServerAddress(); 22 | Logger.info('MODELS', `Using server address: ${host}:${port}`); 23 | 24 | const response = await listModels(host, port); // Uses updated listModels 25 | 26 | if (response.error) { 27 | throw new Error(response.error); 28 | } 29 | 30 | setModels(response.models); 31 | Logger.info('MODELS', `Successfully loaded ${response.models.length} models`); 32 | } catch (err) { 33 | const errorMessage = err instanceof Error ? err.message : String(err); 34 | setError(errorMessage); 35 | Logger.error('MODELS', `Failed to fetch models: ${errorMessage}`); 36 | } finally { 37 | setLoading(false); 38 | setRefreshing(false); 39 | } 40 | }; 41 | 42 | useEffect(() => { 43 | fetchModels(); 44 | }, []); 45 | 46 | const handleRefresh = () => { 47 | setRefreshing(true); 48 | fetchModels(); 49 | }; 50 | 51 | if (loading && !refreshing) { 52 | // ... (loading state remains the same) 53 | return ( 54 |
55 |
56 | 57 |
58 |

Loading available models...

59 |
60 | ); 61 | } 62 | 63 | return ( 64 |
65 |
66 |

Available Models

67 | 79 |
80 | 81 | {error ? ( 82 | // ... (error display remains the same) 83 |
84 |

Error: {error}

85 |

86 | Check that your server is running properly and try again. 87 |

88 |
89 | ) : models.length === 0 ? ( 90 | // ... (no models display remains the same) 91 |
92 |

No models found on the server.

93 |

94 | Ensure that your server is properly configured and has models available. 95 |

96 |
97 | ) : ( 98 |
99 | {models.map((model) => ( 100 |
104 |
105 | 106 |
107 |

{model.name}

108 | {/* Container for parameter size and multimodal icon */} 109 |
110 | {model.parameterSize && model.parameterSize !== "N/A" && ( 111 | 112 | {model.parameterSize} 113 | 114 | )} 115 | {/* Conditionally render the Eye icon if multimodal is true */} 116 | {model.multimodal && ( 117 | 118 | 119 | Vision 120 | 121 | )} 122 |
123 |
124 |
125 |
126 | ))} 127 |
128 | )} 129 | 130 | {/* ... (footer text remains the same) ... */} 131 |
132 |

133 | These models are available on your configured model server. 134 | You can use them in your agents by specifying their name. 135 |

136 |
137 |
138 | ); 139 | }; 140 | 141 | export default AvailableModels; 142 | -------------------------------------------------------------------------------- /app/src/components/EditAgent/Modal.tsx: -------------------------------------------------------------------------------- 1 | // src/components/EditAgent/Modal.tsx 2 | import React, { ReactNode, useEffect } from 'react'; 3 | import ReactDOM from 'react-dom'; 4 | 5 | interface ModalProps { 6 | open: boolean; 7 | onClose: () => void; 8 | children: ReactNode; 9 | className?: string; // width / height / flex etc. 10 | backdropClassName?: string; 11 | } 12 | 13 | let openCount = 0; // track how many modals are mounted for scroll-lock 14 | 15 | const Modal: React.FC = ({ 16 | open, 17 | onClose, 18 | children, 19 | className = '', 20 | backdropClassName = 'bg-black/50' 21 | }) => { 22 | // 1. Early exit 23 | if (!open) return null; 24 | 25 | // 2. Target element for the portal 26 | const portalTarget = 27 | document.getElementById('modal-root') ?? document.body; 28 | 29 | // 3. Close on 30 | useEffect(() => { 31 | const handler = (e: KeyboardEvent) => { 32 | if (e.key === 'Escape') onClose(); 33 | }; 34 | window.addEventListener('keydown', handler); 35 | return () => window.removeEventListener('keydown', handler); 36 | }, [onClose]); 37 | 38 | // 4. Disable body scroll while any modal is open 39 | useEffect(() => { 40 | openCount += 1; 41 | document.body.classList.add('overflow-hidden'); 42 | return () => { 43 | openCount -= 1; 44 | if (openCount === 0) document.body.classList.remove('overflow-hidden'); 45 | }; 46 | }, []); 47 | 48 | // 5. Backdrop click only when the *originating* target *is* the backdrop 49 | const handleBackdropMouseDown = (e: React.MouseEvent) => { 50 | if (e.target === e.currentTarget) onClose(); 51 | }; 52 | 53 | return ReactDOM.createPortal( 54 |
58 |
e.stopPropagation()} 62 | > 63 | {children} 64 |
65 |
, 66 | portalTarget 67 | ); 68 | }; 69 | 70 | export default Modal; 71 | 72 | -------------------------------------------------------------------------------- /app/src/components/ErrorDisplay.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | interface ErrorDisplayProps { 4 | message: string; 5 | } 6 | 7 | const ErrorDisplay: React.FC = ({ message }) => { 8 | return ( 9 |
10 | {message} 11 |
12 | ); 13 | }; 14 | 15 | export default ErrorDisplay; 16 | -------------------------------------------------------------------------------- /app/src/components/GetStarted.tsx: -------------------------------------------------------------------------------- 1 | // src/components/GetStarted.tsx 2 | import React, { useState } from 'react'; 3 | import { Plus, Users, Sparkles, Terminal, Code } from 'lucide-react'; 4 | import GenerateAgent from './GenerateAgent'; 5 | 6 | // Fixed model for GetStarted page 7 | const FIXED_MODEL = 'gemini-2.5-flash-preview-04-17'; 8 | 9 | interface GetStartedProps { 10 | onExploreCommunity: () => void; 11 | onCreateNewAgent: () => void; 12 | } 13 | 14 | const GetStarted: React.FC = ({ 15 | onExploreCommunity, 16 | onCreateNewAgent, 17 | }) => { 18 | const [showAiGenerator, setShowAiGenerator] = useState(false); 19 | const [agentType, setAgentType] = useState<'browser' | 'python'>('browser'); 20 | 21 | 22 | return ( 23 |
24 |
25 |

Welcome to Observer AI

26 | 27 |
28 |
29 | 40 | 51 |
52 |
53 | 54 |
55 | {agentType === 'browser' ? ( 56 | null 57 | ) : ( 58 |

59 | Requires Jupyter server setup 60 |

61 | )} 62 |
63 | 64 | {/* AI Agent Generator Section */} 65 |
66 |
67 | 68 |
69 |

70 | {agentType === 'browser' ? 'Create AI Browser Agent' : 'Create AI System Agent'} 71 |

72 |
73 |
74 | 75 |
76 | {showAiGenerator ? ( 77 | 78 | ) : ( 79 |
80 | setShowAiGenerator(true)} 88 | readOnly 89 | /> 90 | 97 |
98 | )} 99 |
100 |
101 | 102 | {/* Browse Community and Create Custom options */} 103 |
104 |
108 |
109 | 110 |
111 |

Community Agents

112 |
113 | 114 |
118 |
119 | 120 |
121 |

Custom Agent

122 |
123 |
124 |
125 |
126 | ); 127 | }; 128 | 129 | export default GetStarted; 130 | -------------------------------------------------------------------------------- /app/src/components/LocalServerSetupDialog.tsx: -------------------------------------------------------------------------------- 1 | import { useEffect } from 'react'; 2 | import { Terminal, CheckCircle2, XCircle, LoaderCircle, ArrowRight, ArrowLeft } from 'lucide-react'; 3 | import { checkOllamaServer } from '@utils/ollamaServer'; 4 | 5 | interface LocalServerSetupDialogProps { 6 | serverStatus: 'unchecked' | 'online' | 'offline'; 7 | setServerStatus: (status: 'unchecked' | 'online' | 'offline') => void; 8 | onDismiss: () => void; 9 | onBack: () => void; 10 | } 11 | 12 | const LocalServerSetupDialog = ({ 13 | serverStatus, 14 | setServerStatus, 15 | onDismiss, 16 | onBack 17 | }: LocalServerSetupDialogProps) => { 18 | useEffect(() => { 19 | const checkStatus = async () => { 20 | const result = await checkOllamaServer('localhost', '3838'); 21 | setServerStatus(result.status === 'online' ? 'online' : 'offline'); 22 | }; 23 | 24 | const timer = setTimeout(checkStatus, 500); 25 | return () => clearTimeout(timer); 26 | }, [setServerStatus]); 27 | 28 | const StatusIcon = { 29 | online: CheckCircle2, 30 | offline: XCircle, 31 | unchecked: LoaderCircle 32 | }[serverStatus]; 33 | 34 | return ( 35 |
36 |
37 |
38 |
39 | 40 |

Set Up Local Server

41 |
42 | 49 |
50 | 51 |

52 | Follow these steps to set up your own Observer inference server on your local machine. 53 |

54 | 55 |
56 |
57 |
58 |
59 | 1 60 |
61 |

Install Ollama

62 |
63 |

64 | Install Ollama from ollama.com 65 |

66 |
67 | 68 |
69 |
70 |
71 | 2 72 |
73 |

Install Observer-Ollama

74 |
75 |
76 | pip install observer-ollama 77 |
78 |
79 | 80 |
81 |
82 |
83 | 3 84 |
85 |

Run Observer-Ollama

86 |
87 |
88 | observer-ollama 89 |
90 |
91 | 92 |
93 |
94 |
95 | 4 96 |
97 |

Accept Certificates

98 |
99 | 100 |

101 | Click the link in terminal: 102 |

103 |
104 | https://localhost:3838 105 |
106 |
107 | 108 |
109 |
110 |
111 | 5 112 |
113 |

Connect to Your Server

114 |
115 |

116 | Enter your local inference server address in the field above. 117 |

118 |
119 |
120 | 121 |
122 | 126 | 127 | {serverStatus === 'online' ? 'Connected successfully' : 128 | serverStatus === 'offline' ? 'Connection failed' : 129 | 'Checking connection...'} 130 | 131 |
132 | 133 | {serverStatus === 'offline' && ( 134 |
135 |

Unable to connect to the Ollama server. Please verify:

136 |
    137 |
  • Ollama is running on your system
  • 138 |
  • Run "observer-ollama" in terminal
  • 139 |
  • Server address is correct
  • 140 |
141 |
142 | )} 143 | 144 |
145 | 152 | 153 | 160 |
161 |
162 |
163 | ); 164 | }; 165 | 166 | export default LocalServerSetupDialog; 167 | -------------------------------------------------------------------------------- /app/src/components/MemoryManager.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect, useRef } from 'react'; 2 | import { X, Save, Trash2 } from 'lucide-react'; 3 | import { getAgentMemory, updateAgentMemory } from '@utils/agent_database'; 4 | import { Logger } from '@utils/logging'; 5 | 6 | // Create a custom event for memory updates 7 | export const MEMORY_UPDATE_EVENT = 'agent-memory-update'; 8 | 9 | // Dispatch a memory update event 10 | export function dispatchMemoryUpdate(agentId: string) { 11 | const event = new CustomEvent(MEMORY_UPDATE_EVENT, { 12 | detail: { agentId } 13 | }); 14 | window.dispatchEvent(event); 15 | } 16 | 17 | interface MemoryManagerProps { 18 | agentId: string; 19 | agentName: string; 20 | isOpen: boolean; 21 | onClose: () => void; 22 | } 23 | 24 | const MemoryManager: React.FC = ({ 25 | agentId, 26 | agentName, 27 | isOpen, 28 | onClose 29 | }) => { 30 | const [memory, setMemory] = useState(''); 31 | const [savedMemory, setSavedMemory] = useState(''); 32 | const [isSaving, setIsSaving] = useState(false); 33 | const [isClearing, setIsClearing] = useState(false); 34 | const [error, setError] = useState(null); 35 | 36 | // Polling interval for memory updates (in milliseconds) 37 | const pollingInterval = 2000; 38 | const pollingTimer = useRef(null); 39 | 40 | // Start polling for memory updates when the component is open 41 | useEffect(() => { 42 | if (isOpen) { 43 | // Initial load 44 | loadMemory(); 45 | 46 | // Set up polling for updates 47 | pollingTimer.current = window.setInterval(() => { 48 | loadMemory(false); // Silent update (no logging for routine checks) 49 | }, pollingInterval); 50 | 51 | // Clean up interval on unmount or when closing 52 | return () => { 53 | if (pollingTimer.current !== null) { 54 | window.clearInterval(pollingTimer.current); 55 | pollingTimer.current = null; 56 | } 57 | }; 58 | } 59 | }, [isOpen, agentId]); 60 | 61 | // Listen for memory update events 62 | useEffect(() => { 63 | const handleMemoryUpdate = (event: CustomEvent) => { 64 | if (event.detail.agentId === agentId && isOpen) { 65 | loadMemory(false); // Silent update 66 | } 67 | }; 68 | 69 | // Add event listener 70 | window.addEventListener(MEMORY_UPDATE_EVENT, handleMemoryUpdate as EventListener); 71 | 72 | // Clean up 73 | return () => { 74 | window.removeEventListener(MEMORY_UPDATE_EVENT, handleMemoryUpdate as EventListener); 75 | }; 76 | }, [agentId, isOpen]); 77 | 78 | const loadMemory = async (logActivity = true) => { 79 | try { 80 | setError(null); 81 | const agentMemory = await getAgentMemory(agentId); 82 | 83 | // Skip update if memory hasn't changed 84 | if (agentMemory === savedMemory && memory === savedMemory) { 85 | return; 86 | } 87 | 88 | setMemory(agentMemory); 89 | setSavedMemory(agentMemory); 90 | 91 | if (logActivity) { 92 | Logger.debug(agentId, `Memory loaded (${agentMemory.length} characters)`); 93 | } 94 | } catch (err) { 95 | const errorMessage = err instanceof Error ? err.message : 'Unknown error'; 96 | setError(`Failed to load memory: ${errorMessage}`); 97 | Logger.error(agentId, `Failed to load memory: ${errorMessage}`, err); 98 | } 99 | }; 100 | 101 | const handleSave = async () => { 102 | try { 103 | setError(null); 104 | setIsSaving(true); 105 | await updateAgentMemory(agentId, memory); 106 | setSavedMemory(memory); 107 | Logger.debug(agentId, `Memory saved (${memory.length} characters)`); 108 | } catch (err) { 109 | const errorMessage = err instanceof Error ? err.message : 'Unknown error'; 110 | setError(`Failed to save memory: ${errorMessage}`); 111 | Logger.error(agentId, `Failed to save memory: ${errorMessage}`, err); 112 | } finally { 113 | setIsSaving(false); 114 | } 115 | }; 116 | 117 | const handleClear = async () => { 118 | if (window.confirm(`Are you sure you want to clear the memory for agent "${agentName}"?`)) { 119 | try { 120 | setError(null); 121 | setIsClearing(true); 122 | await updateAgentMemory(agentId, ''); 123 | setMemory(''); 124 | setSavedMemory(''); 125 | Logger.info(agentId, 'Memory cleared'); 126 | } catch (err) { 127 | const errorMessage = err instanceof Error ? err.message : 'Unknown error'; 128 | setError(`Failed to clear memory: ${errorMessage}`); 129 | Logger.error(agentId, `Failed to clear memory: ${errorMessage}`, err); 130 | } finally { 131 | setIsClearing(false); 132 | } 133 | } 134 | }; 135 | 136 | const hasChanges = memory !== savedMemory; 137 | 138 | if (!isOpen) { 139 | return null; 140 | } 141 | 142 | return ( 143 |
144 |
145 |
146 |

Memory Manager: {agentName}

147 | 150 |
151 | 152 | {error && ( 153 |
154 | {error} 155 |
156 | )} 157 | 158 |
159 |