diff --git a/packages/client/index.html b/packages/client/index.html
index dacc6e00..adc43e0b 100644
--- a/packages/client/index.html
+++ b/packages/client/index.html
@@ -3,8 +3,12 @@
+
+
+
+
Valet
diff --git a/packages/client/public/icons/apple-touch-icon.png b/packages/client/public/icons/apple-touch-icon.png
new file mode 100644
index 00000000..0983544d
Binary files /dev/null and b/packages/client/public/icons/apple-touch-icon.png differ
diff --git a/packages/client/public/icons/icon-192.png b/packages/client/public/icons/icon-192.png
new file mode 100644
index 00000000..146ad0d3
Binary files /dev/null and b/packages/client/public/icons/icon-192.png differ
diff --git a/packages/client/public/icons/icon-512-maskable.png b/packages/client/public/icons/icon-512-maskable.png
new file mode 100644
index 00000000..a856aab7
Binary files /dev/null and b/packages/client/public/icons/icon-512-maskable.png differ
diff --git a/packages/client/public/icons/icon-512.png b/packages/client/public/icons/icon-512.png
new file mode 100644
index 00000000..b76331a5
Binary files /dev/null and b/packages/client/public/icons/icon-512.png differ
diff --git a/packages/client/public/manifest.json b/packages/client/public/manifest.json
new file mode 100644
index 00000000..22355ba5
--- /dev/null
+++ b/packages/client/public/manifest.json
@@ -0,0 +1,27 @@
+{
+ "name": "Valet",
+ "short_name": "Valet",
+ "description": "AI coding agent platform — background sessions with full dev environments",
+ "start_url": "/",
+ "display": "standalone",
+ "background_color": "#0a0a0a",
+ "theme_color": "#0a0a0a",
+ "icons": [
+ {
+ "src": "/icons/icon-192.png",
+ "sizes": "192x192",
+ "type": "image/png"
+ },
+ {
+ "src": "/icons/icon-512.png",
+ "sizes": "512x512",
+ "type": "image/png"
+ },
+ {
+ "src": "/icons/icon-512-maskable.png",
+ "sizes": "512x512",
+ "type": "image/png",
+ "purpose": "maskable"
+ }
+ ]
+}
diff --git a/packages/client/public/sw.js b/packages/client/public/sw.js
new file mode 100644
index 00000000..247d3132
--- /dev/null
+++ b/packages/client/public/sw.js
@@ -0,0 +1,22 @@
+// TODO: Integrate ACP (Agent Client Protocol) to enable connecting Valet to local
+// coding agents and vice versa. This PWA is the foundation for a native-feeling
+// desktop experience that can bridge cloud-hosted agent sessions with local dev tools.
+
+// Minimal service worker for PWA installability.
+// This does NOT provide offline support or caching — it simply satisfies
+// Chrome's PWA install criteria (a registered service worker with a fetch handler).
+
+self.addEventListener('install', (event) => {
+ // Activate immediately — no waiting for existing clients to close
+ self.skipWaiting();
+});
+
+self.addEventListener('activate', (event) => {
+ // Claim all open clients so the SW is controlling pages right away
+ event.waitUntil(self.clients.claim());
+});
+
+self.addEventListener('fetch', (event) => {
+ // Pass all requests through to the network (no caching)
+ event.respondWith(fetch(event.request));
+});
diff --git a/packages/client/src/main.tsx b/packages/client/src/main.tsx
index 56c26ee9..99e72b44 100644
--- a/packages/client/src/main.tsx
+++ b/packages/client/src/main.tsx
@@ -11,3 +11,12 @@ createRoot(rootElement).render(
);
+
+// Register service worker for PWA install support
+if ('serviceWorker' in navigator) {
+ window.addEventListener('load', () => {
+ navigator.serviceWorker.register('/sw.js').catch(() => {
+ // Service worker registration failed — non-critical for app functionality
+ });
+ });
+}