{"_id":"@ashutosh0x/jarvis","_rev":"13-ebe1d24a4b02aefe69ed6c9617470ee1","name":"@ashutosh0x/jarvis","dist-tags":{"latest":"0.11.0"},"versions":{"0.1.0":{"name":"@ashutosh0x/jarvis","version":"0.1.0","keywords":["ai","assistant","electron","desktop","voice-assistant","web-search","search-engine","rrf","bm25","gemini","ollama","local-first","tts","ocr","rag","offline-first","jarvis"],"author":{"name":"Ashutosh Kumar Singh","email":"ashutoshkumarsingh0x@gmail.com"},"license":"MIT","_id":"@ashutosh0x/jarvis@0.1.0","maintainers":[{"name":"ashutosh0x","email":"ashutoshkumarsingh0x@gmail.com"}],"homepage":"https://github.com/Ashutosh0x/jarvis#readme","bugs":{"url":"https://github.com/Ashutosh0x/jarvis/issues"},"bin":{"jarvis":"bin/jarvis.mjs"},"dist":{"shasum":"56b03ebaf407b0eea6ff05b01215d724c17e67b6","tarball":"https://registry.npmjs.org/@ashutosh0x/jarvis/-/jarvis-0.1.0.tgz","fileCount":163,"integrity":"sha512-BWNj4UYe9STJFOxhiDiPLWnLO49UnAlBnVT99s22hG0jHXW2lePFZZcsYDp8YBb5sGRtgkqQlHWyAMtEPbC8lw==","signatures":[{"sig":"MEUCIQD+cjBSegQ4gNYSsnzjfIBtpWJ3ldejd/gXcYhf2OEvGgIgAwuG/PYyOQA4FVK8WjCj7drwINOZMYGPDaC1Mf9VSOs=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":6938207},"main":"electron.js","type":"commonjs","engines":{"npm":">=10.0.0","node":">=22.0.0"},"exports":{".":{"import":"./lib/index.mjs","require":"./webSearch.js"},"./search":"./webSearch.js","./metrics":"./metricStore.js","./package.json":"./package.json"},"gitHead":"c9b150cb68b455ec2a6374d36aff91226b5d64af","scripts":{"dev":"vite","dist":"npm run icon && npm run build && electron-builder --publish never","eval":"node eval/retrieval-eval.mjs && node eval/memory-eval.mjs && node eval/answer-eval.mjs","icon":"node scripts/make-icon.mjs","test":"node scripts/run-tests.mjs","build":"vite build","icons":"npm run icon && npm run favicons","smoke":"node scripts/smoke-test.mjs","jarvis":"node bin/jarvis.mjs","prepack":"npm run build","release":"npm run build && electron-builder --publish always","dist:all":"npm run build && electron-builder -mwl --publish never","dist:mac":"npm run icon && npm run build && electron-builder --mac --publish never","dist:win":"npm run icon && npm run build && electron-builder --win --publish never","electron":"node node_modules/electron/cli.js .","favicons":"node scripts/make-favicons.mjs","checksums":"node scripts/checksums.mjs","dist:linux":"npm run icon && npm run build && electron-builder --linux --publish never","electron:dev":"set NODE_ENV=development&& node node_modules/electron/cli.js .","electron:build":"vite build && electron-builder","checksums:verify":"node scripts/checksums.mjs --verify"},"_npmUser":{"name":"ashutosh0x","email":"ashutoshkumarsingh0x@gmail.com"},"repository":{"url":"git+https://github.com/Ashutosh0x/jarvis.git","type":"git"},"_npmVersion":"11.5.2","description":"A local-first desktop AI assistant: federated live web search with no API key, on-device vision and OCR, neural TTS, and a self-healing supervisor. Ships as an Electron app and an importable search library.","directories":{},"_nodeVersion":"22.18.0","dependencies":{"ws":"^8.21.1","axios":"^1.6.0","three":"^0.158.0","qrcode":"^1.5.4","dat.gui":"^0.7.9","chokidar":"^5.0.0","electron":"^39.2.4","pdf-to-img":"^6.2.0","@google/genai":"^1.34.0","bonjour-service":"^1.4.3","electron-updater":"^6.6.2"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vite":"^7.2.4","parcel":"^2.7.0","electron-builder":"^26.0.12","@electron/notarize":"^2.5.0","@parcel/config-default":"^2.4.0","parcel-reporter-static-files-copy":"^1.3.4"},"_npmOperationalInternal":{"tmp":"tmp/jarvis_0.1.0_1785459044012_0.3178144272952894","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"name":"@ashutosh0x/jarvis","version":"0.1.1","keywords":["ai","assistant","electron","desktop","voice-assistant","web-search","search-engine","rrf","bm25","gemini","ollama","local-first","tts","ocr","rag","offline-first","jarvis"],"author":{"name":"Ashutosh Kumar Singh","email":"ashutoshkumarsingh0x@gmail.com"},"license":"MIT","_id":"@ashutosh0x/jarvis@0.1.1","maintainers":[{"name":"ashutosh0x","email":"ashutoshkumarsingh0x@gmail.com"}],"homepage":"https://github.com/Ashutosh0x/jarvis#readme","bugs":{"url":"https://github.com/Ashutosh0x/jarvis/issues"},"bin":{"jarvis":"bin/jarvis.mjs"},"dist":{"shasum":"91162f8f4486e18284fe74fbc781fb45de435978","tarball":"https://registry.npmjs.org/@ashutosh0x/jarvis/-/jarvis-0.1.1.tgz","fileCount":163,"integrity":"sha512-GTjAZchr1CLsJ1RvzVkAvDvC9mWttRydOgFceqIbBVaeCxoQq+MFuz94yKYFF7nO8+JxgoyxKo4scqBF2HH8dQ==","signatures":[{"sig":"MEYCIQD04wUFucJ9QVyDJyoIcxWI1TytPblhbbAA2rdMF85VvAIhAJfrn3QwkQlE2kE+e2wV+dk1UkIyIIgGtu3FwlrE2oor","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":6922779},"main":"electron.js","type":"commonjs","engines":{"npm":">=10.0.0","node":">=22.0.0"},"exports":{".":{"import":"./lib/index.mjs","require":"./webSearch.js"},"./search":"./webSearch.js","./metrics":"./metricStore.js","./package.json":"./package.json"},"gitHead":"d7078d08bffa7ecb27ee47727520a77f21634657","scripts":{"dev":"vite","dist":"npm run icon && npm run build && electron-builder --publish never","eval":"node eval/retrieval-eval.mjs && node eval/memory-eval.mjs && node eval/answer-eval.mjs","icon":"node scripts/make-icon.mjs","test":"node scripts/run-tests.mjs","build":"vite build","icons":"npm run icon && npm run favicons","smoke":"node scripts/smoke-test.mjs","jarvis":"node bin/jarvis.mjs","prepack":"npm run build","release":"npm run build && electron-builder --publish always","dist:all":"npm run build && electron-builder -mwl --publish never","dist:mac":"npm run icon && npm run build && electron-builder --mac --publish never","dist:win":"npm run icon && npm run build && electron-builder --win --publish never","electron":"node node_modules/electron/cli.js .","favicons":"node scripts/make-favicons.mjs","checksums":"node scripts/checksums.mjs","dist:linux":"npm run icon && npm run build && electron-builder --linux --publish never","electron:dev":"set NODE_ENV=development&& node node_modules/electron/cli.js .","electron:build":"vite build && electron-builder","checksums:verify":"node scripts/checksums.mjs --verify"},"_npmUser":{"name":"ashutosh0x","email":"ashutoshkumarsingh0x@gmail.com"},"repository":{"url":"git+https://github.com/Ashutosh0x/jarvis.git","type":"git"},"_npmVersion":"11.5.2","description":"A local-first desktop AI assistant: federated live web search with no API key, on-device vision and OCR, neural TTS, and a self-healing supervisor. Ships as an Electron app and an importable search library.","directories":{},"_nodeVersion":"22.18.0","dependencies":{"ws":"^8.21.1","axios":"^1.6.0","three":"^0.158.0","qrcode":"^1.5.4","dat.gui":"^0.7.9","chokidar":"^5.0.0","electron":"^39.2.4","pdf-to-img":"^6.2.0","@google/genai":"^1.34.0","bonjour-service":"^1.4.3","electron-updater":"^6.6.2"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vite":"^7.2.4","parcel":"^2.7.0","electron-builder":"^26.0.12","@electron/notarize":"^2.5.0","@parcel/config-default":"^2.4.0","parcel-reporter-static-files-copy":"^1.3.4"},"_npmOperationalInternal":{"tmp":"tmp/jarvis_0.1.1_1785459616678_0.6695557594464547","host":"s3://npm-registry-packages-npm-production"}},"0.1.2":{"name":"@ashutosh0x/jarvis","version":"0.1.2","keywords":["ai","assistant","electron","desktop","voice-assistant","web-search","search-engine","rrf","bm25","gemini","ollama","local-first","tts","ocr","rag","offline-first","jarvis"],"author":{"name":"Ashutosh Kumar Singh","email":"ashutoshkumarsingh0x@gmail.com"},"license":"MIT","_id":"@ashutosh0x/jarvis@0.1.2","maintainers":[{"name":"ashutosh0x","email":"ashutoshkumarsingh0x@gmail.com"}],"homepage":"https://github.com/Ashutosh0x/jarvis#readme","bugs":{"url":"https://github.com/Ashutosh0x/jarvis/issues"},"bin":{"jarvis":"bin/jarvis.mjs"},"dist":{"shasum":"72afa86a0df3c943e8211e473e25795d3ce8a349","tarball":"https://registry.npmjs.org/@ashutosh0x/jarvis/-/jarvis-0.1.2.tgz","fileCount":163,"integrity":"sha512-CGJbdzwEdWZeAyw7dVVTSm2JlVjUetsi93O9DX0sa7R9XKXSdAwFdtjweTXtLvlD04biSrwmUFofrp3wa251hA==","signatures":[{"sig":"MEQCIFbhh/uEXnPoaCn+uE2sM2wdNeMpTGe8tojNo0YKWZEMAiAFMtA5rC92ohUykuORNR3WA7vRWeY7vfhOMhBI82axuw==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":6922818},"main":"electron.js","engines":{"npm":">=10.0.0","node":">=22.0.0"},"exports":{".":{"import":"./lib/index.mjs","require":"./webSearch.js"},"./search":"./webSearch.js","./metrics":"./metricStore.js","./package.json":"./package.json"},"gitHead":"e5c86c8e70fc2051fbdd9c2f563babbde4a31ee1","scripts":{"dev":"vite","dist":"npm run icon && npm run build && electron-builder --publish never","eval":"node eval/retrieval-eval.mjs && node eval/memory-eval.mjs && node eval/answer-eval.mjs","icon":"node scripts/make-icon.mjs","test":"node scripts/run-tests.mjs","build":"vite build","icons":"npm run icon && npm run favicons","smoke":"node scripts/smoke-test.mjs","jarvis":"node bin/jarvis.mjs","prepack":"npm run build","release":"npm run build && electron-builder --publish always","dist:all":"npm run build && electron-builder -mwl --publish never","dist:mac":"npm run icon && npm run build && electron-builder --mac --publish never","dist:win":"npm run icon && npm run build && electron-builder --win --publish never","electron":"node node_modules/electron/cli.js .","favicons":"node scripts/make-favicons.mjs","checksums":"node scripts/checksums.mjs","dist:linux":"npm run icon && npm run build && electron-builder --linux --publish never","electron:dev":"set NODE_ENV=development&& node node_modules/electron/cli.js .","electron:build":"vite build && electron-builder","checksums:verify":"node scripts/checksums.mjs --verify"},"_npmUser":{"name":"ashutosh0x","email":"ashutoshkumarsingh0x@gmail.com"},"repository":{"url":"git+https://github.com/Ashutosh0x/jarvis.git","type":"git"},"_npmVersion":"11.5.2","description":"A local-first desktop AI assistant: federated live web search with no API key, on-device vision and OCR, neural TTS, and a self-healing supervisor. Ships as an Electron app and an importable search library.","directories":{},"_nodeVersion":"22.18.0","dependencies":{"ws":"^8.21.1","axios":"^1.6.0","three":"^0.158.0","qrcode":"^1.5.4","dat.gui":"^0.7.9","chokidar":"^5.0.0","pdf-to-img":"^6.2.0","@google/genai":"^1.34.0","bonjour-service":"^1.4.3","electron-updater":"^6.6.2"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vite":"^7.2.4","parcel":"^2.7.0","electron":"^39.2.4","electron-builder":"^26.0.12","@electron/notarize":"^2.5.0","@parcel/config-default":"^2.4.0","parcel-reporter-static-files-copy":"^1.3.4"},"optionalDependencies":{"electron":"^39.2.4"},"_npmOperationalInternal":{"tmp":"tmp/jarvis_0.1.2_1785464141790_0.7144480552011634","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@ashutosh0x/jarvis","version":"0.2.0","keywords":["ai","assistant","electron","desktop","voice-assistant","web-search","search-engine","rrf","bm25","gemini","ollama","local-first","tts","ocr","rag","offline-first","jarvis"],"author":{"name":"Ashutosh Kumar Singh","email":"ashutoshkumarsingh0x@gmail.com"},"license":"MIT","_id":"@ashutosh0x/jarvis@0.2.0","maintainers":[{"name":"ashutosh0x","email":"ashutoshkumarsingh0x@gmail.com"}],"homepage":"https://github.com/Ashutosh0x/jarvis#readme","bugs":{"url":"https://github.com/Ashutosh0x/jarvis/issues"},"bin":{"jarvis":"bin/jarvis.mjs"},"dist":{"shasum":"7bedaf875a78dcf07f5f6d57f6b44fa7f09ec72c","tarball":"https://registry.npmjs.org/@ashutosh0x/jarvis/-/jarvis-0.2.0.tgz","fileCount":174,"integrity":"sha512-2Xp6O5xVtb1Q+m3grAu2JuPmUToA5b7CKXxn8KiP7BJiT4LJpCa2vFxO8+ibA3lQB9XGvgIrGdu7OLUkrfSUlA==","signatures":[{"sig":"MEUCIQD9mvXXydWo1ilGdJG3TO41oQzT6WZbmfgdFuSD+iWqlwIgWTo0TgJAcLjsfinvXSAkiS6J1gwkNzxkcCvN8beAkiA=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":7097180},"main":"electron.js","engines":{"npm":">=10.0.0","node":">=22.0.0"},"exports":{".":{"import":"./lib/index.mjs","require":"./webSearch.js"},"./search":"./webSearch.js","./metrics":"./metricStore.js","./package.json":"./package.json"},"gitHead":"d9018bdf21735a82677ec7e891d1a25a7401bf27","scripts":{"dev":"vite","dist":"npm run icon && npm run build && electron-builder --publish never","eval":"node eval/retrieval-eval.mjs && node eval/memory-eval.mjs && node eval/answer-eval.mjs","icon":"node scripts/make-icon.mjs","test":"node scripts/run-tests.mjs","build":"vite build","icons":"npm run icon && npm run favicons","smoke":"node scripts/smoke-test.mjs","jarvis":"node bin/jarvis.mjs","prepack":"npm run build","release":"npm run build && electron-builder --publish always","dist:all":"npm run build && electron-builder -mwl --publish never","dist:mac":"npm run icon && npm run build && electron-builder --mac --publish never","dist:win":"npm run icon && npm run build && electron-builder --win --publish never","electron":"node node_modules/electron/cli.js .","favicons":"node scripts/make-favicons.mjs","checksums":"node scripts/checksums.mjs","dist:linux":"npm run icon && npm run build && electron-builder --linux --publish never","electron:dev":"set NODE_ENV=development&& node node_modules/electron/cli.js .","electron:build":"vite build && electron-builder","checksums:verify":"node scripts/checksums.mjs --verify"},"_npmUser":{"name":"ashutosh0x","email":"ashutoshkumarsingh0x@gmail.com"},"repository":{"url":"git+https://github.com/Ashutosh0x/jarvis.git","type":"git"},"_npmVersion":"11.5.2","description":"A local-first desktop AI assistant: federated live web search with no API key, on-device vision and OCR, neural TTS, and a self-healing supervisor. Ships as an Electron app and an importable search library.","directories":{},"_nodeVersion":"22.18.0","dependencies":{"ws":"^8.21.1","axios":"^1.6.0","three":"^0.158.0","qrcode":"^1.5.4","dat.gui":"^0.7.9","chokidar":"^5.0.0","pdf-to-img":"^6.2.0","@google/genai":"^1.34.0","bonjour-service":"^1.4.3","electron-updater":"^6.6.2"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vite":"^7.2.4","parcel":"^2.7.0","electron":"^39.2.4","electron-builder":"^26.0.12","@electron/notarize":"^2.5.0","@parcel/config-default":"^2.4.0","parcel-reporter-static-files-copy":"^1.3.4"},"optionalDependencies":{"electron":"^39.2.4"},"_npmOperationalInternal":{"tmp":"tmp/jarvis_0.2.0_1785469484343_0.99949193711323","host":"s3://npm-registry-packages-npm-production"}},"0.3.0":{"name":"@ashutosh0x/jarvis","version":"0.3.0","keywords":["ai","assistant","electron","desktop","voice-assistant","web-search","search-engine","rrf","bm25","gemini","ollama","local-first","tts","ocr","rag","offline-first","jarvis"],"author":{"name":"Ashutosh Kumar Singh","email":"ashutoshkumarsingh0x@gmail.com"},"license":"MIT","_id":"@ashutosh0x/jarvis@0.3.0","maintainers":[{"name":"ashutosh0x","email":"ashutoshkumarsingh0x@gmail.com"}],"homepage":"https://github.com/Ashutosh0x/jarvis#readme","bugs":{"url":"https://github.com/Ashutosh0x/jarvis/issues"},"bin":{"jarvis":"bin/jarvis.mjs"},"dist":{"shasum":"1535fbfde452a834bcf740e8b39f67aa3b873997","tarball":"https://registry.npmjs.org/@ashutosh0x/jarvis/-/jarvis-0.3.0.tgz","fileCount":181,"integrity":"sha512-cMjbj1BiKnuLej+JKsCR9A5yheY/9nMEvEilp2tePmG4QIbop6AWVP1dmtXlt8WsaRxPqnpx8fLLE7fB0RhOfw==","signatures":[{"sig":"MEUCIQDibzULR5DZuiGw8wzEOlKKsavxsVfskMk98aBvY5fWsgIgVGwJA1rU3al/rnwTMsSjLE6qv+KZGdu5IJrt1eDDqMw=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":7212340},"main":"electron.js","engines":{"npm":">=10.0.0","node":">=22.0.0"},"exports":{".":{"import":"./lib/index.mjs","require":"./webSearch.js"},"./search":"./webSearch.js","./metrics":"./metricStore.js","./package.json":"./package.json"},"gitHead":"8c605ad75bb2f459b1016ea2f46bd219e0a5da4f","scripts":{"dev":"vite","dist":"npm run icon && npm run build && electron-builder --publish never","eval":"node eval/retrieval-eval.mjs && node eval/memory-eval.mjs && node eval/answer-eval.mjs","icon":"node scripts/make-icon.mjs","test":"node scripts/run-tests.mjs","build":"vite build","icons":"npm run icon && npm run favicons","smoke":"node scripts/smoke-test.mjs","jarvis":"node bin/jarvis.mjs","prepack":"npm run build","release":"npm run build && electron-builder --publish always","dist:all":"npm run build && electron-builder -mwl --publish never","dist:mac":"npm run icon && npm run build && electron-builder --mac --publish never","dist:win":"npm run icon && npm run build && electron-builder --win --publish never","electron":"node node_modules/electron/cli.js .","favicons":"node scripts/make-favicons.mjs","checksums":"node scripts/checksums.mjs","dist:linux":"npm run icon && npm run build && electron-builder --linux --publish never","electron:dev":"set NODE_ENV=development&& node node_modules/electron/cli.js .","electron:build":"vite build && electron-builder","checksums:verify":"node scripts/checksums.mjs --verify"},"_npmUser":{"name":"ashutosh0x","email":"ashutoshkumarsingh0x@gmail.com"},"repository":{"url":"git+https://github.com/Ashutosh0x/jarvis.git","type":"git"},"_npmVersion":"11.5.2","description":"A local-first desktop AI assistant: federated live web search with no API key, on-device vision and OCR, neural TTS, and a self-healing supervisor. Ships as an Electron app and an importable search library.","directories":{},"_nodeVersion":"22.18.0","dependencies":{"ws":"^8.21.1","axios":"^1.6.0","three":"^0.158.0","qrcode":"^1.5.4","dat.gui":"^0.7.9","chokidar":"^5.0.0","pdf-to-img":"^6.2.0","@google/genai":"^1.34.0","bonjour-service":"^1.4.3","electron-updater":"^6.6.2"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vite":"^7.2.4","parcel":"^2.7.0","electron":"^39.2.4","electron-builder":"^26.0.12","@electron/notarize":"^2.5.0","@parcel/config-default":"^2.4.0","parcel-reporter-static-files-copy":"^1.3.4"},"optionalDependencies":{"electron":"^39.2.4"},"_npmOperationalInternal":{"tmp":"tmp/jarvis_0.3.0_1785474351424_0.08646927359801837","host":"s3://npm-registry-packages-npm-production"}},"0.4.0":{"name":"@ashutosh0x/jarvis","version":"0.4.0","keywords":["ai","assistant","electron","desktop","voice-assistant","web-search","search-engine","rrf","bm25","gemini","ollama","local-first","tts","ocr","rag","offline-first","screen-mirroring","scrcpy","android","adb","jarvis"],"author":{"name":"Ashutosh Kumar Singh","email":"ashutoshkumarsingh0x@gmail.com"},"license":"MIT","_id":"@ashutosh0x/jarvis@0.4.0","maintainers":[{"name":"ashutosh0x","email":"ashutoshkumarsingh0x@gmail.com"}],"homepage":"https://github.com/Ashutosh0x/jarvis#readme","bugs":{"url":"https://github.com/Ashutosh0x/jarvis/issues"},"bin":{"jarvis":"bin/jarvis.mjs"},"dist":{"shasum":"bc97010a068f6c416351aa9b66c443a1f2805c23","tarball":"https://registry.npmjs.org/@ashutosh0x/jarvis/-/jarvis-0.4.0.tgz","fileCount":188,"integrity":"sha512-ybEpSp55ceLFmUVXauQ7PUedoq/mg0K6BQ30IIjyxmQq2QGgr/4mKP1uDDQNcmqdgPLnwTa1a55qk/OlHaKB/A==","signatures":[{"sig":"MEYCIQCxunGZ2/d0zX0whAP0S2uNO/yRl7xUGtK9n0B0QW3ZUQIhAPlYidNy2jVqpbM/SJ2KruGO5clK4CSDR92Y6olKbppx","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":7744947},"main":"electron.js","engines":{"npm":">=10.0.0","node":">=22.0.0"},"exports":{".":{"import":"./lib/index.mjs","require":"./webSearch.js"},"./search":"./webSearch.js","./metrics":"./metricStore.js","./package.json":"./package.json"},"gitHead":"4fecdb97dfd267c67a0d9330fe5ce4cf60685b82","scripts":{"dev":"vite","dist":"npm run icon && npm run build && electron-builder --publish never","eval":"node eval/retrieval-eval.mjs && node eval/memory-eval.mjs && node eval/answer-eval.mjs","icon":"node scripts/make-icon.mjs","test":"node scripts/run-tests.mjs","build":"vite build","icons":"npm run icon && npm run favicons","smoke":"node scripts/smoke-test.mjs","jarvis":"node bin/jarvis.mjs","prepack":"npm run build","release":"npm run build && electron-builder --publish always","dist:all":"npm run build && electron-builder -mwl --publish never","dist:mac":"npm run icon && npm run build && electron-builder --mac --publish never","dist:win":"npm run icon && npm run build && electron-builder --win --publish never","electron":"node node_modules/electron/cli.js .","favicons":"node scripts/make-favicons.mjs","checksums":"node scripts/checksums.mjs","dist:linux":"npm run icon && npm run build && electron-builder --linux --publish never","electron:dev":"set NODE_ENV=development&& node node_modules/electron/cli.js .","electron:build":"vite build && electron-builder","checksums:verify":"node scripts/checksums.mjs --verify"},"_npmUser":{"name":"ashutosh0x","email":"ashutoshkumarsingh0x@gmail.com"},"repository":{"url":"git+https://github.com/Ashutosh0x/jarvis.git","type":"git"},"_npmVersion":"11.5.2","description":"A local-first desktop AI assistant: federated live web search with no API key, on-device vision and OCR, neural TTS, live Android screen mirroring with touch control, and a self-healing supervisor. Ships as an Electron app and an importable search library","directories":{},"_nodeVersion":"22.18.0","dependencies":{"ws":"^8.21.1","axios":"^1.6.0","three":"^0.158.0","qrcode":"^1.5.4","dat.gui":"^0.7.9","chokidar":"^5.0.0","pdf-to-img":"^6.2.0","@google/genai":"^1.34.0","@yume-chan/adb":"^2.6.2","bonjour-service":"^1.4.3","electron-updater":"^6.6.2","@yume-chan/scrcpy":"^2.3.0","@yume-chan/struct":"^2.3.2","@yume-chan/adb-scrcpy":"^2.3.2","@yume-chan/pcm-player":"^1.0.0","@yume-chan/stream-extra":"^2.6.1","@yume-chan/adb-server-node-tcp":"^2.5.2","@yume-chan/scrcpy-decoder-webcodecs":"^2.5.3"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vite":"^7.2.4","parcel":"^2.7.0","electron":"^39.2.4","electron-builder":"^26.0.12","@electron/notarize":"^2.5.0","@parcel/config-default":"^2.4.0","parcel-reporter-static-files-copy":"^1.3.4"},"optionalDependencies":{"electron":"^39.2.4"},"_npmOperationalInternal":{"tmp":"tmp/jarvis_0.4.0_1785673149462_0.12555789160838748","host":"s3://npm-registry-packages-npm-production"}},"0.5.0":{"name":"@ashutosh0x/jarvis","version":"0.5.0","keywords":["ai","assistant","electron","desktop","voice-assistant","web-search","search-engine","rrf","bm25","gemini","ollama","local-first","tts","ocr","rag","offline-first","screen-mirroring","scrcpy","android","adb","haptics","haptic-feedback","jarvis"],"author":{"name":"Ashutosh Kumar Singh","email":"ashutoshkumarsingh0x@gmail.com"},"license":"MIT","_id":"@ashutosh0x/jarvis@0.5.0","maintainers":[{"name":"ashutosh0x","email":"ashutoshkumarsingh0x@gmail.com"}],"homepage":"https://github.com/Ashutosh0x/jarvis#readme","bugs":{"url":"https://github.com/Ashutosh0x/jarvis/issues"},"bin":{"jarvis":"bin/jarvis.mjs"},"dist":{"shasum":"58d0441e2dab2c900b236da115b493213edbb465","tarball":"https://registry.npmjs.org/@ashutosh0x/jarvis/-/jarvis-0.5.0.tgz","fileCount":192,"integrity":"sha512-AKbC7QCj+PredvHRzRZqYieEQi8T7nuUDhmhF49CNAqHHUpqNvr6urjopbPhwqCUwQ8W12pdLEJ55iJS7qg1aQ==","signatures":[{"sig":"MEYCIQCo9HtfG46YjIlvzlfUJn17GOPXw1p7mmR4rajV+oV7UgIhAO8q91TYxaKsU9YaDHaGNup36JTt8nMGsCkj/oF9tKh2","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":7811661},"main":"electron.js","engines":{"npm":">=10.0.0","node":">=22.0.0"},"exports":{".":{"import":"./lib/index.mjs","require":"./webSearch.js"},"./search":"./webSearch.js","./metrics":"./metricStore.js","./package.json":"./package.json"},"gitHead":"682480c97885c2dd0918ccd39ffeae4b6b5680ca","scripts":{"dev":"vite","dist":"npm run icon && npm run build && electron-builder --publish never","eval":"node eval/retrieval-eval.mjs && node eval/memory-eval.mjs && node eval/answer-eval.mjs","icon":"node scripts/make-icon.mjs","test":"node scripts/run-tests.mjs","build":"vite build","icons":"npm run icon && npm run favicons","smoke":"node scripts/smoke-test.mjs","jarvis":"node bin/jarvis.mjs","prepack":"npm run build","release":"npm run build && electron-builder --publish always","dist:all":"npm run build && electron-builder -mwl --publish never","dist:mac":"npm run icon && npm run build && electron-builder --mac --publish never","dist:win":"npm run icon && npm run build && electron-builder --win --publish never","electron":"node node_modules/electron/cli.js .","favicons":"node scripts/make-favicons.mjs","checksums":"node scripts/checksums.mjs","dist:linux":"npm run icon && npm run build && electron-builder --linux --publish never","electron:dev":"set NODE_ENV=development&& node node_modules/electron/cli.js .","electron:build":"vite build && electron-builder","checksums:verify":"node scripts/checksums.mjs --verify"},"_npmUser":{"name":"ashutosh0x","email":"ashutoshkumarsingh0x@gmail.com"},"repository":{"url":"git+https://github.com/Ashutosh0x/jarvis.git","type":"git"},"_npmVersion":"11.5.2","description":"A local-first desktop AI assistant: federated live web search with no API key, on-device vision and OCR, neural TTS, live Android screen mirroring with touch control, multimodal feedback, and a self-healing supervisor. Ships as an Electron app and an impo","directories":{},"_nodeVersion":"22.18.0","dependencies":{"ws":"^8.21.1","axios":"^1.6.0","three":"^0.158.0","qrcode":"^1.5.4","dat.gui":"^0.7.9","chokidar":"^5.0.0","pdf-to-img":"^6.2.0","@google/genai":"^1.34.0","@yume-chan/adb":"^2.6.2","bonjour-service":"^1.4.3","electron-updater":"^6.6.2","@yume-chan/scrcpy":"^2.3.0","@yume-chan/struct":"^2.3.2","@yume-chan/adb-scrcpy":"^2.3.2","@yume-chan/pcm-player":"^1.0.0","@yume-chan/stream-extra":"^2.6.1","@yume-chan/adb-server-node-tcp":"^2.5.2","@yume-chan/scrcpy-decoder-webcodecs":"^2.5.3"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vite":"^7.2.4","parcel":"^2.7.0","electron":"^39.2.4","electron-builder":"^26.0.12","@electron/notarize":"^2.5.0","@parcel/config-default":"^2.4.0","parcel-reporter-static-files-copy":"^1.3.4"},"optionalDependencies":{"electron":"^39.2.4"},"_npmOperationalInternal":{"tmp":"tmp/jarvis_0.5.0_1785679332812_0.9776747396326904","host":"s3://npm-registry-packages-npm-production"}},"0.6.0":{"name":"@ashutosh0x/jarvis","version":"0.6.0","keywords":["ai","assistant","electron","desktop","voice-assistant","web-search","search-engine","rrf","bm25","gemini","ollama","local-first","tts","ocr","rag","offline-first","screen-mirroring","scrcpy","android","adb","haptics","haptic-feedback","spotify","music","semantic-routing","embeddings","jarvis"],"author":{"name":"Ashutosh Kumar Singh","email":"ashutoshkumarsingh0x@gmail.com"},"license":"MIT","_id":"@ashutosh0x/jarvis@0.6.0","maintainers":[{"name":"ashutosh0x","email":"ashutoshkumarsingh0x@gmail.com"}],"homepage":"https://github.com/Ashutosh0x/jarvis#readme","bugs":{"url":"https://github.com/Ashutosh0x/jarvis/issues"},"bin":{"jarvis":"bin/jarvis.mjs"},"dist":{"shasum":"644fe2503168fe8f823ca5b14742fe7fd8cf4d06","tarball":"https://registry.npmjs.org/@ashutosh0x/jarvis/-/jarvis-0.6.0.tgz","fileCount":237,"integrity":"sha512-N4e3Rk1jo9mvQYvA/+gGO167OJ16NXR5seiBzaPtAVuX8Wl2nIYNTcSZvLhy7EE1YdfJyMxVhPpRGdlxTOORoQ==","signatures":[{"sig":"MEUCIQDUnTKyO55fLk+lJGupvw4Du128eUEe0mrzGuvEnxicVgIgD27BCCxfdSG1S1oNzaR4VoG9CFVMxRD3kmKtv2na58s=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":11024000},"main":"electron.js","engines":{"npm":">=10.0.0","node":">=22.0.0"},"exports":{".":{"import":"./lib/index.mjs","require":"./webSearch.js"},"./search":"./webSearch.js","./metrics":"./metricStore.js","./package.json":"./package.json"},"gitHead":"e5b90b85f294bd8eef47dc6cc2ca5b0603999c1a","scripts":{"dev":"vite","dist":"npm run icon && npm run build && electron-builder --publish never","eval":"node eval/retrieval-eval.mjs && node eval/memory-eval.mjs && node eval/answer-eval.mjs","icon":"node scripts/make-icon.mjs","test":"node scripts/run-tests.mjs","build":"vite build","icons":"npm run icon && npm run favicons","smoke":"node scripts/smoke-test.mjs","jarvis":"node bin/jarvis.mjs","foundry":"node scripts/foundry.mjs","prepack":"npm run build","release":"npm run build && electron-builder --publish always","dist:all":"npm run build && electron-builder -mwl --publish never","dist:mac":"npm run icon && npm run build && electron-builder --mac --publish never","dist:win":"npm run icon && npm run build && electron-builder --win --publish never","electron":"node node_modules/electron/cli.js .","favicons":"node scripts/make-favicons.mjs","checksums":"node scripts/checksums.mjs","dist:linux":"npm run icon && npm run build && electron-builder --linux --publish never","electron:dev":"set NODE_ENV=development&& node node_modules/electron/cli.js .","electron:build":"vite build && electron-builder","foundry:doctor":"node scripts/foundry.mjs doctor","foundry:install":"node scripts/foundry.mjs install","checksums:verify":"node scripts/checksums.mjs --verify"},"_npmUser":{"name":"ashutosh0x","email":"ashutoshkumarsingh0x@gmail.com"},"repository":{"url":"git+https://github.com/Ashutosh0x/jarvis.git","type":"git"},"_npmVersion":"11.5.2","description":"A local-first desktop AI assistant: federated live web search with no API key, semantic capability routing, on-device vision and OCR, neural TTS, live Android screen mirroring with touch control, Spotify search and playback, multimodal feedback, and a sel","directories":{},"_nodeVersion":"22.18.0","dependencies":{"ws":"^8.21.1","axios":"^1.6.0","three":"^0.158.0","qrcode":"^1.5.4","dat.gui":"^0.7.9","chokidar":"^5.0.0","pdf-to-img":"^6.2.0","@google/genai":"^1.34.0","@yume-chan/adb":"^2.6.2","bonjour-service":"^1.4.3","electron-updater":"^6.6.2","@yume-chan/scrcpy":"^2.3.0","@yume-chan/struct":"^2.3.2","@yume-chan/adb-scrcpy":"^2.3.2","@yume-chan/pcm-player":"^1.0.0","@yume-chan/stream-extra":"^2.6.1","@yume-chan/adb-server-node-tcp":"^2.5.2","@yume-chan/scrcpy-decoder-webcodecs":"^2.5.3"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vite":"^7.2.4","parcel":"^2.7.0","electron":"^39.2.4","electron-builder":"^26.0.12","@electron/notarize":"^2.5.0","@parcel/config-default":"^2.4.0","parcel-reporter-static-files-copy":"^1.3.4"},"optionalDependencies":{"electron":"^39.2.4"},"_npmOperationalInternal":{"tmp":"tmp/jarvis_0.6.0_1785815460232_0.9684632087010809","host":"s3://npm-registry-packages-npm-production"}},"0.7.0":{"name":"@ashutosh0x/jarvis","version":"0.7.0","keywords":["ai","assistant","electron","desktop","voice-assistant","web-search","search-engine","rrf","bm25","gemini","ollama","local-first","tts","ocr","rag","offline-first","screen-mirroring","scrcpy","android","adb","haptics","haptic-feedback","spotify","music","semantic-routing","embeddings","jarvis"],"author":{"name":"Ashutosh Kumar Singh","email":"ashutoshkumarsingh0x@gmail.com"},"license":"MIT","_id":"@ashutosh0x/jarvis@0.7.0","maintainers":[{"name":"ashutosh0x","email":"ashutoshkumarsingh0x@gmail.com"}],"homepage":"https://github.com/Ashutosh0x/jarvis#readme","bugs":{"url":"https://github.com/Ashutosh0x/jarvis/issues"},"bin":{"jarvis":"bin/jarvis.mjs"},"dist":{"shasum":"c53f1a519d4dbe79fea9bee76295f12b6a6fc211","tarball":"https://registry.npmjs.org/@ashutosh0x/jarvis/-/jarvis-0.7.0.tgz","fileCount":239,"integrity":"sha512-URPnk7plFb7kbu+zqhFAV5AgZucNg6puFnfJWwAKHOOZZKUTsQNSuwLTha4kOqCrj3RyXAwIS+P0sX0rWpqbcw==","signatures":[{"sig":"MEUCIHSwr/p4DVJ0s/H80HZKcMhPFI2cEvP4RZ7sLkrlqBPCAiEA1MdJNQ7b3XqlMoQJHZLXRIiNkkCxFh9onSpJ1IHzVqo=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":11102944},"main":"electron.js","engines":{"npm":">=10.0.0","node":">=22.0.0"},"exports":{".":{"import":"./lib/index.mjs","require":"./webSearch.js"},"./search":"./webSearch.js","./metrics":"./metricStore.js","./package.json":"./package.json"},"gitHead":"bbea8d8f17d72ba897e562ed289636c47f0aaf67","scripts":{"dev":"vite","dist":"npm run icon && npm run build && electron-builder --publish never","eval":"node eval/retrieval-eval.mjs && node eval/memory-eval.mjs && node eval/answer-eval.mjs","icon":"node scripts/make-icon.mjs","test":"node scripts/run-tests.mjs","build":"vite build","icons":"npm run icon && npm run favicons","smoke":"node scripts/smoke-test.mjs","jarvis":"node bin/jarvis.mjs","foundry":"node scripts/foundry.mjs","prepack":"npm run build","release":"npm run build && electron-builder --publish always","dist:all":"npm run build && electron-builder -mwl --publish never","dist:mac":"npm run icon && npm run build && electron-builder --mac --publish never","dist:win":"npm run icon && npm run build && electron-builder --win --publish never","electron":"node node_modules/electron/cli.js .","favicons":"node scripts/make-favicons.mjs","checksums":"node scripts/checksums.mjs","dist:linux":"npm run icon && npm run build && electron-builder --linux --publish never","electron:dev":"set NODE_ENV=development&& node node_modules/electron/cli.js .","electron:build":"vite build && electron-builder","foundry:doctor":"node scripts/foundry.mjs doctor","foundry:install":"node scripts/foundry.mjs install","checksums:verify":"node scripts/checksums.mjs --verify"},"_npmUser":{"name":"ashutosh0x","email":"ashutoshkumarsingh0x@gmail.com"},"repository":{"url":"git+https://github.com/Ashutosh0x/jarvis.git","type":"git"},"_npmVersion":"11.5.2","description":"A local-first desktop AI assistant: federated live web search with no API key, semantic capability routing, on-device vision and OCR, neural TTS, live Android screen mirroring with touch control, Spotify search and playback, multimodal feedback, and a sel","directories":{},"_nodeVersion":"22.18.0","dependencies":{"ws":"^8.21.1","axios":"^1.6.0","three":"^0.158.0","qrcode":"^1.5.4","dat.gui":"^0.7.9","chokidar":"^5.0.0","pdf-to-img":"^6.2.0","@google/genai":"^1.34.0","@yume-chan/adb":"^2.6.2","bonjour-service":"^1.4.3","electron-updater":"^6.6.2","@yume-chan/scrcpy":"^2.3.0","@yume-chan/struct":"^2.3.2","@yume-chan/adb-scrcpy":"^2.3.2","@yume-chan/pcm-player":"^1.0.0","@yume-chan/stream-extra":"^2.6.1","@yume-chan/adb-server-node-tcp":"^2.5.2","@yume-chan/scrcpy-decoder-webcodecs":"^2.5.3"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vite":"^7.2.4","parcel":"^2.7.0","electron":"^39.2.4","electron-builder":"^26.0.12","@electron/notarize":"^2.5.0","@parcel/config-default":"^2.4.0","parcel-reporter-static-files-copy":"^1.3.4"},"optionalDependencies":{"electron":"^39.2.4"},"_npmOperationalInternal":{"tmp":"tmp/jarvis_0.7.0_1785819998499_0.18083699644967544","host":"s3://npm-registry-packages-npm-production"}},"0.8.0":{"name":"@ashutosh0x/jarvis","version":"0.8.0","keywords":["ai","assistant","electron","desktop","voice-assistant","web-search","search-engine","rrf","bm25","gemini","ollama","local-first","tts","ocr","rag","offline-first","screen-mirroring","scrcpy","android","adb","haptics","haptic-feedback","spotify","music","semantic-routing","embeddings","jarvis"],"author":{"name":"Ashutosh Kumar Singh","email":"ashutoshkumarsingh0x@gmail.com"},"license":"MIT","_id":"@ashutosh0x/jarvis@0.8.0","maintainers":[{"name":"ashutosh0x","email":"ashutoshkumarsingh0x@gmail.com"}],"homepage":"https://github.com/Ashutosh0x/jarvis#readme","bugs":{"url":"https://github.com/Ashutosh0x/jarvis/issues"},"bin":{"jarvis":"bin/jarvis.mjs"},"dist":{"shasum":"a0b3785b2ef1363a94507fda93abfd31ea96ee4f","tarball":"https://registry.npmjs.org/@ashutosh0x/jarvis/-/jarvis-0.8.0.tgz","fileCount":242,"integrity":"sha512-c1jzBEwH0aMqR4AsocO5jjRMk6CUPVouubqaHNSF8MC5mL0rBuzchNGx5bEOR1gDR4jQeDLKIFlLvdkBxnPG9g==","signatures":[{"sig":"MEQCIA9W3K/+nleLuPJAA0eT6GL8CwVDP/hv95epYx2N0/O6AiAgkzpx5Eql+D4frqeyNswcRHbNo3xMKV69P1TDazX+LQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":11494664},"main":"electron.js","engines":{"npm":">=10.0.0","node":">=22.0.0"},"exports":{".":{"import":"./lib/index.mjs","require":"./webSearch.js"},"./search":"./webSearch.js","./metrics":"./metricStore.js","./package.json":"./package.json"},"gitHead":"c6205ffb057d8866e2b416a8e1fb350e0c8a9d58","scripts":{"dev":"vite","dist":"npm run icon && npm run build && electron-builder --publish never","eval":"node eval/retrieval-eval.mjs && node eval/memory-eval.mjs && node eval/answer-eval.mjs","icon":"node scripts/make-icon.mjs","test":"node scripts/run-tests.mjs","build":"vite build","icons":"npm run icon && npm run favicons","smoke":"node scripts/smoke-test.mjs","jarvis":"node bin/jarvis.mjs","foundry":"node scripts/foundry.mjs","prepack":"npm run build","release":"npm run build && electron-builder --publish always","dist:all":"npm run build && electron-builder -mwl --publish never","dist:mac":"npm run icon && npm run build && electron-builder --mac --publish never","dist:win":"npm run icon && npm run build && electron-builder --win --publish never","electron":"node node_modules/electron/cli.js .","favicons":"node scripts/make-favicons.mjs","checksums":"node scripts/checksums.mjs","dist:linux":"npm run icon && npm run build && electron-builder --linux --publish never","electron:dev":"set NODE_ENV=development&& node node_modules/electron/cli.js .","electron:build":"vite build && electron-builder","foundry:doctor":"node scripts/foundry.mjs doctor","foundry:install":"node scripts/foundry.mjs install","checksums:verify":"node scripts/checksums.mjs --verify"},"_npmUser":{"name":"ashutosh0x","email":"ashutoshkumarsingh0x@gmail.com"},"repository":{"url":"git+https://github.com/Ashutosh0x/jarvis.git","type":"git"},"_npmVersion":"11.5.2","description":"A local-first desktop AI assistant: federated live web search with no API key, semantic capability routing, on-device vision and OCR, neural TTS, live Android screen mirroring with touch control, Spotify search and playback, multimodal feedback, and a sel","directories":{},"_nodeVersion":"22.18.0","dependencies":{"ws":"^8.21.1","axios":"^1.6.0","three":"^0.158.0","qrcode":"^1.5.4","dat.gui":"^0.7.9","chokidar":"^5.0.0","pdf-to-img":"^6.2.0","@google/genai":"^1.34.0","@yume-chan/adb":"^2.6.2","bonjour-service":"^1.4.3","electron-updater":"^6.6.2","@yume-chan/scrcpy":"^2.3.0","@yume-chan/struct":"^2.3.2","@yume-chan/adb-scrcpy":"^2.3.2","@yume-chan/pcm-player":"^1.0.0","@yume-chan/stream-extra":"^2.6.1","@yume-chan/adb-server-node-tcp":"^2.5.2","@yume-chan/scrcpy-decoder-webcodecs":"^2.5.3"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vite":"^7.2.4","parcel":"^2.7.0","electron":"^39.2.4","electron-builder":"^26.0.12","@electron/notarize":"^2.5.0","@parcel/config-default":"^2.4.0","parcel-reporter-static-files-copy":"^1.3.4"},"optionalDependencies":{"electron":"^39.2.4"},"_npmOperationalInternal":{"tmp":"tmp/jarvis_0.8.0_1785821823100_0.990095429564837","host":"s3://npm-registry-packages-npm-production"}},"0.9.0":{"name":"@ashutosh0x/jarvis","version":"0.9.0","keywords":["ai","assistant","electron","desktop","voice-assistant","web-search","search-engine","rrf","bm25","gemini","ollama","local-first","tts","ocr","rag","offline-first","screen-mirroring","scrcpy","android","adb","haptics","haptic-feedback","spotify","music","semantic-routing","embeddings","jarvis"],"author":{"name":"Ashutosh Kumar Singh","email":"ashutoshkumarsingh0x@gmail.com"},"license":"MIT","_id":"@ashutosh0x/jarvis@0.9.0","maintainers":[{"name":"ashutosh0x","email":"ashutoshkumarsingh0x@gmail.com"}],"homepage":"https://github.com/Ashutosh0x/jarvis#readme","bugs":{"url":"https://github.com/Ashutosh0x/jarvis/issues"},"bin":{"jarvis":"bin/jarvis.mjs"},"dist":{"shasum":"f3aab358b8096600a3839b39643c792a33cc067e","tarball":"https://registry.npmjs.org/@ashutosh0x/jarvis/-/jarvis-0.9.0.tgz","fileCount":249,"integrity":"sha512-bCZgh2Ju81kO24uku7uJj6l6ua4aR1PMCTJ1Cy+j+RZVolc1uuyY0ZwxTCYED5jtts7roVoeLVixESdaNCq9Zw==","signatures":[{"sig":"MEQCIDt1tee/tlFFpfwBBNPTbVSpZvXDIwVpV8vKDIISBM7NAiBzv2EJKcF7WQXuqk0CEHTYcOffXxXlDqYd00kPVPIv6A==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":11634692},"main":"electron.js","engines":{"npm":">=10.0.0","node":">=22.0.0"},"exports":{".":{"import":"./lib/index.mjs","require":"./webSearch.js"},"./search":"./webSearch.js","./metrics":"./metricStore.js","./package.json":"./package.json"},"gitHead":"5c4e4478dea8c9bc9d4ea50b1c009e9bd99b3e5d","scripts":{"dev":"vite","dist":"npm run icon && npm run build && electron-builder --publish never","eval":"node eval/retrieval-eval.mjs && node eval/memory-eval.mjs && node eval/answer-eval.mjs","icon":"node scripts/make-icon.mjs","test":"node scripts/run-tests.mjs","build":"vite build","icons":"npm run icon && npm run favicons","smoke":"node scripts/smoke-test.mjs","jarvis":"node bin/jarvis.mjs","foundry":"node scripts/foundry.mjs","prepack":"npm run build","release":"npm run build && electron-builder --publish always","dist:all":"npm run build && electron-builder -mwl --publish never","dist:mac":"npm run icon && npm run build && electron-builder --mac --publish never","dist:win":"npm run icon && npm run build && electron-builder --win --publish never","electron":"node node_modules/electron/cli.js .","favicons":"node scripts/make-favicons.mjs","checksums":"node scripts/checksums.mjs","dist:linux":"npm run icon && npm run build && electron-builder --linux --publish never","electron:dev":"set NODE_ENV=development&& node node_modules/electron/cli.js .","electron:build":"vite build && electron-builder","foundry:doctor":"node scripts/foundry.mjs doctor","foundry:install":"node scripts/foundry.mjs install","checksums:verify":"node scripts/checksums.mjs --verify"},"_npmUser":{"name":"ashutosh0x","email":"ashutoshkumarsingh0x@gmail.com"},"repository":{"url":"git+https://github.com/Ashutosh0x/jarvis.git","type":"git"},"_npmVersion":"11.5.2","description":"A local-first desktop AI assistant: federated live web search with no API key, semantic capability routing, on-device vision and OCR, neural TTS, live Android screen mirroring with touch control, Spotify search and playback, multimodal feedback, and a sel","directories":{},"_nodeVersion":"22.18.0","dependencies":{"ws":"^8.21.1","axios":"^1.6.0","three":"^0.158.0","qrcode":"^1.5.4","dat.gui":"^0.7.9","chokidar":"^5.0.0","pdf-to-img":"^6.2.0","satellite.js":"^6.0.2","@google/genai":"^1.34.0","@yume-chan/adb":"^2.6.2","bonjour-service":"^1.4.3","electron-updater":"^6.6.2","@yume-chan/scrcpy":"^2.3.0","@yume-chan/struct":"^2.3.2","@yume-chan/adb-scrcpy":"^2.3.2","@yume-chan/pcm-player":"^1.0.0","@yume-chan/stream-extra":"^2.6.1","@yume-chan/adb-server-node-tcp":"^2.5.2","@yume-chan/scrcpy-decoder-webcodecs":"^2.5.3"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vite":"^7.2.4","parcel":"^2.7.0","electron":"^39.2.4","electron-builder":"^26.0.12","@electron/notarize":"^2.5.0","@parcel/config-default":"^2.4.0","parcel-reporter-static-files-copy":"^1.3.4"},"optionalDependencies":{"electron":"^39.2.4"},"_npmOperationalInternal":{"tmp":"tmp/jarvis_0.9.0_1785837983430_0.11870039645225461","host":"s3://npm-registry-packages-npm-production"}},"0.10.0":{"name":"@ashutosh0x/jarvis","version":"0.10.0","keywords":["ai","assistant","electron","desktop","voice-assistant","web-search","search-engine","rrf","bm25","gemini","ollama","local-first","tts","ocr","rag","offline-first","screen-mirroring","scrcpy","android","adb","haptics","haptic-feedback","spotify","music","semantic-routing","embeddings","globe","threejs","geospatial","openstreetmap","market-cap","jarvis"],"author":{"name":"Ashutosh Kumar Singh","email":"ashutoshkumarsingh0x@gmail.com"},"license":"MIT","_id":"@ashutosh0x/jarvis@0.10.0","maintainers":[{"name":"ashutosh0x","email":"ashutoshkumarsingh0x@gmail.com"}],"homepage":"https://github.com/Ashutosh0x/jarvis#readme","bugs":{"url":"https://github.com/Ashutosh0x/jarvis/issues"},"bin":{"jarvis":"bin/jarvis.mjs"},"dist":{"shasum":"1f2b7c068fe93cd018536e7960ef8647f0cebbd2","tarball":"https://registry.npmjs.org/@ashutosh0x/jarvis/-/jarvis-0.10.0.tgz","fileCount":289,"integrity":"sha512-o8ieUaOOmJGugRy/Bvfd5Wja7uIIJ8JzJgzrXwLo5mXdXXIR9b6iCuE/qOotZWz/M1zS6ngFa/B8YkxT2irKow==","signatures":[{"sig":"MEYCIQC1FMo3PrRBhZL3ZABpctKqKPA1VpqsG5jS2yLyQMnZTAIhAJMAYqmKa4MOJM/09sKY9qyD1FI9O03OWmPmJXNSQZnZ","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":23576238},"main":"electron.js","engines":{"npm":">=10.0.0","node":">=22.0.0"},"exports":{".":{"import":"./lib/index.mjs","require":"./webSearch.js"},"./search":"./webSearch.js","./metrics":"./metricStore.js","./package.json":"./package.json"},"gitHead":"139fd3998ffd6b38485a40dadcd216eb09615c6a","scripts":{"dev":"vite","dist":"npm run icon && npm run build && electron-builder --publish never","eval":"node eval/retrieval-eval.mjs && node eval/memory-eval.mjs && node eval/answer-eval.mjs","icon":"node scripts/make-icon.mjs","test":"node scripts/run-tests.mjs","build":"vite build","icons":"npm run icon && npm run favicons","smoke":"node scripts/smoke-test.mjs","jarvis":"node bin/jarvis.mjs","foundry":"node scripts/foundry.mjs","prepack":"npm run build","release":"npm run build && electron-builder --publish always","dist:all":"npm run build && electron-builder -mwl --publish never","dist:mac":"npm run icon && npm run build && electron-builder --mac --publish never","dist:win":"npm run icon && npm run build && electron-builder --win --publish never","electron":"node node_modules/electron/cli.js .","favicons":"node scripts/make-favicons.mjs","checksums":"node scripts/checksums.mjs","dist:linux":"npm run icon && npm run build && electron-builder --linux --publish never","electron:dev":"set NODE_ENV=development&& node node_modules/electron/cli.js .","electron:build":"vite build && electron-builder","foundry:doctor":"node scripts/foundry.mjs doctor","foundry:install":"node scripts/foundry.mjs install","checksums:verify":"node scripts/checksums.mjs --verify"},"_npmUser":{"name":"ashutosh0x","email":"ashutoshkumarsingh0x@gmail.com"},"repository":{"url":"git+https://github.com/Ashutosh0x/jarvis.git","type":"git"},"_npmVersion":"11.5.2","description":"A local-first desktop AI assistant: federated live web search with no API key, semantic capability routing, a vector-globe command centre with live earth layers and 11,222 companies mapped to their head offices, on-device vision and OCR, neural TTS, live ","directories":{},"_nodeVersion":"22.18.0","dependencies":{"ws":"^8.21.1","axios":"^1.6.0","three":"^0.158.0","qrcode":"^1.5.4","dat.gui":"^0.7.9","chokidar":"^5.0.0","pdf-to-img":"^6.2.0","satellite.js":"^6.0.2","@google/genai":"^1.34.0","@yume-chan/adb":"^2.6.2","bonjour-service":"^1.4.3","electron-updater":"^6.6.2","@yume-chan/scrcpy":"^2.3.0","@yume-chan/struct":"^2.3.2","@yume-chan/adb-scrcpy":"^2.3.2","@yume-chan/pcm-player":"^1.0.0","@yume-chan/stream-extra":"^2.6.1","@yume-chan/adb-server-node-tcp":"^2.5.2","@yume-chan/scrcpy-decoder-webcodecs":"^2.5.3"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vite":"^7.2.4","parcel":"^2.7.0","electron":"^39.2.4","electron-builder":"^26.0.12","@electron/notarize":"^2.5.0","@parcel/config-default":"^2.4.0","parcel-reporter-static-files-copy":"^1.3.4"},"optionalDependencies":{"electron":"^39.2.4"},"_npmOperationalInternal":{"tmp":"tmp/jarvis_0.10.0_1786473558559_0.2694405690527739","host":"s3://npm-registry-packages-npm-production"}},"0.11.0":{"name":"@ashutosh0x/jarvis","version":"0.11.0","description":"A local-first desktop AI assistant: federated live web search with no API key, semantic capability routing, a vector-globe command centre with live earth layers and 11,222 companies mapped to their head offices, an offline store of 304,613 USGS mineral de","main":"electron.js","scripts":{"test":"node scripts/run-tests.mjs","eval":"node eval/retrieval-eval.mjs && node eval/memory-eval.mjs && node eval/answer-eval.mjs","eval:mineral":"node eval/mineral-eval.mjs","dev":"vite","build":"vite build","electron":"node node_modules/electron/cli.js .","electron:dev":"set NODE_ENV=development&& node node_modules/electron/cli.js .","electron:build":"vite build && electron-builder","icon":"node scripts/make-icon.mjs","dist":"npm run icon && npm run build && electron-builder --publish never","dist:win":"npm run icon && npm run build && electron-builder --win --publish never","dist:mac":"npm run icon && npm run build && electron-builder --mac --publish never","dist:linux":"npm run icon && npm run build && electron-builder --linux --publish never","dist:all":"npm run build && electron-builder -mwl --publish never","checksums":"node scripts/checksums.mjs","checksums:verify":"node scripts/checksums.mjs --verify","smoke":"node scripts/smoke-test.mjs","release":"npm run build && electron-builder --publish always","favicons":"node scripts/make-favicons.mjs","icons":"npm run icon && npm run favicons","prepack":"npm run build","jarvis":"node bin/jarvis.mjs","foundry":"node scripts/foundry.mjs","foundry:doctor":"node scripts/foundry.mjs doctor","foundry:install":"node scripts/foundry.mjs install","eval:routing":"node eval/routing-eval.mjs","build:vocab":"node scripts/build-routing-vocab.mjs"},"keywords":["ai","assistant","electron","desktop","voice-assistant","web-search","search-engine","rrf","bm25","gemini","ollama","local-first","tts","ocr","rag","offline-first","screen-mirroring","scrcpy","android","adb","haptics","haptic-feedback","spotify","music","semantic-routing","embeddings","globe","threejs","geospatial","openstreetmap","market-cap","jarvis"],"author":{"name":"Ashutosh Kumar Singh","email":"ashutoshkumarsingh0x@gmail.com"},"license":"MIT","dependencies":{"@google/genai":"^1.34.0","@huggingface/transformers":"^4.2.0","@yume-chan/adb":"^2.6.2","@yume-chan/adb-scrcpy":"^2.3.2","@yume-chan/adb-server-node-tcp":"^2.5.2","@yume-chan/pcm-player":"^1.0.0","@yume-chan/scrcpy":"^2.3.0","@yume-chan/scrcpy-decoder-webcodecs":"^2.5.3","@yume-chan/stream-extra":"^2.6.1","@yume-chan/struct":"^2.3.2","axios":"^1.6.0","bonjour-service":"^1.4.3","chokidar":"^5.0.0","dat.gui":"^0.7.9","electron-updater":"^6.6.2","pdf-to-img":"^6.2.0","puppeteer-core":"^25.7.0","qrcode":"^1.5.4","satellite.js":"^6.0.2","three":"^0.158.0","ws":"^8.21.1"},"devDependencies":{"@electron/notarize":"^2.5.0","@parcel/config-default":"^2.4.0","electron":"^39.2.4","electron-builder":"^26.0.12","parcel":"^2.7.0","parcel-reporter-static-files-copy":"^1.3.4","vite":"^7.2.4"},"engines":{"node":">=22.0.0","npm":">=10.0.0"},"repository":{"type":"git","url":"git+https://github.com/Ashutosh0x/jarvis.git"},"homepage":"https://github.com/Ashutosh0x/jarvis#readme","bugs":{"url":"https://github.com/Ashutosh0x/jarvis/issues"},"bin":{"jarvis":"bin/jarvis.mjs"},"exports":{".":{"import":"./lib/index.mjs","require":"./webSearch.js"},"./search":"./webSearch.js","./metrics":"./metricStore.js","./package.json":"./package.json"},"publishConfig":{"access":"public"},"optionalDependencies":{"electron":"^39.2.4"},"_id":"@ashutosh0x/jarvis@0.11.0","gitHead":"8b48b4b098dfc9473e7dd1f9805f7d20a012a774","_nodeVersion":"22.18.0","_npmVersion":"11.5.2","dist":{"integrity":"sha512-86Lo5v8gHx3JMN+CgevqVqVCxIwrSuUTJu90yuD6zG236C8Evd4UkHNzO5a1zCoHE/pqbQGZTTdJDHxe50pFUg==","shasum":"0e5faf41526d152996b31e7dd6c9aa6f0b589846","tarball":"https://registry.npmjs.org/@ashutosh0x/jarvis/-/jarvis-0.11.0.tgz","fileCount":396,"unpackedSize":28640142,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIEUemfFlXJ6sKRTbjM6qsZbrN+Voe5aGEMnm2Aj5YAJFAiEAnPEUlo17UgfEQuuaTGOAdPEIaivXVs5z45fDjjlh1PQ="}]},"_npmUser":{"name":"ashutosh0x","email":"ashutoshkumarsingh0x@gmail.com"},"directories":{},"maintainers":[{"name":"ashutosh0x","email":"ashutoshkumarsingh0x@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/jarvis_0.11.0_1787252173398_0.15125571058682374"},"_hasShrinkwrap":false}},"time":{"created":"2026-07-31T00:50:43.829Z","modified":"2026-08-20T18:56:13.884Z","0.1.0":"2026-07-31T00:50:44.344Z","0.1.1":"2026-07-31T01:00:16.980Z","0.1.2":"2026-07-31T02:15:42.040Z","0.2.0":"2026-07-31T03:44:44.611Z","0.3.0":"2026-07-31T05:05:51.749Z","0.4.0":"2026-08-02T12:19:09.703Z","0.5.0":"2026-08-02T14:02:13.166Z","0.6.0":"2026-08-04T03:51:00.439Z","0.7.0":"2026-08-04T05:06:38.778Z","0.8.0":"2026-08-04T05:37:03.367Z","0.9.0":"2026-08-04T10:06:23.669Z","0.10.0":"2026-08-11T18:39:18.833Z","0.11.0":"2026-08-20T18:56:13.693Z"},"bugs":{"url":"https://github.com/Ashutosh0x/jarvis/issues"},"author":{"name":"Ashutosh Kumar Singh","email":"ashutoshkumarsingh0x@gmail.com"},"license":"MIT","homepage":"https://github.com/Ashutosh0x/jarvis#readme","keywords":["ai","assistant","electron","desktop","voice-assistant","web-search","search-engine","rrf","bm25","gemini","ollama","local-first","tts","ocr","rag","offline-first","screen-mirroring","scrcpy","android","adb","haptics","haptic-feedback","spotify","music","semantic-routing","embeddings","globe","threejs","geospatial","openstreetmap","market-cap","jarvis"],"repository":{"type":"git","url":"git+https://github.com/Ashutosh0x/jarvis.git"},"description":"A local-first desktop AI assistant: federated live web search with no API key, semantic capability routing, a vector-globe command centre with live earth layers and 11,222 companies mapped to their head offices, an offline store of 304,613 USGS mineral de","maintainers":[{"name":"ashutosh0x","email":"ashutoshkumarsingh0x@gmail.com"}],"readme":"# JARVIS - Local-First Desktop Assistant\n\n<p align=\"center\">\n  <img src=\"https://img.shields.io/badge/Electron-47848F?style=for-the-badge&logo=electron&logoColor=white\" alt=\"Electron\" />\n  <img src=\"https://img.shields.io/badge/Vite-646CFF?style=for-the-badge&logo=vite&logoColor=white\" alt=\"Vite\" />\n  <img src=\"https://img.shields.io/badge/Three.js-000000?style=for-the-badge&logo=three.js&logoColor=white\" alt=\"Three.js\" />\n  <img src=\"https://img.shields.io/badge/Node.js-5FA04E?style=for-the-badge&logo=node.js&logoColor=white\" alt=\"Node.js\" />\n  <img src=\"https://img.shields.io/badge/JavaScript-F7DF1E?style=for-the-badge&logo=javascript&logoColor=black\" alt=\"JavaScript\" />\n</p>\n\n<p align=\"center\">\n  <img src=\"https://img.shields.io/badge/Ollama-000000?style=for-the-badge&logo=ollama&logoColor=white\" alt=\"Ollama\" />\n  <img src=\"https://img.shields.io/badge/Gemma%203-4285F4?style=for-the-badge&logo=google&logoColor=white\" alt=\"Gemma 3\" />\n  <img src=\"https://img.shields.io/badge/OpenAI%20Whisper-412991?style=for-the-badge&logo=openai&logoColor=white\" alt=\"Whisper via transformers.js\" />\n  <img src=\"https://img.shields.io/badge/Python-3776AB?style=for-the-badge&logo=python&logoColor=white\" alt=\"Python\" />\n  <img src=\"https://img.shields.io/badge/WebGL-990000?style=for-the-badge&logo=webgl&logoColor=white\" alt=\"WebGL\" />\n</p>\n\n<p align=\"center\">\n  <img src=\"https://img.shields.io/badge/Google%20Calendar-4285F4?style=for-the-badge&logo=googlecalendar&logoColor=white\" alt=\"Google Calendar API\" />\n  <img src=\"https://img.shields.io/badge/Google%20Meet-00897B?style=for-the-badge&logo=googlemeet&logoColor=white\" alt=\"Google Meet API\" />\n  <img src=\"https://img.shields.io/badge/OAuth%202.0%20%2B%20PKCE-EB5424?style=for-the-badge&logo=auth0&logoColor=white\" alt=\"OAuth 2.0 with PKCE\" />\n  <img src=\"https://img.shields.io/badge/Web%20Audio-FF3E00?style=for-the-badge&logo=webaudio&logoColor=white\" alt=\"Web Audio API\" />\n  <img src=\"https://img.shields.io/badge/npm-CB3837?style=for-the-badge&logo=npm&logoColor=white\" alt=\"npm\" />\n</p>\n\n<p align=\"center\">\n  <img src=\"https://img.shields.io/badge/Google%20Maps%20Platform-4285F4?style=for-the-badge&logo=googlemaps&logoColor=white\" alt=\"Google Maps Platform\" />\n  <img src=\"https://img.shields.io/badge/Natural%20Earth-7EBC6F?style=for-the-badge&logo=openstreetmap&logoColor=white\" alt=\"Natural Earth\" />\n  <img src=\"https://img.shields.io/badge/Nominatim-7EBC6F?style=for-the-badge&logo=openstreetmap&logoColor=white\" alt=\"Nominatim / OpenStreetMap\" />\n  <img src=\"https://img.shields.io/badge/Wikipedia-000000?style=for-the-badge&logo=wikipedia&logoColor=white\" alt=\"Wikipedia REST API\" />\n  <img src=\"https://img.shields.io/badge/Wikimedia%20Commons-006699?style=for-the-badge&logo=wikimediacommons&logoColor=white\" alt=\"Wikimedia Commons\" />\n</p>\n\n<p align=\"center\">\n  <img src=\"https://img.shields.io/badge/USGS%20earthquakes-1A6B54?style=for-the-badge&logo=googleearth&logoColor=white\" alt=\"USGS earthquakes\" />\n  <img src=\"https://img.shields.io/badge/NASA%20FIRMS-E03C31?style=for-the-badge&logo=nasa&logoColor=white\" alt=\"NASA FIRMS\" />\n  <img src=\"https://img.shields.io/badge/OpenSky-1B4C7E?style=for-the-badge&logo=airplayaudio&logoColor=white\" alt=\"OpenSky Network\" />\n  <img src=\"https://img.shields.io/badge/Esri%20Imagery-0079C1?style=for-the-badge&logo=esri&logoColor=white\" alt=\"Esri World Imagery\" />\n  <img src=\"https://img.shields.io/badge/Luma%20events-6B21A8?style=for-the-badge&logo=googlecalendar&logoColor=white\" alt=\"Luma events\" />\n</p>\n\n<p align=\"center\">\n  <img src=\"https://img.shields.io/badge/NASA%20EONET-0B3D91?style=for-the-badge&logo=nasa&logoColor=white\" alt=\"NASA EONET\" />\n  <img src=\"https://img.shields.io/badge/Wikidata-006699?style=for-the-badge&logo=wikidata&logoColor=white\" alt=\"Wikidata\" />\n  <img src=\"https://img.shields.io/badge/CelesTrak%20SGP4-1B4C7E?style=for-the-badge\" alt=\"CelesTrak SGP4\" />\n  <img src=\"https://img.shields.io/badge/NOAA%20SWPC-005EB8?style=for-the-badge\" alt=\"NOAA Space Weather Prediction Center\" />\n  <img src=\"https://img.shields.io/badge/11%2C222%20companies%20mapped-2CA02C?style=for-the-badge\" alt=\"11,222 companies mapped\" />\n</p>\n\n<p align=\"center\">\n  <img src=\"https://img.shields.io/badge/Spotify-1DB954?style=for-the-badge&logo=spotify&logoColor=white\" alt=\"Spotify Web API\" />\n  <img src=\"https://img.shields.io/badge/Android-34A853?style=for-the-badge&logo=android&logoColor=white\" alt=\"Android\" />\n  <img src=\"https://img.shields.io/badge/Kotlin-7F52FF?style=for-the-badge&logo=kotlin&logoColor=white\" alt=\"Kotlin\" />\n  <img src=\"https://img.shields.io/badge/Gradle-02303A?style=for-the-badge&logo=gradle&logoColor=white\" alt=\"Gradle\" />\n  <img src=\"https://img.shields.io/badge/OkHttp-3E4348?style=for-the-badge&logo=square&logoColor=white\" alt=\"OkHttp\" />\n  <img src=\"https://img.shields.io/badge/WebSocket-010101?style=for-the-badge&logo=socketdotio&logoColor=white\" alt=\"WebSocket\" />\n</p>\n\n<p align=\"center\">\n  <img src=\"https://img.shields.io/badge/platform-Windows%2011-0078D6?style=flat-square&logo=windows&logoColor=white\" alt=\"Platform\" />\n  <img src=\"https://img.shields.io/badge/runs-100%25%20offline-success?style=flat-square\" alt=\"Offline\" />\n  <img src=\"https://img.shields.io/badge/cloud%20API-none-critical?style=flat-square\" alt=\"No cloud\" />\n</p>\n\n<p align=\"center\">\n  <a href=\"https://www.npmjs.com/package/@ashutosh0x/jarvis\"><img src=\"https://img.shields.io/npm/v/@ashutosh0x/jarvis?style=flat-square&color=CB3837&logo=npm&logoColor=white\" alt=\"npm version\" /></a>\n  <a href=\"https://www.npmjs.com/package/@ashutosh0x/jarvis\"><img src=\"https://img.shields.io/npm/dm/@ashutosh0x/jarvis?style=flat-square&color=CB3837&logo=npm&logoColor=white\" alt=\"npm downloads\" /></a>\n  <img src=\"https://img.shields.io/badge/node-%E2%89%A522-339933?style=flat-square&logo=node.js&logoColor=white\" alt=\"Node 22+\" />\n  <img src=\"https://img.shields.io/badge/license-MIT-blue?style=flat-square\" alt=\"License\" />\n</p>\n\n## Install\n\n```bash\nnpm install -g @ashutosh0x/jarvis\njarvis\n```\n\nOr without installing anything permanently:\n\n```bash\nnpx @ashutosh0x/jarvis\n```\n\n**No API key is required to start.** Web search works out of the box. Every key\nyou add unlocks a feature, and the app degrades honestly without one rather than\nerroring — run `jarvis doctor` to see exactly what is and is not available on\nyour machine:\n\n```\n  Jarvis 0.1.0\n\n  Runtime\n    ✓ Node                   v22.14.0\n    ✓ Platform               win32 x64\n    ✓ Electron               installed\n    ✓ Interface built\n    ✓ jarvis command         %LOCALAPPDATA%\\Jarvis\\bin\\jarvis.cmd\n\n  Optional services\n    · GEMINI_API_KEY         unset — conversational answers disabled\n    ✓ Ollama                 http://127.0.0.1:11434\n    · SearXNG                unset — using public search providers\n```\n\nEverything marked `·` is optional.\n\n### The `jarvis` command\n\nWhichever way you install it, `jarvis` becomes a command you can type in any\nterminal. Installing an app normally gives you a Start-menu entry, not a\ncommand, so setup closes that gap itself: it writes a small launcher to a\nper-user directory and puts that directory on your PATH.\n\n| | |\n|---|---|\n| Windows | `%LOCALAPPDATA%\\Jarvis\\bin\\jarvis.cmd`, registered in `HKCU\\Environment` |\n| macOS / Linux | `~/.local/bin/jarvis`, with a guarded block in your shell rc if that directory is not already on PATH |\n\n```bash\njarvis link      # do it now, if setup could not\njarvis unlink    # undo it completely\njarvis doctor    # shows where the command resolves from\n```\n\nWhat it will not do:\n\n- **No `setx`.** It truncates PATH at 1024 characters and rewrites\n  `REG_EXPAND_SZ` as `REG_SZ`, so `%VAR%` entries stop expanding. The registry\n  is written directly, preserving the original value kind. (The PATH on the\n  machine this was built on is 2,152 characters — `setx` would have destroyed it.)\n- **No elevation, no machine-wide change.** `HKCU` only.\n- **No second `jarvis`.** If one is already on PATH — `npm i -g` puts one there —\n  it is left alone and reported rather than shadowed.\n- **No claimed success.** The previous PATH is saved to disk first, the new value\n  is read back after writing, and the change is verified against the *stored*\n  PATH rather than this process's environment.\n\nA shell inherits its environment when it starts, so **the terminal you ran it\nfrom cannot see the change** — open a new one. Every message says so rather than\nimplying otherwise.\n\n> The package bundles Electron, so the first install downloads a platform\n> binary (~100 MB). That is the price of `npm i -g` producing a working app\n> instead of a list of instructions. Prefer a native installer? See\n> [releases](https://github.com/Ashutosh0x/jarvis/releases) for signed `.exe`,\n> `.dmg`, `.AppImage`, `.deb` and `.rpm` builds, each with a SHA-256 checksum.\n\n### Use the search engine as a library\n\nThe search engine has **no Electron dependency and no DOM** — it is plain Node,\nindependently tested, and installable on its own terms:\n\n```js\nimport { search } from '@ashutosh0x/jarvis';\n\nconst { results, answer, providers } = await search('rust async runtime comparison');\nconsole.log(answer);      // extracted answer, or null\nconsole.log(providers);   // ['crates', 'github', 'hn', …]\n```\n\n<details>\n<summary><b>Individual exports</b></summary>\n\n```js\nimport {\n  rrfFuse,          // Reciprocal Rank Fusion, k=60\n  bm25Search,       // BM25, k1=1.2 b=0.75\n  editDistance,     // Damerau-Levenshtein\n  isTimeSensitive,  // does this answer change by the hour?\n  gatherAll,        // parallel provider fan-out, returns on quorum\n  SearchCache,\n  hedgedRace,       // race N RPC endpoints, take the first good one\n} from '@ashutosh0x/jarvis';\n\nimport { stats, rollup } from '@ashutosh0x/jarvis/metrics';\n```\n\n**Search** — `search`, `buildProviders`, `detectIntents`, `isTimeSensitive`,\n`gatherAll`, `rrfFuse`, `bm25Search`, `rankResults`, `dedupeResults`,\n`extractAnswer`, `verifyAnswer`, `providerWeights`, `editDistance`,\n`shouldApplyCorrection`, `htmlToText`, `SearchCache`\n\n**Metrics** — `stats`, `windowed`, `rollup`, `rollupByDay`, `pruneRaw`, `makeSample`\n\n**Networking** — `hedgedRace`, `createStickyOrder`, `backoffDelay`,\n`createDedup`, `createBlockTracker`, `prioritizeAlerts`\n\n**Market analytics** — `dailyReturns`, `correlation`, `beta`, `peerIndex`,\n`realizedVol`, `trailingReturn`, `drawdown`, `PEER_GROUPS`\n\n</details>\n\nThe full architecture, feature reference and configuration guide follow below.\n\n---\n\nJARVIS is a desktop assistant whose intelligence runs entirely on your own\nmachine. Speech recognition, language understanding, retrieval, and vision all\nexecute locally. No model API keys, no network calls to a model provider, and no\nconversation data leaving the device.\n\nSome features ask the outside world for facts it alone has — a share price, a\nheadline, the state of a blockchain. Those calls send a ticker or an address and\nnothing else: no transcript, no memory, no conversation. Everything else works\nwith the network unplugged.\n\nIt presents as a frameless, transparent 3D visualizer that floats above your\ndesktop, listens continuously, and answers by voice. A companion Android app\nextends the same interface and control surface to a paired phone over Wi-Fi,\nand a spoken \"mirror my phone\" puts that phone's live screen on the desktop\nwith touch and keyboard control.\n\n---\n\n## Contents\n\n- [Install](#install)\n- [What makes this different](#what-makes-this-different)\n- [Architecture](#architecture)\n- [Feature reference](#feature-reference)\n  - [Listening to a video](#listening-to-a-video)\n  - [Connection queries](#connection-queries)\n- [Calendar and meetings](#calendar-and-meetings)\n- [On-chain intelligence](#on-chain-intelligence)\n- [Web search](#web-search)\n- [Retrieval engine](#retrieval-engine)\n- [Evaluation](#evaluation)\n- [Android companion](#android-companion)\n- [Screen mirror](#screen-mirror)\n- [Globe](#globe)\n- [Mineral intelligence](#mineral-intelligence)\n- [Routing](#routing)\n- [Music](#music)\n- [Installation](#installation)\n- [Running](#running)\n- [Configuration](#configuration)\n- [Network ports](#network-ports)\n- [Troubleshooting](#troubleshooting)\n- [Known limits](#known-limits)\n\n---\n\n## What makes this different\n\nMost assistants send your microphone to a datacenter. This one does not.\n\n| Capability | Typical assistant | JARVIS |\n| --- | --- | --- |\n| Speech to text | Cloud ASR | Whisper via transformers.js, local |\n| Language model | Hosted API | Gemma 3 via Ollama, local |\n| Embeddings | Hosted API | nomic-embed-text, local |\n| Vision / screen reading | Cloud vision | Gemma 3 multimodal, local |\n| Conversation storage | Provider servers | Local disk only |\n| Works without internet | No | Yes, except live data lookups |\n| Per-query cost | Metered | Zero |\n\nOutbound traffic is limited to fact lookups that cannot be answered locally:\nkeyless web search when a query is search-shaped, quote and news endpoints, and\nblockchain RPC. Each sends only the subject of the question — a ticker, a\nsearch string, an address. Inference never leaves the machine.\n\n---\n\n## Architecture\n\n### System overview\n\n> **Diagram:** [System overview →](docs/ARCHITECTURE.md#1-system-overview)\n\n| Colour | Layer |\n| --- | --- |\n| Purple | Renderer. Visualizer, voice loop, retrieval, intent routing |\n| Cyan | Electron main. Service supervision, IPC, LAN listeners |\n| Green | Local inference. Everything bound to loopback |\n| Light green | Android companion, reached over Wi-Fi |\n| Red | External network. The single outbound path |\n| Amber | Local persistence |\n\nThe Electron main process (`electron.js`) supervises every local service and\nrestarts them on failure. The renderer owns the visualizer, voice loop, and\nretrieval.\n\n### Voice pipeline\n\n> **Diagram:** [Voice pipeline →](docs/ARCHITECTURE.md#2-voice-pipeline)\n\nTwo feedback loops are load-bearing. The dashed edges back into the VAD and echo\nguard are what stop JARVIS transcribing its own voice, and both are required:\nthe `ttsActive` gate leaks because synthesised audio bypasses Chromium echo\ncancellation, so the text-level guard catches what the gate misses.\n\n**Speech to text is JavaScript, not Python.** `server/stt-server.mjs` runs\nWhisper (`onnx-community/whisper-base.en`, q8) through transformers.js on\nONNX Runtime, spawned on Electron's own Node with `ELECTRON_RUN_AS_NODE=1`.\nThe previous implementation shelled out to\n`uv run --python 3.12 --with faster-whisper`, which meant every machine needed\nuv, a Python toolchain and a package download before voice input worked at all\n— and silently had no voice input if any of that was missing. The wire protocol\nis unchanged, so anything already speaking to `:8770` needs no edit:\n\n| Client sends | Server does |\n| --- | --- |\n| binary frames | raw 16 kHz mono PCM16, appended to a buffer |\n| `{\"type\":\"end\"}` | transcribe what has accumulated, reply, clear |\n| `{\"type\":\"reset\"}` | discard without transcribing |\n| | replies `{\"type\":\"final\",\"text\":\"…\",\"ms\":123}` |\n\nThree gates sit in front of the model, because a language model asked to\ndescribe silence will invent words rather than return nothing:\n\n1. **Under 250 ms of audio** — dropped without transcribing. Note that this\n   path sends **no reply at all**, matching the old Python server's behaviour\n   exactly. A client that waits for one `final` per `end` will wait forever on\n   a blip, so drive it on arrival rather than on a strict request/response\n   pairing.\n2. **RMS below 0.0015** — the window is effectively silence. Returns `\"\"`\n   without invoking the model.\n3. **The artefact list** — a whole-utterance match against `\"you\"`,\n   `\"thank you\"`, `\"thanks for watching\"`, `\"bye\"`, `\".\"` and similar is\n   blanked after the fact. Matching is on the *entire* utterance, so a real\n   sentence that merely contains \"thank you\" is untouched.\n\nRequests are serialised. ONNX Runtime is not reentrant for a single session, so\ntwo overlapping transcriptions would interleave into each other's output;\nthey queue instead, which preserves the ordering everything downstream depends\non.\n\nMeasured on this machine, 20 Aug 2026, `whisper-base.en` at q8, after the model\nis cached:\n\n| Input | Transcribe | Result |\n| --- | --- | --- |\n| 3.38s utterance | 844 ms | exact |\n| 5.00s window | 1060 ms | exact |\n| 5.00s window | 1407 ms | exact |\n| 3s digital silence | — | `\"\"`, not transcribed |\n| 3s low room noise | — | `\"\"`, not transcribed |\n\nThe first run downloads the weights into the transformers.js cache. Every run\nafter that is fully offline.\n\n### Process supervision\n\n> **Diagram:** [Process supervision →](docs/ARCHITECTURE.md#3-process-supervision)\n\nTwo invariants are encoded above. **Only kill what you spawned:** an Ollama the\nuser started is reused and left running on quit. **Preload is not optional:**\nOllama's default `keep_alive` is 5 minutes, so without the 60-minute preload the\nfirst question after any idle period pays a multi-second cold load.\n\nOn launch, `electron.js` starts and monitors:\n\n| Service | Behaviour on failure |\n| --- | --- |\n| Ollama | Reuses an existing instance if one is running; otherwise spawns `ollama serve`, waits for readiness, preloads the model with `keep_alive: 60m`, and auto-respawns after 15s |\n| Whisper STT | Runs on Electron's own Node via `ELECTRON_RUN_AS_NODE`, no Python. Auto-respawns after 15s; port conflicts exit harmlessly and the 30s watchdog reclaims the port if its holder later dies |\n| Phone bridge | Token-authenticated HTTP listener |\n| Companion bridge | WebSocket server plus mDNS advertisement |\n| Downloads watcher | chokidar; new documents are OCR'd and ingested |\n| Clipboard monitor | Scans for leaked secrets, reports masked hints only |\n| Active window tracker | 10s cadence |\n| Finance service | 60s quote cadence |\n\nServices that JARVIS spawns are terminated on quit. Services it merely reused,\nsuch as an Ollama you started yourself, are left running.\n\n---\n\n## Feature reference\n\n### Voice\n\nOpen conversation mode. Every transcript is routed and answered; no wake word\nis required. Leading \"Jarvis\" and common mis-hearings are stripped.\n\n- Always-on microphone with adaptive noise-floor VAD\n- Deliberate microphone selection, excluding loopback devices such as Stereo Mix\n  which would otherwise capture JARVIS listening to itself\n- Streaming speech: each completed sentence is spoken during token generation,\n  cutting time-to-first-word from roughly 5-10s to 1-2s\n- Echo guard using word-overlap against recently spoken text, because the\n  synthesis-active flag alone is known to leak\n- Self-healing microphone recovery with a 5s watchdog for eventless device death\n\n### Listening to a video\n\nPaste or say a YouTube link and JARVIS opens it, listens to it, and says what\nis being said.\n\n```\nhttps://www.youtube.com/watch?v=...     start listening\nwhat did he say                         replay the last ~60 words, from the buffer\nstop listening                          stop, and report how many passages were spoken\n```\n\nThe link is checked **before** intent detection. A bare URL matches no intent,\nso it used to fall through to the model and come back as \"I encountered an\nerror processing your request\".\n\nTwo properties the design exists to protect:\n\n**The words are the speaker's.** No model rewrites the transcript on its way to\nthe speakers. `transcriptEngine.js` is string comparison with fixed rules, so\nwhat JARVIS says is what Whisper heard, minus the repetition the window overlap\ncreates. Summarising is a separate thing you have to ask for.\n\n**The audio comes from the player, not the room.** Capture is scoped to the\nplayer frame through `getDisplayMedia` rather than system loopback. That is\nwhat lets JARVIS speak while still listening — system capture would hear its\nown voice and transcribe it back into the queue, which compounds into nonsense\nwithin a few turns.\n\n| Constant | Value | Why |\n| --- | --- | --- |\n| Window | 5000 ms | The floor on lag: Whisper cannot start until it has one. Measured at 1.06–1.41s to transcribe, landing end-to-end around two to three seconds behind the source |\n| Overlap | 700 ms | A word spanning the cut survives whole in one of the two windows. The engine removes the duplication this creates |\n| Sample rate | 16 kHz mono | What Whisper wants; the `AudioWorklet` in `youtube-pcm-processor.js` does the conversion |\n\nWindows are transcribed concurrently but spoken strictly in order —\n`transcriptEngine.js` holds a window until every lower sequence number has\narrived, because two overlapping utterances are unintelligible and a\nre-ordered transcript is worse than a late one.\n\n### Connection queries\n\n`correlationEngine.js` answers how things in the ontology relate. This is the\nsurface that lets a sentence reach it.\n\n```\nhow is apple connected to foxconn        paths between two entities\nwhat do apple and foxconn have in common\nwhat is connected to apple               everything adjacent to one entity\nshow me everything related to apple\nwhat happened around the same time       events that co-occur\nwhat are the bridges                     articulation points — what holds the graph together\n```\n\nThree outcomes are deliberately kept distinct all the way to the spoken\nsentence, because collapsing them is the tempting bug — \"no connection found\"\nand \"I have never heard of either of these\" sound identical to a listener and\nmean opposite things:\n\n| Case | Answer |\n| --- | --- |\n| The graph has never heard of the entity | Cede — the web may know |\n| The graph holds both, and no path exists | A **finding**, said plainly |\n| The graph holds a path | Report it, with sources |\n\n\"Connect\" is the most overloaded verb in this assistant: it already means pair\nmy phone, link my calendar, and go online. Every one of those matchers sits\nfurther down `detectIntent` than this one, so an over-broad pattern here would\nnot merely mis-answer a graph question — it would silently break phone pairing.\nTwo guards prevent that. A connection question must name two *things*, so a\nbare pronoun or a possessive (\"my\", \"our\", \"their\") disqualifies it; and any\nutterance containing `phone`, `calendar`, `wifi`, `bluetooth`, `wallet`,\n`spotify` and the rest of that list is refused outright before parsing starts.\n\n### Visualizer\n\n- Icosahedron with Perlin-noise vertex displacement driven by live FFT\n- Bass, mid, and treble bands weighted separately\n- Frequency-mapped colour, with time-based hue cycling when idle\n- Transparent frameless window that floats over other applications\n- F2 toggles between orb-only and full HUD\n\n### Feedback\n\n> **Full write-up:** [docs/FEEDBACK.md](docs/FEEDBACK.md)\n\nConfirmation on the channels the machine actually has. Nothing vibrates on a\ndesktop — `navigator.vibrate` is callable in Electron and moves nothing,\nbecause there is no motor — so a press is carried by a short animation and a\nsynthesized click, and the vibration channel reports itself unavailable rather\nthan pretending.\n\n- Every heard utterance gets an instant 8 ms acknowledgement, before the answer\n  exists. Speech has no click of its own, so the gap before the first word back\n  was otherwise indistinguishable from not being heard\n- Every unprompted event — a phone notification, a download, a whale transfer,\n  a price alert — carries the same rising marker\n- Destructive actions get the one effect with a **gap** in it: pulse, pause,\n  pulse, falling and low. Every other effect is a single gesture, so it cannot\n  be mistaken for a confirmation. A warning falls; a success rises\n- Audio is synthesized rather than sampled: no binary assets, nothing to fetch,\n  and the sound is a table of numbers that can be reviewed and tested\n- `prefers-reduced-motion` gates the animation only. It is a statement about\n  motion, not about feedback, and silencing the audio would strip the\n  non-visual confirmation from the user who just asked for less movement\n- The paired phone has a real motor, and gets the same vocabulary mapped onto\n  Android's own haptic API — probed by primitive, not by API level\n\n### System control\n\n- Application launch through an allowlist\n- Volume, brightness, media keys, power state\n- Wi-Fi scan, connect to saved profiles, disconnect, and measured link\n  diagnostics reporting real latency and packet loss\n- File operations, clipboard read and write\n- Windows Settings deep links\n- Live CPU, RAM, uptime, and active window telemetry in the HUD\n\n### Files and folders\n\nCreate folders and files by voice, anywhere inside your own user folders.\n\n```\n\"create a folder called notes on the desktop\"\n\"make a file called todo.txt in documents\"\n\"make a file called shopping list saying milk and eggs\"\n```\n\n- Parsing is rule-based, never model-driven. These commands write to disk, and\n  a model deciding what \"create a file called that thing\" means is a model\n  deciding what to name a file on your Desktop\n- Confined to Desktop, Documents, Downloads, Pictures, Videos and Music.\n  Containment is checked at a path-separator boundary, so `~/Desktop-evil` is\n  not treated as `~/Desktop`\n- An existing file is never silently overwritten\n- Executable types (`.exe`, `.bat`, `.vbs`) are refused. Source files are not —\n  writing `.js` is the point, and a `.js` is only dangerous when something runs\n  it, which JARVIS never does\n- A name that does not survive sanitising is reported, not replaced with a\n  fallback. A file called `untitled` appearing because a name was misheard is\n  worse than being told the name was not understood\n\n### Writing code\n\n```\n\"open vscode and write a binary search in java\"\n\"write a quicksort in python on the desktop\"\n```\n\nGemma writes the file contents; the filename, directory and language are fixed\nby rule before the model is asked anything. Java and C# get PascalCase names\nbecause those languages resolve the type by filename. The file opens in VS Code\nwhen an editor was named, and VS Code is resolved to the real `Code.exe` rather\nthan shelling out to the `code` shim.\n\nIf the model returns nothing, no file is created — an empty file reported as\nsuccess would be a lie.\n\n### Alarms and timers\n\n```\n\"set a timer for 40 minutes\"\n\"set an alarm for tomorrow at 2:30 pm\"\n\"set an alarm for an hour and a half\"\n\"set a timer for twenty minutes to check the oven\"\n\"cancel the timer\"   ·   \"what timers do I have\"   ·   \"stop\"\n```\n\n- Scheduled to the exact instant rather than polled, so a 30-second timer works\n- Persists across restarts, and a missed alarm is announced on return rather\n  than silently dropped\n- Fires with speech, a synthesised tone and a notification; each is independent,\n  so a suspended AudioContext does not suppress the other two\n- Repeats every five seconds until dismissed, stopping after two minutes\n- An unresolvable time is refused. \"Set a timer for the pasta\" asks how long\n  instead of choosing a duration\n\n#### Waking up\n\n```\n\"wake me up tomorrow morning\"        ·   \"wake me up in the morning\"\n\"wake me up every day at 6\"\n\"wake me up at 6 tomorrow morning\"\n\"set an alarm on Monday at 9am\"\n```\n\nNamed parts of the day resolve to a time: morning 07:00, breakfast 08:00, noon\n12:00, lunch 13:00, afternoon 15:00, evening 19:00, dinner 20:00, night 22:00.\n\nThis is the one place the parser uses a default, which sits against the\nno-guessing rule the rest of the file keeps. The rule survives in the only way\nthat matters: **an assumed time is always spoken back concretely** — *\"Alarm set\nfor 7:00 am tomorrow. I have taken morning as 7:00 AM — say a different time if\nyou would rather\"* — so a wrong default costs one sentence rather than being\ndiscovered at 7am.\n\nTwo details that are easy to get backwards:\n\n- **A day-part beside a number settles the meridiem.** \"Wake me at 6 tomorrow\n  morning\" is 06:00. Without this it takes the roll-forward branch and lands on\n  18:00, the one reading the sentence rules out.\n- **\"Wake me\" implies AM between 4 and 11.** Bare hours roll forward everywhere\n  else, but waking is a morning act; \"wake me at seven\" said at 9am means 07:00\n  tomorrow, not 19:00 tonight.\n\nRecurrence — `every day`, `daily`, `every weekday`, `every Monday` — is carried\non the alarm and re-armed by the scheduler after each firing. A repeat advances\nby whole calendar days rather than by 86,400,000 milliseconds, because those\ndiffer across a DST change and a 7am alarm quietly becoming a 6am alarm twice a\nyear is a bug discovered at 6am.\n\n#### The daily routine\n\n```\n\"wake me up at 7 every day\"           ·   \"move lunch to two\"\n\"remind me every day at 8 to have breakfast\"\n\"what's left today\"                   ·   \"what is my routine\"\n\"no dinner reminder\"                  ·   \"set my morning walk for 7\"\n```\n\nNamed slots — wake, walk, breakfast, study, lunch, dinner — each firing daily\non its own. Seeded with a starting template on first run, because a routine that\nstarts empty needs six spoken commands before it does anything.\n\n- **The routine owns only its own alarms.** Entries are tagged `source:\n  'routine'`, so editing lunch never cancels the alarm you set for a meeting.\n- **\"What's left today\" reads the live schedule,** not the template, so it stays\n  true after you move a slot mid-morning.\n- Slots are matched longest-alias-first, so \"set my morning walk\" edits the walk\n  slot rather than the wake slot.\n\n> [!IMPORTANT]\n> **A wake-up alarm belongs on your phone, not here.** The desktop scheduler\n> runs in the renderer: if JARVIS is closed at 6am, nothing fires, and nobody\n> leaves a desktop awake and unlocked overnight to be woken by it.\n>\n> With the companion paired, every alarm is mirrored to Android and scheduled\n> through `AlarmManager.setAlarmClock()`, which survives a closed app, a locked\n> screen and Doze. Both copies ring; two alarms is a far better failure than\n> none. See [Android companion](#android-companion).\n\n> **Diagram:** [Waking someone up →](docs/ARCHITECTURE.md#9-waking-someone-up)\n\n### Calendar and meetings\n\n```\n\"connect my calendar\"\n\"what are my meetings\"        ·   \"when is my next meeting\"\n\"schedule a meeting\"\n\"create a meet room\"\n\"I know about the meeting\"\n```\n\nScheduling is a conversation. JARVIS asks what to call it, what it is about,\nwhen, how long, and who to invite, then reads the whole thing back before\ncreating anything:\n\n```\nYou    schedule a meeting\nJARVIS Of course, Sir. What should I call this meeting?\nYou    project review\nJARVIS \"Project review\". And what is this meeting about?\nYou    Q3 roadmap and hiring\nJARVIS Noted. When should I schedule it, Sir?\nYou    tomorrow at 3 pm\nJARVIS 3:00 PM tomorrow. How long should it run?\nYou    an hour\nJARVIS Anyone to invite? Say their email addresses, or \"no one\".\nYou    no one\nJARVIS \"Project review\", 3:00 PM tomorrow for 1 hour, ending 4:00 PM.\n       About: Q3 roadmap and hiring. Shall I create it, Sir?\nYou    yes\nJARVIS Creating it now, Sir.\n```\n\n- Background awareness escalates rather than repeats: one warning at 30 minutes,\n  then 10, 5 and 1, each phrased differently. Eight identical five-minute\n  reminders train you to ignore the one that matters\n- The calendar is fetched every five minutes, but checked against local clocks\n  every twenty seconds, so alert timing does not depend on when a network call\n  landed\n- A failed poll keeps the last known schedule. Announcing \"no meetings today\"\n  because one fetch failed would be a fabrication\n- Spoken email addresses work — \"john at example dot com\" — and an address that\n  cannot be parsed is refused rather than quietly inviting nobody\n- The model is asked for exactly one thing: a better title when you gave a\n  generic one, which you then confirm. Nothing that lands in your calendar is\n  model-decided\n\n> [!IMPORTANT]\n> Creating a **Google Meet link** requires a paid Google Workspace account. On a\n> personal Gmail the event is created without one — the API returns no link and\n> no error — and JARVIS says so rather than implying a link exists.\n\n### Running in the background\n\n```\n\"start with windows\"     ·   \"don't start with windows\"\n\"hide yourself\"          ·   click the tray icon to bring it back\n```\n\nClosing the window hides JARVIS to the tray rather than quitting, so alarms\nstill fire and meetings are still watched. Only **Quit Jarvis** from the tray\nmenu actually exits.\n\n- Autostart is registered through the OS login-items API, and starts hidden —\n  someone who wanted a window on every boot would not need autostart\n- A **single-instance lock** is claimed before anything else. This is not a\n  nicety: JARVIS spawns a speech server, a TTS server, a vision server and\n  Ollama on fixed ports, and runs a microphone listener and an alarm scheduler.\n  Autostart plus a manual launch is the ordinary case on day one, and a second\n  instance would fight the first for all of it. Launching again surfaces the\n  running window instead\n- Autostart can only be registered from an **installed build**. In a\n  development run `process.execPath` is `electron.exe`, and registering there\n  would put a bare Electron runtime in your startup that launches and shows\n  nothing. JARVIS refuses and says so, rather than leaving an entry that looks\n  installed and does nothing\n\n### What JARVIS can reach\n\nJARVIS runs as **you**, not as Administrator, and that is deliberate.\n\nUnelevated already covers everything you do day to day: your files, launching\nprograms, reading the process list and network state, the microphone and the\nscreen. What it does not cover is writing to `Program Files`, the Windows\ndirectory, or another user's data — none of which JARVIS has a reason to touch.\n\nRunning it elevated would mean a misheard word, or anything that reached the\nrenderer through a web result, inherits Administrator. JARVIS acts on speech\nrecognition, which mishears; that is the whole reason the file commands are\nrule-parsed and the write path is allowlisted in the first place.\n\nTo widen its reach, name the directories:\n\n```\nJARVIS_EXTRA_ROOTS=D:\\Projects;C:\\Work\n```\n\nAbsolute paths, semicolon-separated. A relative entry is dropped with a warning\nrather than resolved against the working directory, because a typo'd `Work`\nbecoming `<cwd>/Work` would grant a directory nobody chose. Point it at a drive\nroot if you genuinely want that — the difference that matters is that it is\nyour decision, written down, rather than an implicit consequence of enabling\nautostart.\n\n### Screen and documents\n\n- Screen reading through Gemma 3 vision, fully offline. The captured question is\n  passed through, so \"what error is showing\" reaches the model intact\n- Optional Unlimited-OCR server for dense text\n- Downloads are watched, OCR'd, and ingested into memory automatically\n- Your Android screen, live on the desktop with touch and keyboard control —\n  see [Screen mirror](#screen-mirror). \"take a phone screenshot\" grabs the\n  current frame and describes it through the same local vision path\n\n### Knowledge\n\n- Hybrid retrieval over local memory, detailed below\n- Keyless web search with a three-provider failover chain — DuckDuckGo HTML,\n  DuckDuckGo Instant Answer, then Wikipedia — injected as cited context for\n  search-shaped queries. The chain exists because the HTML endpoint starts\n  serving a captcha once an IP is flagged, which silently emptied every result\n- Finance watchlist with crossing alerts. Read-only by design: no order\n  placement code exists anywhere in the project\n\n### Markets and quantitative analysis\n\nEvery number here is computed by tested code from measured data. The language\nmodel is never asked to calculate a financial figure, because it cannot be\ntrusted with one and a wrong figure stated confidently is worse than no answer.\n\n- Live quotes with day change, resolved name to ticker\n- **Single security** — `src/js/services/quant.js`: annualised return,\n  volatility, Sharpe, Sortino, maximum drawdown, beta and alpha against a\n  benchmark, correlation, R², historical VaR, expected shortfall, information\n  ratio, tracking error, up/down capture, and Black-Scholes pricing with greeks\n- **A book of holdings** — `src/js/services/portfolio.js`: covariance,\n  risk contribution, risk parity, minimum variance, maximum Sharpe,\n  diversification ratio, and portfolio-level VaR and expected shortfall\n- **A name against its peers** — `sectorMove.js`: how much of a move the sector\n  explains and how much belongs to the company\n- SEC filings through a pinned fetch guard, `edgarGuard.js`\n- **Disclosure venues beyond EDGAR** — see below\n- Headlines from Google News with Bing failover, keyless\n\nThree choices in here are load-bearing rather than incidental.\n\n**VaR is historical simulation, never parametric.** The Gaussian form\n(`mu - z*sigma`) is one line shorter and wrong in exactly the situation the\nnumber exists for: return distributions have fat tails, so it understates the\n99th percentile precisely when that matters. The implementation reads the\nquantile off the observed returns and refuses fewer than 30 observations rather\nthan resting a 99% loss estimate on a single bad day.\n\n**R² gates the interpretation of beta and alpha.** Measured against the S&P 500\nover 250 sessions, Micron's R² is 0.283 — the benchmark explains 28% of its\nvariance, so its beta of 3.29 and alpha of 170% are weakly determined and are\nreported as such. Without that gate the assistant would speak a 170% alpha as\nthough it meant something.\n\n**Dollar weight is not risk weight.** A 60/40 book is roughly a 94% equity-risk\nbook, which is arithmetic rather than opinion: it is what `riskContributions()`\nreturns for those weights, and it is checked against that case in the tests.\n\n---\n\n### Peer and portfolio analysis\n\nTwo questions the single-security metrics cannot answer, each with its own\nmodule and voice intent.\n\n`SECTOR_QUERY` — *\"decompose Micron's move\"*, *\"break down the memory sector\"*.\nThe move is split into the part the peer group explains (beta times the sector's\nmove) and the part that belongs to the company. Measured 29 July 2026: Micron\nfell 9.94% while its peer group fell 4.96%; with a beta of 0.91 that leaves\n-5.40% as its own, while Western Digital's flat day was +4.77% of relative\nstrength. Group mode ranks every member by that residual, because the largest\nfaller is often just the highest beta.\n\n`PORTFOLIO_QUERY` — *\"how risky is my watchlist\"*, *\"what would risk parity\ndo\"*, *\"minimum variance weights for MU, SNDK, WDC\"*. Holdings are aligned by\ndate, never by index, because two venues do not share a trading calendar. The\ncovariance inverse refuses a singular matrix rather than returning the enormous\noffsetting weights that a collinear book produces, and a short position in the\nminimum-variance solution is surfaced rather than clipped — \"hold none\" and\n\"sell short\" are different instructions.\n\nBoth modules state their limits instead of implying them with a null. A holding\nthat only listed three weeks ago truncates every other series to match; the\nanalysis names it and says that dropping it would widen the window.\n\n---\n\n### Disclosure venues beyond EDGAR\n\nThe memory industry is mostly not American, so an EDGAR-only assistant answers\n\"no filings\" for half of it and is wrong every time. `edgarGuard.js` carries a\nvenue registry naming where each issuer's filings actually live, and every\nentry records the date it was checked rather than a permanent claim.\n\n| Issuer | Venue | Reachable how |\n| --- | --- | --- |\n| Micron, Sandisk, Western Digital | SEC EDGAR | Atom feeds, keyless, declared User-Agent |\n| SK hynix | Both — SEC since 9 Jul 2026, and DART | 6-K and 424B4 on EDGAR; business reports on DART |\n| Samsung Electronics | Korea's DART | Open API, free key required |\n| CXMT | Shanghai STAR Market since 27 Jul 2026 | HTML announcements only |\n| YMTC | None — privately held | No public filings of any kind |\n\nProbed live on 30 July 2026, because pasted endpoint lists have been wrong\nrepeatedly in this project:\n\n- **HKEX** publishes real RSS. Two feeds are wired and return 25 parsed items\n  each. They are exchange-wide, not per-company.\n- **DART** advertises no RSS anywhere. Its Open API is real and returns clean\n  JSON, but rejects unregistered callers with `{\"status\":\"010\"}`. The key is\n  free, so this follows the Alchemy and Helius pattern: dormant without one,\n  and it says so rather than failing obscurely.\n- **SSE** serves announcements as HTML only — no feed, no public API.\n- **SZSE** does not complete a fetch from here at all.\n\nThe registry exists because of a specific failure. Its first version asserted\nthat CXMT was \"a private Chinese DRAM maker with no US listing.\" That was true\nwhen written and false three days later: CXMT listed on the STAR Market on\n27 July 2026, rose 466% on debut, and now trades as `688825.SS`. A registry\nthat hardcodes a company's status will state a falsehood the moment that status\nchanges, so entries carry a venue and a checked-on date instead.\n\n---\n\n## On-chain intelligence\n\nRead-only by construction. Only a hard allowlist of JSON-RPC read methods is\never sent; there is no signing code, no transaction construction, and no private\nkey handling anywhere in the project.\n\nThe governing rule is the same one the quant engine follows: **the chain is the\nsource of truth, and anything the chain cannot prove is not claimed.** An\naddress with no ENS name stays an address. No exchange or entity is ever named\nfrom a guess.\n\n### Address and contract reads\n\n- Native and ERC-20 balances, gas, nonce, across Ethereum, Arbitrum, Base,\n  Optimism, Polygon and BNB Chain\n- ENS forward and reverse resolution, implemented from a pure keccak-256 in\n  `src/js/services/keccak.js` and verified against public vectors\n- Transaction decode: status, native value, and every ERC-20/721 Transfer in the\n  receipt, resolved to symbols and exact decimal amounts\n- Contract classification through ERC-165 `supportsInterface` and an ERC-20\n  probe. Classification only; this is not a vulnerability auditor\n- Cross-chain portfolio. With an Alchemy key this returns everything a wallet\n  holds, priced; without one it falls back to scanning known tokens per chain\n- Solana wallet assets and recent activity through Helius, including native SOL\n  balance and USDC/USDT supply\n\n### Real-time whale stream\n\nA websocket subscription to new block headers. Each confirmed block is scanned\nfor large movements, and everything announced is a fact read out of that block.\n\n> **Diagram:** [Real-time whale stream →](docs/ARCHITECTURE.md#4-real-time-whale-stream)\n\n- **Token flows, not just native.** Most large value on Ethereum moves as\n  stablecoins. Sampled over five live blocks: 0-2 native ETH whales versus 16\n  token movements\n- **Token decimals are verified on chain** with a `decimals()` call before any\n  amount is decoded. Reading a 6-decimal token as 18 turns $4M into $4\n- **One transaction is one movement.** An arbitrage route through several pools\n  emits the same tokens repeatedly; a live drill caught the same 14,050 WETH\n  being announced three times. Transfers are now grouped per transaction, the\n  source is the address that only sends, the destination the one that only\n  receives, and the hop count and any round trip are stated\n- **Ranked across assets by measured USD.** 100 ETH has more raw units than\n  4,000,000 USDC, so unit ordering picks the wrong headline\n- **Stablecoin issuance.** A mint is a Transfer from the zero address and a burn\n  is one to it, so supply changes need no label database. Live-verified against\n  mainnet: a 5,414,317 USDC mint, and in one hour DAI net +6.9M against USDC net\n  -6.0M\n- **Address context on both ends**: ENS name, contract or wallet via\n  `eth_getCode`, transactions sent, ETH held. The display carries full addresses\n  and the transaction hash; speech carries the readable form\n- **Operational hardening**: exponential backoff with jitter, 30s heartbeat and\n  90s silence detection, gap detection with in-order backfill through the same\n  code path as live blocks, and bounded dedup so memory stays flat\n\n### What is deliberately not built\n\n| Asked for | Why not |\n| --- | --- |\n| Exchange labels (\"from Binance\") | Not on-chain data. It requires a proprietary attribution database; naming a wallet on a guess is the one thing that would make these alerts untrustworthy. Arkham is supported *with your own key*, and its labels are spoken attributed |\n| Wallet classification by the model | A 4B model producing \"institutional accumulator\" is a confabulated verdict, not analysis |\n| Mempool alerts | Pending transactions get dropped and replaced. An alert about a transaction that never lands is misinformation |\n| Global Solana whale scanning | Measured: the Helius socket delivers over 200 token-program events in 15 seconds. Filtering that firehose is not something this machine does while also running voice |\n| Bitcoin monitoring | A different data source entirely, and none is connected |\n\n### Provider keys\n\nAll optional. JARVIS runs keyless and degrades honestly, saying which chains it\ncan read and why one is missing.\n\n| Key | Unlocks | Without it |\n| --- | --- | --- |\n| `ALCHEMY_API_KEY` | Full wallet holdings with prices, faster RPC, keyed websocket | Public endpoints, known-token scanning only |\n| `HELIUS_API_KEY` | Solana wallets, activity, stablecoin supply | No Solana |\n| `DUNE_API_KEY` | Aggregate analytics: top holders, USD-priced flows | Those queries state the key is needed |\n| `ARKHAM_API_KEY` | Entity labels, spoken with attribution | Addresses stay addresses |\n| `GOOGLE_MAPS_API_KEY` | Globe: country/state/street geocoding, place photos, weather, air quality | Cities only, from the bundled gazetteer; Nominatim, Wikipedia and USGS still work |\n| `LUMA_API_KEY` | Globe: events from one Luma calendar (needs Luma Plus) | No events layer; every other globe feature is unaffected |\n| `AVIATIONSTACK_API_KEY` | Globe: real flight routes with origin and destination | Live aircraft still shown from OpenSky, but a route query reports traffic over the corridor rather than named flights |\n| `WINDY_WEBCAMS_API_KEY` | Globe: ~70,000 opt-in public webcams worldwide | Camera layer still covers London and Singapore road cameras |\n| `PARSE_API_KEY` | Globe: refreshing the company ranking, and per-company revenue history. **Metered — one credit per uncached call** | The 11,222-company crawl in `data/` still draws in full; it is a snapshot rather than today's prices |\n\nNetworks are **discovered, not assumed**: each candidate endpoint must return\nthe chain ID it claims before it is used. On the free Alchemy tier this\ncorrectly rejects Optimism and Polygon, which answer 403, rather than failing\nlater with a confusing error.\n\nMeasured provider limits that shape the design: Alchemy's free tier caps\n`eth_getLogs` at 10 blocks, 1rpc at 50, and drpc handles a few hundred but\nrefuses under load. Wide-range log queries are therefore chunked at 50 blocks\nacross the keyless pool, and any chunk that fails is reported rather than\nsilently dropped — \"nothing happened this hour\" and \"I could only read half the\nhour\" are different answers.\n\n### Fund tracing\n\n`src/js/services/tracer.js` implements the deterministic Approximate\nPersonalized PageRank from the TRacer paper, with its tracing-tendency and\nweighted-pollution strategies, plus structural pattern detection (amount\nconsistency, cycles, consistent chains). It reports pattern presence, never a\nverdict. The algorithm is tested and works; live tracing needs address history,\nwhich public RPC cannot enumerate, so it awaits an Etherscan-family key.\n\n---\n\n## Web search\n\n`webSearch.js` (main process) and `src/js/services/webSearchIntent.js`\n(renderer) answer questions from the live internet. Split along the process\nboundary, not by topic: the renderer cannot fetch these origins because CORS\nblocks it, and Rollup cannot take named imports from a CommonJS module.\n\n### Why it exists\n\nThere was no web search. `search about elon musk` was classified `TYPE_TEXT` —\nthe dictation intent — so asking for a search typed the words into whatever\nwindow had focus. Anything that instead reached `AI_COMMAND` was answered by\nthe local model, which has no network access. It did not decline; it invented:\n\n```\n\"search about elon musk\"       -> \"...recognized as a trillionaire in US dollars .\"\n\"list latest vulnerabilities\"  -> \"According to OpenCVE, Google released Chrome 151\n                                   with patches for 382 vulnerabilities\"\n\"latest cve number of chrome\"  -> \"According to Google's Chrome Releases,\n                                   CVE-2026-15905 is the latest critical vulnerability\"\n```\n\nThose citations are fabricated. A fabricated CVE number is worse than a refusal.\n\n### Pipeline\n\n> **Diagram:** [Pipeline →](docs/ARCHITECTURE.md#5-pipeline)\n\n### Providers are measured, not assumed\n\nHTML scraping was tried first and does not work:\n\n| Endpoint | Result |\n| --- | --- |\n| `html.duckduckgo.com` | HTTP 202 + challenge page, 0 results |\n| `lite.duckduckgo.com` | HTTP 202 + challenge page, 0 results |\n| `mojeek.com` | HTTP 200, body is an altcha CAPTCHA |\n| `searx.be` | HTTP 200, JSON output disabled |\n\nThe first DuckDuckGo query of a session usually succeeds, which makes this\nespecially deceptive: it looks like it works until it is used twice.\n\nKeyless general open-web search is not available in 2026. Google's Custom Search\nJSON API closed to new signups in 2025 and shuts down on 1 Jan 2027; Bing's\nSearch APIs were retired on 11 Aug 2025; Brave withdrew its free tier. So the\nproviders below are the keyless endpoints that **are** official, each measured\nbefore being added:\n\n| Provider | Measured | Intent |\n| --- | --- | --- |\n| DuckDuckGo Instant Answer | 361 ms | general (sourced abstract) |\n| Wikipedia | 541 ms | general (encyclopedic) |\n| Google News RSS | 642 ms | news, anything current |\n| Hacker News (Algolia) | 831 ms | discuss |\n| crates.io | 1147 ms | code (Rust) |\n| Open Library | 1259 ms | book |\n| NVD | 1462 ms | security |\n| GitHub repos | 1523 ms | code |\n| Stack Overflow | 1555 ms | code, discuss |\n| arXiv | 1973 ms | academic |\n| npm | 2078 ms | code (JS) |\n| Brave | — | general, only with `BRAVE_API_KEY` |\n\nProbed and **rejected**: GitHub code search (HTTP 401, needs auth), Semantic\nScholar (HTTP 429), Reddit (HTTP 403 to datacentre traffic).\n\n### Gather, don't race\n\nProviders here are complementary rather than interchangeable — a Rust question\nwants the crates.io entry *and* the GitHub repo *and* the Stack Overflow thread\n— so `gatherAll` collects everything that arrives inside the budget instead of\nresolving on the first success.\n\nThe early exit counts **providers, not results**. Counting results was tried\nfirst and silently destroyed the feature: Google News alone returns six, which\nsatisfied a result quota instantly and ended the query before any other source\nreplied — measured as `answered 1: google-news` on every single query, a\nfirst-wins race wearing a gather's clothes.\n\n`rrfFuse` merges the ranked lists by position only (k=60), because GitHub stars,\nStack Overflow votes and news recency cannot be normalised against each other.\nProvider weights are derived from the query, after plain RRF put npm's\n`uniffi-bindgen-react-native` first for *\"best rust crate for async runtime\"* —\nan off-target index's rank-1 beating a relevant index's rank-2.\n\n### Query understanding\n\nSpelling and entity correction run **concurrently** with the search, so they\ncost nothing when nothing needs correcting, and the corrected query is only\nre-run when the original returned fewer than three results — and only kept if\nit did better. A bad suggestion cannot make results worse.\n\n**Jarvis auto-corrects your spelling.** Mistype a word and it recognises what\nyou meant, fixes it, and shows you what it searched for — so a typo never\nsilently returns nothing.\n\nThere is no hardcoded dictionary. Building one from the local corpus was tried\nand measured useless: 721 feed items yield 2652 \"entities\" that are almost\nentirely filing boilerplate (`Filer`, `Filed`, `AccNo`), and the result knew\nnone of the terms people actually mistype. Wikipedia's search API knows all of\nthem, live, and stays current for free.\n\nTwo kinds of correction, handled differently:\n\n| Kind | Example | Behaviour |\n| --- | --- | --- |\n| **Spelling** | `situtational` → situational | corrected silently, shown on screen |\n| **Entity** | a misspelt name → the right one | corrected **and spoken aloud** |\n\nThe difference matters. Reading \"showing results for situational awareness\"\naloud after a one-letter typo is noise. But a misheard *name* resolves to a\ndifferent person entirely, and answering about someone else without saying so is\nindistinguishable from being wrong — so entity corrections are always announced.\n\nSuggestions are never applied blindly. The decision is made locally on\nDamerau-Levenshtein distance relative to word length, and a correction is only\nkept if it actually returned better results than the original. A bad suggestion\ncannot make things worse.\n\n### Latency\n\nSearch returns in **46–956 ms** against 31–51 s for the old path, which ran\nretrieval plus local generation. Repeat queries are **0 ms** (cached).\n\nConnection warmth was measured rather than assumed. Node 22's default dispatcher\nholds pooled connections for at least **120 s** idle — far longer than the ~4 s\ncommonly quoted — so no custom `undici` dispatcher is needed and none is added:\n\n```\ncold (first ever fan-out)   7812 ms\nafter   0s idle              664 ms\nafter  60s idle              690 ms\nafter 120s idle              564 ms\n```\n\nOnly the cold start is worth removing, so the three **general** origins are\nwarmed once, 3 s after launch. The eight specialised providers are intent-gated\nand left cold. There is no repeating warmer: warmth already survives a session,\nand periodic warming would be unsolicited traffic to third parties.\n\n---\n\n## Retrieval engine\n\n`src/js/services/ragService.js` implements hybrid retrieval. Design choices are\nevidence-driven and each is traceable to a measurement or a paper.\n\n> **Diagram:** [Retrieval engine →](docs/ARCHITECTURE.md#6-retrieval-engine)\n\n### Components\n\n| Stage | Implementation | Rationale |\n| --- | --- | --- |\n| Sparse | BM25 over a persistent inverted index, incremental on ingest | Re-tokenising the corpus per query measured 104.8ms at 5k chunks on the render thread |\n| Dense | nomic-embed-text through Ollama, cosine similarity | Degrades to BM25-only when no embedder is present |\n| Fusion | Reciprocal Rank Fusion, k=60 | Derived from PubHealthBench, where hybrid beat both single-retriever modes. **This did not reproduce locally — see [Retrieval accuracy](#retrieval-accuracy)** |\n| Expansion | PRF: top 4 chunks, top 6 non-query terms, fused as a separate list at weight 0.5 | Kept separate so a poor feedback pool can dilute but not corrupt the original ranking |\n| Entities | Normalised Levenshtein, threshold 0.25, after exact-match miss | Input is speech-to-text, so names arrive mangled |\n| Selection | Late sentence selection, IDF-weighted overlap with lead bias, budget 10 | LongEval's winning system paired plain passages with late sentence selection |\n| Reranking | Ambiguity-gated LLM rerank, opt-in | See below |\n\n### Retrieval accuracy\n\nSpeed was measured long before accuracy was, which is backwards: a fast ranker\nthat ranks the wrong passage first is worse than a slow one that does not.\n`eval/` now carries a labelled benchmark — 29 questions over 30 documents,\ndriving the shipped module through ablation switches rather than a\nreimplementation of it. Full numbers and method in\n[eval/RESULTS.md](eval/RESULTS.md).\n\n| Configuration | P@1 | P@3 | MRR | ms/query |\n| --- | ---: | ---: | ---: | ---: |\n| lexical only (BM25) | 69.0% | 79.3% | 0.737 | <1 |\n| **dense only** | **89.7%** | **100%** | **0.948** | 60 |\n| hybrid, as shipped | 72.4% | 93.1% | 0.825 | 61 |\n| hybrid + rerank | 72.4% | 93.1% | 0.825 | 3,243 |\n\n**Dense-only beats the shipped hybrid by 17 points at rank 1.** The rationale\nfor hybrid fusion came from the literature and did not reproduce on this corpus\nwith this embedding model. Lexical retrieval is in the stack to catch rare\nproper nouns; dense matched it there (5/5) and beat it on every other question\ntype, so its weight in the fusion is diluting a better ranking rather than\nprotecting against a weakness.\n\nThe default has not been changed on that basis, for reasons stated in full in\nthe results: the benchmark's author also wrote its questions, 29 questions makes\nanything under ~7 points a single labelling choice, and BM25 is the only thing\nthat still works when the embedder is down. But the shipped weighting is\ncurrently **unsupported by the only measurement that exists**, and saying so is\nmore useful than citing the paper it came from.\n\nReranking changed no answer on this set while costing 3.2 seconds per query.\n\n### Memory accuracy\n\nThe belief store's claims, measured by replaying 12 scripted observations over\n6 simulated days (`node eval/memory-eval.mjs`):\n\n| Claim | Result |\n| --- | --- |\n| A repeated genuine preference becomes durable | 3/3 held |\n| A one-off speech mangling never does | 0/2 admitted |\n| A changed fact replaces the old value | VS Code durable, Sublime archived |\n| Confidence bounded and reported | 83% after 3 observations |\n| Provenance retained | 3 records, sources voice and text |\n\nThis exercises the state machine — corroboration, decay, competition, revision.\nIt does not measure how well a 4B model distils facts from real conversation,\nnor whether durable beliefs improve the final answer. Both need labelled real\ndata, and neither is claimed here.\n\n### Measured performance\n\nInverted index against the previous implementation, top-10 rankings verified\nbit-identical at every size:\n\n| Corpus | Before | After | Speedup |\n| --- | --- | --- | --- |\n| 100 chunks | 1.87 ms | 0.008 ms | 223x |\n| 500 chunks | 8.71 ms | 0.037 ms | 238x |\n| 2,000 chunks | 37.1 ms | 0.116 ms | 319x |\n| 5,000 chunks | 104.8 ms | 0.456 ms | 230x |\n\nLate sentence selection, measured end to end on real document text:\n\n| Metric | Result |\n| --- | --- |\n| Context size reduction | 81 percent, 11,396 to 2,192 characters |\n| Correct evidence position | ranks 1 to 3 |\n| Determinism across repeated calls | byte-identical |\n\n### On reranking\n\nOllama exposes no `/api/rerank` endpoint, so a conventional cross-encoder is not\navailable. Gemma 3 can rerank correctly, scoring 3 of 3 top-1 on labelled\npassages, but a single call costs roughly 3 seconds.\n\nReranking is therefore gated on ambiguity and is opt-in rather than default:\n\n| Path | Frequency | Latency |\n| --- | --- | --- |\n| Gate skips, top-1 clearly dominant | ~50 percent | ~90 ms |\n| Gate fires, candidates close | ~50 percent | ~4,800 ms |\n\nTyped input opts in. Voice input does not, because roughly 5 seconds of added\nsilence is unacceptable on the spoken path. Any timeout or malformed response\nfalls back to lexical order, so reranking is an enhancement and never a\ndependency.\n\n### Why not agentic retrieval\n\nA-RAG style agentic retrieval was evaluated and deliberately not adopted.\nBenchmarked on this hardware, Gemma 3 routes queries to the correct source with\n92 percent accuracy, which is sufficient. The blocker is latency: a single\nplanning call costs about 3 seconds, and the published agent loops use 5 to 20\nsteps. That is 15 to 60 seconds of silence before the first word, which does not\nwork for a voice interface.\n\n---\n\n## Evaluation\n\n```bash\nnode eval/retrieval-eval.mjs   # ranking accuracy across configurations\nnode eval/memory-eval.mjs      # belief store: corroboration, revision, garble rejection\n```\n\nBoth harnesses drive the shipped modules. Results, method, and the caveats that\nbound them are in [eval/RESULTS.md](eval/RESULTS.md); the headline numbers are\nin [Retrieval accuracy](#retrieval-accuracy) and [Memory accuracy](#memory-accuracy)\nabove.\n\nThe benchmark corpus is synthetic and labelled. It supports comparison between\nconfigurations, since each sees identical data; it does not predict accuracy on\na real user's memory, and it is not presented as doing so.\n\n**What is still unmeasured, and should be:** whether retrieved context and\ndurable beliefs improve the final *answer*, as opposed to the ranking. That\nneeds answer-level labels and a judge. The rankings are now measured; the\nanswers are not, and no claim is made about them.\n\n---\n\n## Android companion\n\n`companion/` contains a Kotlin application that mirrors the visualizer to a\nphone and exposes device control back to the desktop.\n\n<p align=\"left\">\n  <img src=\"https://img.shields.io/badge/minSdk-26-3DDC84?style=flat-square&logo=android&logoColor=white\" alt=\"minSdk 26\" />\n  <img src=\"https://img.shields.io/badge/targetSdk-35-3DDC84?style=flat-square&logo=android&logoColor=white\" alt=\"targetSdk 35\" />\n  <img src=\"https://img.shields.io/badge/Kotlin-2.0.21-7F52FF?style=flat-square&logo=kotlin&logoColor=white\" alt=\"Kotlin 2.0.21\" />\n  <img src=\"https://img.shields.io/badge/AGP-8.7.3-02303A?style=flat-square&logo=gradle&logoColor=white\" alt=\"AGP 8.7.3\" />\n</p>\n\n### The visualizer is copied, not reimplemented\n\n| Asset | Origin | State |\n| --- | --- | --- |\n| `visualizerModes.js` | `src/js/visualizerModes.js` | byte-identical, SHA-256 verified |\n| `three.module.js` | three@0.158.0 | byte-identical |\n| Vertex and fragment shaders | `src/index.html` | verbatim |\n| `visualizer.js` | `src/js/scripts.js` | renderer, uniforms, and FFT blend preserved |\n\n`visualizerModes.js` still carries `import * as THREE from 'three'`. Rather than\nedit the copy, an import map in the host page resolves the bare specifier, so\nthe file stays identical to the desktop original.\n\nAssets are served through `WebViewAssetLoader` on a virtual https origin rather\nthan `file://`. WebView blocks ES module scripts from `file://` because the\norigin is opaque, which presents as a silent black screen.\n\n### Audio bridge\n\nThe desktop fills `window.jarvisFrequencyData` from a WebAudio AnalyserNode. A\nWebView cannot obtain microphone access that way, so `AudioFft.kt` reads\n`AudioRecord`, applies a Hann window, runs a radix-2 FFT, and writes the same 64\nbins natively. Bins use WebAudio's decibel mapping, minus 100 to minus 30 dB\nonto 0 to 255. Linear magnitude was tried and leaves the orb nearly static at\nspeaking volume.\n\n### Pairing\n\n> **Diagram:** [Pairing →](docs/ARCHITECTURE.md#7-pairing)\n\nThe phone always dials outward, which avoids Doze restrictions and handset\naddress churn. Pairing retries every 10 seconds while unpaired, because the\nwindow is usually opened after discovery has already resolved.\n\n`/pair` and `/apk` return 403 once the window closes. That window is the only\nthing standing between a network neighbour and the bridge token, so it is short\nand user-initiated.\n\n### Capability negotiation\n\nOn connect the phone reports what it can actually do, probed rather than\nassumed:\n\n```json\n{\"open_app\":true,\"list_apps\":true,\"clipboard\":true,\"battery\":true,\n \"tts\":true,\"flashlight\":true,\"volume\":true,\"alarms\":true,\n \"ui_automation\":false,\"screenshot\":false,\"read_screen\":false,\n \"silent_install\":false}\n```\n\nThe desktop reasons about the device instead of firing commands blindly. A\nrequest needing accessibility explains how to enable it rather than failing\nopaquely. `alarms` reports whether exact alarms are currently permitted — it is\nuser-revocable on Android 12 and 13, and an assistant that keeps promising\nwake-ups it cannot schedule is the failure this project refuses everywhere else.\n\n### Control tiers\n\n| Tier | Requires | Commands |\n| --- | --- | --- |\n| 1 | Nothing beyond install | `ping`, `device_info`, `battery`, `clipboard_get`, `clipboard_set`, `tts`, `list_apps`, `open_app_by_name`, `flashlight`, `volume`, `capabilities`, `schedule_alarm`, `cancel_alarm`, `list_alarms` |\n| 2 | AccessibilityService enabled | `get_layout`, `click`, `long_press`, `swipe`, `input_text`, `global`, `screenshot`, `read_screen` |\n| 3 | Wireless Debugging enabled | Desktop-side ADB: brightness, volume, keyevents, package management, file transfer, screenrecord |\n\nTier 3 runs entirely on the desktop through `adbService.js`. The APK is not\ninvolved. All ADB invocations pass argument arrays, never concatenated strings,\nand raw shell passthrough is disabled at the IPC boundary.\n\n### Alarms that actually wake you\n\nThe desktop's scheduler cannot, and says so. The companion schedules through\n`AlarmManager.setAlarmClock()` — the only API the platform treats as a real\nalarm clock, exempt from every battery-saver relaxation and special-cased by\nOEM battery layers. `setExactAndAllowWhileIdle` is rate-limited to roughly one\nfiring per app per nine minutes in Doze; a wake-up that is \"usually within a few\nminutes\" is not a wake-up.\n\n- Parsing stays on the desktop, so both surfaces agree on what \"tomorrow\n  morning\" means. Only the **scheduling** happens on the phone.\n- Re-armed on boot. `AlarmManager` forgets everything across a restart, and an\n  alarm app that skips this works perfectly until the first reboot and then\n  fails silently overnight. Xiaomi's `QUICKBOOT_POWERON` is handled alongside\n  the AOSP broadcast.\n- The ring screen shows over the lock screen, says what the alarm was for out\n  loud, and offers a nine-minute snooze that stays **one-shot** even on a\n  repeating alarm — copying the repeat rule would walk a 7am wake-up into the\n  afternoon over a week of snoozing.\n- Rings on `STREAM_ALARM`, which survives silent mode and Do Not Disturb, and\n  raises the alarm volume off zero rather than playing nothing.\n\n### The floating orb\n\n`OverlayService` keeps JARVIS on screen after th","readmeFilename":"README.md"}