From f20854169ac84b1be3470aac74b389139dc4e8f0 Mon Sep 17 00:00:00 2001 From: Yashvant Date: Sat, 7 Nov 2026 08:51:16 +0530 Subject: [PATCH 001/204] Start JARVIS X light premium AI workspace --- index.html | 73 +++++++++++++++++++++--------------------------------- 1 file changed, 28 insertions(+), 45 deletions(-) diff --git a/index.html b/index.html index f303eeb..3901156 100644 --- a/index.html +++ b/index.html @@ -3,56 +3,39 @@ - - - NEON HORIZON — Night Drive - + + + JARVIS X — Personal Intelligence - + + -
-
-
-
- ◈NEON HORIZONNIGHT DRIVE SIMULATOR -
-
-
-
+ \ No newline at end of file From da118b8ec919aec4d873b72d5008d5e5376f2006 Mon Sep 17 00:00:00 2001 From: Yashvant Date: Sat, 7 Nov 2026 09:04:03 +0530 Subject: [PATCH 057/204] Build JARVIS X premium-light workspace foundation --- jarvis-x/src/App.tsx | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 jarvis-x/src/App.tsx diff --git a/jarvis-x/src/App.tsx b/jarvis-x/src/App.tsx new file mode 100644 index 0000000..6acca47 --- /dev/null +++ b/jarvis-x/src/App.tsx @@ -0,0 +1,8 @@ +import{useState}from"react";import{Activity,ArrowUp,AudioLines,BookOpen,BrainCircuit,Check,ChevronDown,Command,Compass,FileText,FolderKanban,Globe,ImagePlus,Menu,Mic,MoreHorizontal,Paperclip,Plus,Search,Send,Settings,ShieldCheck,Sparkles,SquarePen,Workflow,X}from"lucide-react"; +const starter=["Plan and build a new app","Research a topic in depth","Review code or debug an issue","Summarise a document"]; +const nav=[{label:"New chat",icon:SquarePen},{label:"Search",icon:Search},{label:"Projects",icon:FolderKanban},{label:"Research",icon:Compass},{label:"Tasks & automations",icon:Workflow}]; +export default function App(){const[message,setMessage]=useState("");const[active,setActive]=useState("New chat");const[sidebar,setSidebar]=useState(true);const[model,setModel]=useState("Auto");const[showModels,setShowModels]=useState(false);const[items,setItems]=useState<{role:string,text:string}[]>([]);const[notice,setNotice]=useState("Connect your AI provider in Settings to enable live responses."); +function send(){if(!message.trim())return;setItems(v=>[...v,{role:"You",text:message.trim()}]);setMessage("");setNotice("Your message is saved in this preview. Add the Gemini API secret to enable AI responses.");} +return
{sidebar&&} +
{!sidebar&&}Workspace/{active}
Private workspace
+
{active==="New chat"||active==="Welcome to JARVIS X"||active==="App architecture plan"?<>
YOUR PERSONAL AI WORKSPACE

Good morning.
What shall we create?

A thoughtful space for ideas, research and building remarkable things.

Reasoning Research Files Agents
{items.length===0?
{starter.map((s,i)=>)}
:
{items.map((m,i)=>
{m.role}

{m.text}

)}
}
Recent tasks
${w.tasks.map(t=>`
${esc(t.title)}${esc(t.description||'')}
${esc(t.status)}
`).join('')||'
No agent tasks yet.
'}
Capability roadmap
${CAPABILITY_GROUPS.map(g=>`

${esc(g.title)}

${g.items.map(i=>`

◇ ${esc(i)}

`).join('')}
`).join('')}
`; - $('#run-agent').onclick=async()=>{const goal=$('#agent-goal').value.trim();if(!goal)return toast('Describe the task first.');try{const result=await invokeAgent(supabase,'run_agent',{goal});toast(result.message||'Plan created');render(view);}catch(e){toast(e.message);}}; + area.innerHTML=`

AUTONOMOUS WORKSPACE

Agent studio

Describe the outcome. JARVIS X prepares a plan and checks connected services.

✳
${lastPlan?`
${esc(lastPlan.summary||'Execution plan')}
    ${(lastPlan.steps||[]).map(step=>`
  1. ${esc(step.title)}${esc(step.description||'')}${step.requiresApproval?' · Approval required':''}
  2. `).join('')}
`:''}
Recent agent runs
${w.runs.map(run=>`
${esc(run.goal)}${esc(run.status)} · ${new Date(run.created_at).toLocaleString()}
${esc(run.status)}
`).join('')||'
No agent runs yet.
'}
Recent tasks
${w.tasks.map(t=>`
${esc(t.title)}${esc(t.description||'')}
${esc(t.status)}
`).join('')||'
No agent tasks yet.
'}
Capability roadmap
${CAPABILITY_GROUPS.map(g=>`

${esc(g.title)}

${g.items.map(i=>`

◇ ${esc(i)}

`).join('')}
`).join('')}
`; + $('#run-agent').onclick=async()=>{const goal=$('#agent-goal').value.trim();if(!goal)return toast('Describe the task first.');try{const result=await invokeAgent(supabase,'run_agent',{goal});lastPlan=result;toast(result.message||'Plan created');render(view);}catch(e){toast(e.message);}}; }else if(view==='memory'){ area.innerHTML=`

PERSONAL CONTEXT

Memory

Information available across conversations.

${w.memories.map(m=>`
${esc(m.content)}${esc(m.category)} · ${new Date(m.created_at).toLocaleDateString()}
`).join('')||'
No saved memories yet.
'}
`; $('#add-memory').onclick=async()=>{const content=prompt('What should JARVIS X remember?');if(!content?.trim())return;const {error}=await supabase.from('jarvis_memories').insert({user_id:getUser().id,content:content.trim(),category:'general'});if(error)return toast(error.message);toast('Memory saved');render(view);}; From 8a9cc7342a664a53bb0f86371d9b48c6bd879134 Mon Sep 17 00:00:00 2001 From: Yashvant Date: Sat, 7 Nov 2026 09:11:59 +0530 Subject: [PATCH 104/204] Fix agent plan view syntax --- src/lib/workspace.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/lib/workspace.js b/src/lib/workspace.js index b0b3f9c..e3bd60c 100644 --- a/src/lib/workspace.js +++ b/src/lib/workspace.js @@ -4,7 +4,8 @@ import { invokeAgent, loadWorkspace } from './agent-client.js'; const $ = (s) => document.querySelector(s); const esc = (s='') => String(s).replace(/[&<>"']/g,c=>({'&':'&','<':'<','>':'>','"':'"',"'":'''}[c])); export function initializeWorkspace({supabase,getUser,toast,onChat}) { - const area=$('#messages');\n let lastPlan=null; + const area=$('#messages'); + let lastPlan=null; const labels={chat:'Chats',projects:'Projects',tools:'Agent studio',memory:'Memory',activity:'Activity',integrations:'Integrations'}; async function refresh(){return await loadWorkspace(supabase);} function activate(view){ From 95e6adb4517b05667917b34042f37a3c44548e74 Mon Sep 17 00:00:00 2001 From: Yashvant Date: Sat, 7 Nov 2026 09:12:02 +0530 Subject: [PATCH 105/204] Style agent execution plans --- style.css | 2 ++ 1 file changed, 2 insertions(+) diff --git a/style.css b/style.css index 2247dc8..3c5556d 100644 --- a/style.css +++ b/style.css @@ -12,3 +12,5 @@ @media(max-width:800px){.page-view{padding:8px 2px 40px}.page-heading{align-items:flex-start;flex-direction:column}.page-heading h2{font-size:34px}.agent-launch{align-items:flex-start;padding:15px;gap:11px}.agent-orb{width:40px;height:40px;flex-basis:40px}.agent-input .gold-button{align-self:stretch}.list-card{align-items:flex-start}.approval-actions{flex-shrink:0}.integration-grid{grid-template-columns:1fr}.workspace-grid,.capability-grid{grid-template-columns:1fr}} .message-attachments{display:flex;flex-wrap:wrap;gap:7px;margin-top:8px}.message-attachments span{border:1px solid #e8e1d5;background:#f5f1e8;border-radius:7px;padding:5px 8px;color:#88785c;font-size:9px;max-width:100%;overflow-wrap:anywhere} + +.plan-card{margin:0 0 24px;padding:17px 19px;background:#f6f1e7;border:1px solid #e8deca;border-radius:13px}.plan-card>b{display:block;color:#62543b;font:600 17px 'Playfair Display',serif}.plan-card ol{padding-left:20px;margin:12px 0 0}.plan-card li{padding:5px 0;color:#8e7a56;font-size:11px}.plan-card li strong{color:#5e584c}.plan-card li small{display:block;margin-top:4px;color:#969083;font-size:10px} From e521c60590fd5a9a280587c9ce15b7f48a2db8a0 Mon Sep 17 00:00:00 2001 From: Yashvant Date: Sat, 7 Nov 2026 09:12:15 +0530 Subject: [PATCH 106/204] Test gateway ownership and approval safeguards --- tests/smoke.test.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/tests/smoke.test.js b/tests/smoke.test.js index 43ce07c..1df9445 100644 --- a/tests/smoke.test.js +++ b/tests/smoke.test.js @@ -15,4 +15,12 @@ test("JARVIS X is configured as a light-first PWA", async () => { assert.match(html, /theme-color" content="#f7f4ed"/); assert.match(manifest, /"display": "standalone"/); assert.doesNotMatch(html, /prefers-color-scheme:\s*dark/); -}); \ No newline at end of file +}); +test("gateway restricts access and verifies approvals before side effects", async () => { + const worker = await readFile(new URL("../cloudflare/worker.js", import.meta.url), "utf8"); + assert.match(worker, /JARVIS_OWNER_EMAILS/); + assert.match(worker, /jarvis_approvals/); + assert.match(worker, /approvedPayload\.files/); + assert.match(worker, /github\.create_repository/); + assert.match(worker, /cloudflare\.deploy_pages/); +}); From eb6f0fb9add70e6ac57d8e4f11e04b4750258020 Mon Sep 17 00:00:00 2001 From: Yashvant Date: Sat, 7 Nov 2026 09:12:18 +0530 Subject: [PATCH 107/204] Lint the Cloudflare gateway in CI --- .github/workflows/build.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 92dd9d6..610c072 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -14,4 +14,5 @@ jobs: node-version: 22 - run: npm install - run: npm test + - run: node --check cloudflare/worker.js - run: npm run build From 454c4dbb7935d561a7536c0de5a669cfe5bfccf1 Mon Sep 17 00:00:00 2001 From: Yashvant Date: Sat, 7 Nov 2026 09:12:30 +0530 Subject: [PATCH 108/204] Return expected Pages URL with queued deployment status --- cloudflare/worker.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cloudflare/worker.js b/cloudflare/worker.js index 5e25fbc..99f41fa 100644 --- a/cloudflare/worker.js +++ b/cloudflare/worker.js @@ -75,7 +75,7 @@ export default { const response=await fetch(endpoint,{method:"POST",headers:{authorization:"Bearer "+env.GITHUB_TOKEN,accept:"application/vnd.github+json","content-type":"application/json","x-github-api-version":"2022-11-28","user-agent":"JARVIS-X"},body:JSON.stringify({ref:"jarvis-x-foundation",inputs:{project_name:projectName,branch:ref,repository}})}); if(!response.ok){const result=await response.json();return json({error:result.message||"Could not start Cloudflare deployment."},response.status,headers);} await audit(env,auth,"cloudflare.deployment_requested",projectName,{ref}); - return json({success:true,message:"Cloudflare deployment workflow started.",projectName,workflow:"deploy-cloudflare.yml"},202,headers); + return json({success:true,status:"queued",message:"Cloudflare deployment workflow started.",projectName,deploymentUrl:"https://"+projectName+".pages.dev",repository,branch:ref,workflow:"deploy-cloudflare.yml"},202,headers); } return json({error:"Route not found."},404,headers); }catch(error){return json({error:error instanceof Error?error.message:"Gateway error."},401,headers);} From 40dee2ac02a2e71cd3f1913dedbe1ea88bf36be0 Mon Sep 17 00:00:00 2001 From: Yashvant Date: Sat, 7 Nov 2026 09:12:38 +0530 Subject: [PATCH 109/204] Show generated repository and deployment links after approvals --- src/lib/workspace.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/workspace.js b/src/lib/workspace.js index e3bd60c..a5d8f23 100644 --- a/src/lib/workspace.js +++ b/src/lib/workspace.js @@ -30,8 +30,8 @@ export function initializeWorkspace({supabase,getUser,toast,onChat}) { area.querySelectorAll('[data-delete-memory]').forEach(b=>b.onclick=async()=>{const {error}=await supabase.from('jarvis_memories').delete().eq('id',b.dataset.deleteMemory);if(error)return toast(error.message);render(view);}); }else if(view==='activity'){ area.innerHTML=`

CONTROL CENTRE

Activity & approvals

Review actions before the agent performs them.

Pending approvals
${w.approvals.map(a=>`
${esc(a.action)}${esc(a.status)} · ${new Date(a.created_at).toLocaleString()} · ${esc(Array.isArray(a.payload?.files)?a.payload.files.map(file=>file.path).join(', '):JSON.stringify(a.payload||{}))}
${a.status==='approved'?``:``}
`).join('')||'
Nothing is waiting for approval.
'}
Recent tasks
${w.tasks.map(t=>`
${esc(t.title)}${esc(t.description||'')}
${esc(t.status)}
`).join('')||'
Agent activity will appear here.
'}
`; - area.querySelectorAll('[data-decision]').forEach(b=>b.onclick=async()=>{try{await invokeAgent(supabase,'decide_approval',{approvalId:b.dataset.id,decision:b.dataset.decision});toast('Approval updated.');render(view);}catch(e){toast(e.message);}}); - area.querySelectorAll('[data-execute]').forEach(b=>b.onclick=async()=>{try{const result=await invokeAgent(supabase,'execute_approved',{approvalId:b.dataset.execute});toast(result.execution?.success?'Action completed.':'Execution submitted.');render(view);}catch(e){toast(e.message);}}); + area.querySelectorAll('[data-decision]').forEach(b=>b.onclick=async()=>{try{const result=await invokeAgent(supabase,'decide_approval',{approvalId:b.dataset.id,decision:b.dataset.decision});const url=result.execution?.repository?.url||result.execution?.deploymentUrl;toast(url?`Ready: ${url}`:(result.message||'Approval updated.'));render(view);}catch(e){toast(e.message);}}); + area.querySelectorAll('[data-execute]').forEach(b=>b.onclick=async()=>{try{const result=await invokeAgent(supabase,'execute_approved',{approvalId:b.dataset.execute});const url=result.execution?.repository?.url||result.execution?.deploymentUrl;toast(url?`Ready: ${url}`:(result.execution?.message||'Execution submitted.'));render(view);}catch(e){toast(e.message);}}); } } async function renderIntegrations(){ From 3bb3709ca94af6f9b029f4dddc481c36ac88da50 Mon Sep 17 00:00:00 2001 From: Yashvant Date: Sat, 7 Nov 2026 09:12:43 +0530 Subject: [PATCH 110/204] Document secure gateway URL configuration --- .env.example | 1 + 1 file changed, 1 insertion(+) diff --git a/.env.example b/.env.example index 12e9c63..80d0384 100644 --- a/.env.example +++ b/.env.example @@ -12,3 +12,4 @@ VITE_CHAT_FUNCTION_URL=https://tmlrveloeirxsankeqrs.supabase.co/functions/v1/jar # TTS_API_KEY= # EMAIL_PROVIDER_API_KEY= # APP_ORIGIN= +# JARVIS_GATEWAY_URL=https://.workers.dev From 09c7772025677312e6f11bc600eac9e6df9806c0 Mon Sep 17 00:00:00 2001 From: Yashvant Date: Sat, 7 Nov 2026 09:12:55 +0530 Subject: [PATCH 111/204] Correct secret locations and least-privilege provider permissions --- docs/PROVIDER-SETUP.md | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/docs/PROVIDER-SETUP.md b/docs/PROVIDER-SETUP.md index f8d8afe..7a1da5e 100644 --- a/docs/PROVIDER-SETUP.md +++ b/docs/PROVIDER-SETUP.md @@ -10,9 +10,6 @@ Do not paste secrets into the chat, source code, issue comments or frontend envi ## Supabase Edge Function Secrets Add these under Supabase Dashboard → Edge Functions → Secrets: - GEMINI_API_KEY — required for chat and agent planning. -- GITHUB_TOKEN — fine-grained GitHub token; restrict it to repositories the agent should manage. Grant only the repository permissions needed for Contents, Pull Requests, Metadata and Actions. Repository creation may require an OAuth/GitHub App flow with repository creation permission. -- CLOUDFLARE_API_TOKEN — token scoped to Pages/Workers and only the required account resources. -- CLOUDFLARE_ACCOUNT_ID — target Cloudflare account. - SEARCH_API_KEY — selected web-search provider key. - IMAGE_PROVIDER_API_KEY — image provider key if using a separate provider. - TTS_API_KEY — speech provider key if using a separate provider. @@ -20,7 +17,7 @@ Add these under Supabase Dashboard → Edge Functions → Secrets: - JARVIS_GATEWAY_URL — deployed Cloudflare Worker gateway URL used by the agent after approval. ## Cloudflare Worker and GitHub Actions secrets -The Worker deployment workflow reads these GitHub repository Actions secrets: CLOUDFLARE_API_TOKEN, CLOUDFLARE_ACCOUNT_ID, SUPABASE_URL, SUPABASE_PUBLISHABLE_KEY, JARVIS_OWNER_EMAILS, APP_ORIGIN, GITHUB_TOKEN_JARVIS, GITHUB_OWNER, and GITHUB_REPO. The Worker only accepts users whose verified email is listed in JARVIS_OWNER_EMAILS. The Pages deployment workflow also requires CLOUDFLARE_API_TOKEN and CLOUDFLARE_ACCOUNT_ID. +The Worker deployment workflow reads these GitHub repository Actions secrets: CLOUDFLARE_API_TOKEN, CLOUDFLARE_ACCOUNT_ID, SUPABASE_URL, SUPABASE_PUBLISHABLE_KEY, JARVIS_OWNER_EMAILS, APP_ORIGIN, GITHUB_TOKEN_JARVIS, GITHUB_OWNER, and GITHUB_REPO. The Worker only accepts users whose verified email is listed in JARVIS_OWNER_EMAILS. The Pages deployment workflow also requires CLOUDFLARE_API_TOKEN, CLOUDFLARE_ACCOUNT_ID, and GITHUB_TOKEN_JARVIS for private source checkout. Give the GitHub token only the permissions required to create repositories, write repository contents, read metadata, and dispatch Actions. A GitHub App is preferred where possible; verify repository-creation support before enabling it. The Cloudflare token should be scoped to Pages: Edit and Workers Scripts: Edit on the intended account. ## Dashboard configuration (not Edge Function secrets) - Supabase Auth → Providers → Google: configure OAuth Client ID and Client Secret. From 8394a02ec06fc8b80846b68ec6c0874036946639 Mon Sep 17 00:00:00 2001 From: Yashvant Date: Sat, 7 Nov 2026 09:13:07 +0530 Subject: [PATCH 112/204] Show exact GitHub and Cloudflare configuration names --- src/lib/integrations.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/integrations.js b/src/lib/integrations.js index ff7bcba..e35059b 100644 --- a/src/lib/integrations.js +++ b/src/lib/integrations.js @@ -1,7 +1,7 @@ export const INTEGRATIONS = [ { id:'gemini', name:'Google Gemini', category:'AI models', required:true, secrets:['GEMINI_API_KEY'], instructions:'Add GEMINI_API_KEY under Supabase Edge Functions → Secrets. Used by chat, vision and AI task planning.', capabilities:['Chat and reasoning','Image and PDF understanding','Structured agent planning'] }, - { id:'github', name:'GitHub', category:'Development', required:false, secrets:['GITHUB_TOKEN'], instructions:'Add GITHUB_TOKEN_JARVIS as a GitHub Actions secret. The Worker deployment workflow installs it as GITHUB_TOKEN. Grant only the repository and Actions permissions needed; repository creation requires account-level repository creation permission.', capabilities:['Create private repositories','Read and write project files','Branches, commits and pull requests'] }, - { id:'cloudflare', name:'Cloudflare', category:'Deployment', required:false, secrets:['CLOUDFLARE_API_TOKEN','CLOUDFLARE_ACCOUNT_ID'], instructions:'Add CLOUDFLARE_API_TOKEN and CLOUDFLARE_ACCOUNT_ID as GitHub Actions secrets. Use a scoped token for Pages deployment and Worker deployment.', capabilities:['Create Pages projects','Deploy production builds','Run the secure gateway Worker'] }, + { id:'github', name:'GitHub', category:'Development', required:false, secrets:['GITHUB_TOKEN_JARVIS','GITHUB_OWNER','GITHUB_REPO'], instructions:'Add GITHUB_TOKEN_JARVIS as a GitHub Actions secret. The Worker deployment workflow installs it as GITHUB_TOKEN. Grant only the repository and Actions permissions needed; repository creation requires account-level repository creation permission.', capabilities:['Create private repositories','Read and write project files','Branches, commits and pull requests'] }, + { id:'cloudflare', name:'Cloudflare', category:'Deployment', required:false, secrets:['CLOUDFLARE_API_TOKEN','CLOUDFLARE_ACCOUNT_ID','JARVIS_GATEWAY_URL'], instructions:'Add CLOUDFLARE_API_TOKEN and CLOUDFLARE_ACCOUNT_ID as GitHub Actions secrets. Use a scoped token for Pages deployment and Worker deployment.', capabilities:['Create Pages projects','Deploy production builds','Run the secure gateway Worker'] }, { id:'supabase', name:'Supabase', category:'Backend', required:true, secrets:[], setup:'Set VITE_SUPABASE_URL and VITE_SUPABASE_PUBLISHABLE_KEY in the frontend host. Keep service-role and secret keys server-side.', capabilities:['Auth, database and private storage','Edge Functions','Realtime and vector search'] }, { id:'google', name:'Google OAuth', category:'Identity', required:false, secrets:[], setup:'Configure the Google OAuth client in Supabase Auth → Providers → Google, then add the Supabase callback URL to Google Cloud Console. Request Gmail scopes only when that feature is enabled.', capabilities:['Google sign-in','Consent-based Gmail integrations'] }, { id:'search', name:'Web search', category:'Research', required:false, secrets:['SEARCH_API_KEY'], instructions:'Add SEARCH_API_KEY under Supabase Edge Functions → Secrets after selecting the search provider.', capabilities:['Live web search','Source citations','Research reports'] }, From 5c16e2fb65167fcbfc382ad4b952a5cae8dda42e Mon Sep 17 00:00:00 2001 From: Yashvant Date: Sat, 7 Nov 2026 09:13:43 +0530 Subject: [PATCH 113/204] Update GitHub integration secret test --- tests/integrations.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/integrations.test.js b/tests/integrations.test.js index 063299a..a079efe 100644 --- a/tests/integrations.test.js +++ b/tests/integrations.test.js @@ -10,7 +10,7 @@ test('provider registry contains unique ids and named server secrets',()=>{ }); test('providers are never marked configured without all required keys',()=>{ for(const provider of INTEGRATIONS)assert.equal(integrationStatus(provider.id,{}),provider.setup?'manual_setup':'needs_setup'); - assert.equal(integrationStatus('github',{GITHUB_TOKEN:'present'}),'configured'); + assert.equal(integrationStatus('github',{GITHUB_TOKEN_JARVIS:'present',GITHUB_OWNER:'owner',GITHUB_REPO:'repo'}),'configured'); assert.equal(integrationStatus('cloudflare',{CLOUDFLARE_API_TOKEN:'present'}),'needs_setup'); }); test('capability roadmap covers the expected major product areas',()=>{ From 20a7f8d07ab910a199f0cd98c4a828da03a6c20a Mon Sep 17 00:00:00 2001 From: Yashvant Date: Sat, 7 Nov 2026 09:14:07 +0530 Subject: [PATCH 114/204] Report actual configured Cloudflare deployment capability --- cloudflare/worker.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cloudflare/worker.js b/cloudflare/worker.js index 99f41fa..c9b80a8 100644 --- a/cloudflare/worker.js +++ b/cloudflare/worker.js @@ -34,7 +34,7 @@ export default { try{ const auth=await authenticate(request,env); if(url.pathname==="/capabilities"&&request.method==="GET"){ - return json({providers:{gemini:Boolean(env.GEMINI_API_KEY),github:Boolean(env.GITHUB_TOKEN),cloudflareDeploy:Boolean(env.GITHUB_TOKEN&&env.GITHUB_OWNER&&env.GITHUB_REPO),supabase:Boolean(env.SUPABASE_URL&&env.SUPABASE_PUBLISHABLE_KEY)}},200,headers); + return json({providers:{gemini:Boolean(env.GEMINI_API_KEY),github:Boolean(env.GITHUB_TOKEN),cloudflare:Boolean(env.GITHUB_TOKEN&&env.GITHUB_OWNER&&env.GITHUB_REPO&&env.CLOUDFLARE_PAGES_READY==="true"),supabase:Boolean(env.SUPABASE_URL&&env.SUPABASE_PUBLISHABLE_KEY)}},200,headers); } if(url.pathname==="/api/github/repositories"&&request.method==="POST"){ const body=await request.json(); From 9745b3837d40032a7a4772a473c45ab9d43a87a5 Mon Sep 17 00:00:00 2001 From: Yashvant Date: Sat, 7 Nov 2026 09:14:09 +0530 Subject: [PATCH 115/204] Expose Pages deployment readiness in gateway --- .github/workflows/deploy-worker.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/deploy-worker.yml b/.github/workflows/deploy-worker.yml index 15826f9..2a171d1 100644 --- a/.github/workflows/deploy-worker.yml +++ b/.github/workflows/deploy-worker.yml @@ -24,9 +24,10 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN_JARVIS }} GITHUB_OWNER: ${{ secrets.GITHUB_OWNER }} GITHUB_REPO: ${{ secrets.GITHUB_REPO }} + CLOUDFLARE_PAGES_READY: 'true' run: | set -euo pipefail - for key in SUPABASE_URL SUPABASE_PUBLISHABLE_KEY JARVIS_OWNER_EMAILS APP_ORIGIN GITHUB_TOKEN GITHUB_OWNER GITHUB_REPO; do + for key in SUPABASE_URL SUPABASE_PUBLISHABLE_KEY JARVIS_OWNER_EMAILS APP_ORIGIN GITHUB_TOKEN GITHUB_OWNER GITHUB_REPO CLOUDFLARE_PAGES_READY; do value="${!key}" if [ -z "$value" ]; then echo "Missing required secret: $key"; exit 1; fi printf '%s' "$value" | wrangler secret put "$key" --config cloudflare/wrangler.toml From c37a5bad8150e7ab40c4a2a6af89319a240f330b Mon Sep 17 00:00:00 2001 From: Yashvant Date: Sat, 7 Nov 2026 09:14:17 +0530 Subject: [PATCH 116/204] Read GitHub and Cloudflare connection status from gateway --- supabase/functions/jarvis-agent/index.ts | 28 +++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/supabase/functions/jarvis-agent/index.ts b/supabase/functions/jarvis-agent/index.ts index 3486637..e36303c 100644 --- a/supabase/functions/jarvis-agent/index.ts +++ b/supabase/functions/jarvis-agent/index.ts @@ -3,7 +3,18 @@ import { createClient } from "https://esm.sh/@supabase/supabase-js@2"; const cors={"Access-Control-Allow-Origin":Deno.env.get("APP_ORIGIN")||"*","Access-Control-Allow-Headers":"authorization, x-client-info, apikey, content-type","Access-Control-Allow-Methods":"POST, OPTIONS","Content-Type":"application/json"}; const json=(body:unknown,status=200)=>new Response(JSON.stringify(body),{status,headers:cors}); const required:Record={gemini:["GEMINI_API_KEY"],github:["GITHUB_TOKEN"],cloudflare:["CLOUDFLARE_API_TOKEN","CLOUDFLARE_ACCOUNT_ID"],search:["SEARCH_API_KEY"],image:["IMAGE_PROVIDER_API_KEY"],speech:["TTS_API_KEY"],email:["EMAIL_PROVIDER_API_KEY"]}; -const configured=(provider:string)=>(required[provider]||[]).every(key=>Boolean(Deno.env.get(key))); +const configured=(provider:string)=>(required[provider]||[]).every(key=>Boolean(Deno.env.get(key)));\nasync function gatewayProviders(auth:string):Promise> { + const gateway=Deno.env.get("JARVIS_GATEWAY_URL"); + if(!gateway)return {}; + try{ + const base=gateway.endsWith("/")?gateway.slice(0,-1):gateway; + const response=await fetch(base+"/capabilities",{headers:{Authorization:auth}}); + if(!response.ok)return {}; + const result=await response.json(); + return result.providers||{}; + }catch{return {};} +} + Deno.serve(async(req:Request)=>{ if(req.method==="OPTIONS")return new Response("ok",{headers:cors}); if(req.method!=="POST")return json({error:"Method not allowed"},405); @@ -14,7 +25,15 @@ Deno.serve(async(req:Request)=>{ const {data:{user},error:authError}=await supabase.auth.getUser();if(authError||!user)return json({error:"Invalid session"},401); try{ const body=await req.json(),task=String(body.task||""),p=body.payload&&typeof body.payload==="object"?body.payload:{}; - if(task==="capabilities")return json({providers:Object.keys(required).map(id=>({id,configured:configured(id),requiredSecrets:required[id]}))}); + if(task==="capabilities"){ + const gateway=await gatewayProviders(auth); + const providers=Object.keys(required).map(id=>{ + if(id==="github")return {id,configured:Boolean(gateway.github),requiredSecrets:["GITHUB_TOKEN_JARVIS","GITHUB_OWNER","GITHUB_REPO"]}; + if(id==="cloudflare")return {id,configured:Boolean(gateway.cloudflare),requiredSecrets:["CLOUDFLARE_API_TOKEN","CLOUDFLARE_ACCOUNT_ID","JARVIS_GATEWAY_URL"]}; + return {id,configured:configured(id),requiredSecrets:required[id]}; + }); + return json({providers}); + } if(task==="create_project"){ const name=String(p.name||"").trim().slice(0,100);if(!name)return json({error:"Project name is required."},400); const {data,error}=await supabase.from("jarvis_projects").insert({user_id:user.id,name,description:String(p.description||"").slice(0,2000),project_type:String(p.projectType||"general").slice(0,40)}).select().single(); @@ -65,7 +84,10 @@ Deno.serve(async(req:Request)=>{ if(task==="run_agent"){ const goal=String(p.goal||"").trim().slice(0,4000);if(!goal)return json({error:"Describe what you want the agent to do."},400); if(!configured("gemini"))return json({error:"AI planning requires the Gemini API key.",missingProviders:["gemini"]},503); - const available=Object.keys(required).filter(configured); + const gateway=await gatewayProviders(auth); + const available=Object.keys(required).filter(id=>!["github","cloudflare"].includes(id)&&configured(id)); + if(gateway.github)available.push("github"); + if(gateway.cloudflare)available.push("cloudflare"); const planResponse=await fetch("https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent?key="+encodeURIComponent(Deno.env.get("GEMINI_API_KEY")!),{ method:"POST",headers:{"Content-Type":"application/json"}, body:JSON.stringify({ From a9b4493eeae19eb9513d62b0cbcd3eec74848d1f Mon Sep 17 00:00:00 2001 From: Yashvant Date: Sat, 7 Nov 2026 09:14:24 +0530 Subject: [PATCH 117/204] Fix gateway capability helper line break --- supabase/functions/jarvis-agent/index.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/supabase/functions/jarvis-agent/index.ts b/supabase/functions/jarvis-agent/index.ts index e36303c..29a5061 100644 --- a/supabase/functions/jarvis-agent/index.ts +++ b/supabase/functions/jarvis-agent/index.ts @@ -3,7 +3,8 @@ import { createClient } from "https://esm.sh/@supabase/supabase-js@2"; const cors={"Access-Control-Allow-Origin":Deno.env.get("APP_ORIGIN")||"*","Access-Control-Allow-Headers":"authorization, x-client-info, apikey, content-type","Access-Control-Allow-Methods":"POST, OPTIONS","Content-Type":"application/json"}; const json=(body:unknown,status=200)=>new Response(JSON.stringify(body),{status,headers:cors}); const required:Record={gemini:["GEMINI_API_KEY"],github:["GITHUB_TOKEN"],cloudflare:["CLOUDFLARE_API_TOKEN","CLOUDFLARE_ACCOUNT_ID"],search:["SEARCH_API_KEY"],image:["IMAGE_PROVIDER_API_KEY"],speech:["TTS_API_KEY"],email:["EMAIL_PROVIDER_API_KEY"]}; -const configured=(provider:string)=>(required[provider]||[]).every(key=>Boolean(Deno.env.get(key)));\nasync function gatewayProviders(auth:string):Promise> { +const configured=(provider:string)=>(required[provider]||[]).every(key=>Boolean(Deno.env.get(key))); +async function gatewayProviders(auth:string):Promise> { const gateway=Deno.env.get("JARVIS_GATEWAY_URL"); if(!gateway)return {}; try{ From 69ae2dfa9f7406810ef34aa4c49e1cf1151dce5b Mon Sep 17 00:00:00 2001 From: Yashvant Date: Sat, 7 Nov 2026 09:14:46 +0530 Subject: [PATCH 118/204] Add authenticated web research with cited sources --- supabase/functions/jarvis-research/index.ts | 34 +++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 supabase/functions/jarvis-research/index.ts diff --git a/supabase/functions/jarvis-research/index.ts b/supabase/functions/jarvis-research/index.ts new file mode 100644 index 0000000..fa9c848 --- /dev/null +++ b/supabase/functions/jarvis-research/index.ts @@ -0,0 +1,34 @@ +import "jsr:@supabase/functions-js/edge-runtime.d.ts"; +import { createClient } from "https://esm.sh/@supabase/supabase-js@2"; +const cors={"Access-Control-Allow-Origin":Deno.env.get("APP_ORIGIN")||"*","Access-Control-Allow-Headers":"authorization, x-client-info, apikey, content-type","Access-Control-Allow-Methods":"POST, OPTIONS","Content-Type":"application/json"}; +const json=(body:unknown,status=200)=>new Response(JSON.stringify(body),{status,headers:cors}); +Deno.serve(async(req:Request)=>{ + if(req.method==="OPTIONS")return new Response("ok",{headers:cors}); + if(req.method!=="POST")return json({error:"Method not allowed"},405); + const auth=req.headers.get("Authorization")||""; + if(!auth.startsWith("Bearer "))return json({error:"Sign in required."},401); + const supabaseKey=Deno.env.get("SUPABASE_ANON_KEY")||Deno.env.get("SUPABASE_PUBLISHABLE_KEY")||""; + if(!supabaseKey)return json({error:"Supabase publishable key is missing."},503); + const supabase=createClient(Deno.env.get("SUPABASE_URL")!,supabaseKey,{global:{headers:{Authorization:auth}}}); + const {data:{user},error:authError}=await supabase.auth.getUser(); + if(authError||!user)return json({error:"Invalid session."},401); + const tavilyKey=Deno.env.get("TAVILY_API_KEY"),geminiKey=Deno.env.get("GEMINI_API_KEY"); + if(!tavilyKey)return json({error:"TAVILY_API_KEY is not configured."},503); + if(!geminiKey)return json({error:"GEMINI_API_KEY is not configured."},503); + try{ + const body=await req.json(),query=String(body.query||"").trim().slice(0,400); + if(!query)return json({error:"Enter a research question."},400); + const searchResponse=await fetch("https://api.tavily.com/search",{method:"POST",headers:{"Authorization":"Bearer "+tavilyKey,"Content-Type":"application/json"},body:JSON.stringify({query,max_results:8,search_depth:"basic",topic:"general",include_answer:false,include_raw_content:false})}); + const search=await searchResponse.json(); + if(!searchResponse.ok)return json({error:search.message||"Web search provider request failed."},502); + const sources=(search.results||[]).slice(0,8).map((item:any,index:number)=>({number:index+1,title:String(item.title||"Source").slice(0,250),url:String(item.url||""),snippet:String(item.content||"").slice(0,5000),score:item.score||null})); + if(!sources.length)return json({query,summary:"No useful sources were found for this query.",sources:[]}); + const sourceText=sources.map(source=>"["+source.number+"] "+source.title+"\nURL: "+source.url+"\nContent: "+source.snippet).join("\n\n"); + const answerResponse=await fetch("https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent?key="+encodeURIComponent(geminiKey),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({systemInstruction:{parts:[{text:"You are JARVIS X Research. Answer the user's question using only the supplied web search sources. Cite factual claims with numbered references like [1] that match the source list. Clearly distinguish facts from uncertainty, mention when sources disagree, and never invent citations or facts. Respond in the user's language."}]},contents:[{role:"user",parts:[{text:"Research question: "+query+"\n\nSources:\n"+sourceText}]}],generationConfig:{temperature:0.2,maxOutputTokens:5000}})}); + const answer=await answerResponse.json(); + if(!answerResponse.ok)return json({error:answer.error?.message||"Research synthesis failed."},502); + const summary=answer.candidates?.[0]?.content?.parts?.map((p:any)=>p.text||"").join("")||"The research model returned no summary."; + await supabase.from("jarvis_audit_logs").insert({user_id:user.id,action:"research.completed",target:query,details:{sourceCount:sources.length,provider:"tavily"}}); + return json({query,summary,sources:sources.map(({number,title,url,score})=>({number,title,url,score})),provider:"tavily"}); + }catch(error){return json({error:error instanceof Error?error.message:"Unexpected research error."},500);} +}); From 4a0b9797268b766bfe40889ba14b8ea196996f5c Mon Sep 17 00:00:00 2001 From: Yashvant Date: Sat, 7 Nov 2026 09:14:56 +0530 Subject: [PATCH 119/204] Pin Supabase Edge Function dependency version --- supabase/functions/jarvis-chat/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/supabase/functions/jarvis-chat/index.ts b/supabase/functions/jarvis-chat/index.ts index 4af0707..b599abe 100644 --- a/supabase/functions/jarvis-chat/index.ts +++ b/supabase/functions/jarvis-chat/index.ts @@ -1,5 +1,5 @@ import "jsr:@supabase/functions-js/edge-runtime.d.ts"; -import { createClient } from "https://esm.sh/@supabase/supabase-js@2"; +import { createClient } from "https://esm.sh/@supabase/supabase-js@2.119.0"; const cors = { "Access-Control-Allow-Origin": Deno.env.get("APP_ORIGIN") || "*", From 691c67e5498af237a3556e0793da63b93c41b550 Mon Sep 17 00:00:00 2001 From: Yashvant Date: Sat, 7 Nov 2026 09:15:02 +0530 Subject: [PATCH 120/204] Pin Supabase Edge Function dependency version --- supabase/functions/jarvis-agent/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/supabase/functions/jarvis-agent/index.ts b/supabase/functions/jarvis-agent/index.ts index 29a5061..6b3cf55 100644 --- a/supabase/functions/jarvis-agent/index.ts +++ b/supabase/functions/jarvis-agent/index.ts @@ -1,5 +1,5 @@ import "jsr:@supabase/functions-js/edge-runtime.d.ts"; -import { createClient } from "https://esm.sh/@supabase/supabase-js@2"; +import { createClient } from "https://esm.sh/@supabase/supabase-js@2.119.0"; const cors={"Access-Control-Allow-Origin":Deno.env.get("APP_ORIGIN")||"*","Access-Control-Allow-Headers":"authorization, x-client-info, apikey, content-type","Access-Control-Allow-Methods":"POST, OPTIONS","Content-Type":"application/json"}; const json=(body:unknown,status=200)=>new Response(JSON.stringify(body),{status,headers:cors}); const required:Record={gemini:["GEMINI_API_KEY"],github:["GITHUB_TOKEN"],cloudflare:["CLOUDFLARE_API_TOKEN","CLOUDFLARE_ACCOUNT_ID"],search:["SEARCH_API_KEY"],image:["IMAGE_PROVIDER_API_KEY"],speech:["TTS_API_KEY"],email:["EMAIL_PROVIDER_API_KEY"]}; From 0f5e985f16aa173ba38487266c7512aacea3b131 Mon Sep 17 00:00:00 2001 From: Yashvant Date: Sat, 7 Nov 2026 09:15:10 +0530 Subject: [PATCH 121/204] Pin Supabase Edge Function dependency version --- supabase/functions/jarvis-research/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/supabase/functions/jarvis-research/index.ts b/supabase/functions/jarvis-research/index.ts index fa9c848..5909afd 100644 --- a/supabase/functions/jarvis-research/index.ts +++ b/supabase/functions/jarvis-research/index.ts @@ -1,5 +1,5 @@ import "jsr:@supabase/functions-js/edge-runtime.d.ts"; -import { createClient } from "https://esm.sh/@supabase/supabase-js@2"; +import { createClient } from "https://esm.sh/@supabase/supabase-js@2.119.0"; const cors={"Access-Control-Allow-Origin":Deno.env.get("APP_ORIGIN")||"*","Access-Control-Allow-Headers":"authorization, x-client-info, apikey, content-type","Access-Control-Allow-Methods":"POST, OPTIONS","Content-Type":"application/json"}; const json=(body:unknown,status=200)=>new Response(JSON.stringify(body),{status,headers:cors}); Deno.serve(async(req:Request)=>{ From 4489f301008714debdcdf4537f84f8f6c5e3d7cd Mon Sep 17 00:00:00 2001 From: Yashvant Date: Sat, 7 Nov 2026 09:15:21 +0530 Subject: [PATCH 122/204] Add live research workspace with citations --- src/lib/workspace.js | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/src/lib/workspace.js b/src/lib/workspace.js index a5d8f23..69c96aa 100644 --- a/src/lib/workspace.js +++ b/src/lib/workspace.js @@ -6,7 +6,7 @@ const esc = (s='') => String(s).replace(/[&<>"']/g,c=>({'&':'&','<':'<',' export function initializeWorkspace({supabase,getUser,toast,onChat}) { const area=$('#messages'); let lastPlan=null; - const labels={chat:'Chats',projects:'Projects',tools:'Agent studio',memory:'Memory',activity:'Activity',integrations:'Integrations'}; + const labels={chat:'Chats',projects:'Projects',tools:'Agent studio',research:'Research',memory:'Memory',activity:'Activity',integrations:'Integrations'}; async function refresh(){return await loadWorkspace(supabase);} function activate(view){ document.querySelectorAll('[data-view]').forEach(b=>b.classList.toggle('selected',b.dataset.view===view)); @@ -14,6 +14,7 @@ export function initializeWorkspace({supabase,getUser,toast,onChat}) { $('#sidebar').classList.remove('open');$('#sidebar-backdrop').classList.remove('visible'); if(view==='chat'){onChat?.();return;} if(view==='integrations')return renderIntegrations(); + if(view==='research')return renderResearch(); render(view); } async function render(view){ @@ -34,6 +35,25 @@ export function initializeWorkspace({supabase,getUser,toast,onChat}) { area.querySelectorAll('[data-execute]').forEach(b=>b.onclick=async()=>{try{const result=await invokeAgent(supabase,'execute_approved',{approvalId:b.dataset.execute});const url=result.execution?.repository?.url||result.execution?.deploymentUrl;toast(url?`Ready: ${url}`:(result.execution?.message||'Execution submitted.'));render(view);}catch(e){toast(e.message);}}); } } + async function renderResearch(){ + area.innerHTML=`

LIVE WEB KNOWLEDGE

Research

Search the web, compare sources and get a cited summary.

`; + $('#run-research').onclick=async()=>{ + const query=$('#research-query').value.trim();if(!query)return toast('Enter a research question.'); + const button=$('#run-research');button.disabled=true;button.textContent='Researching…'; + try{ + const {data,error}=await supabase.functions.invoke('jarvis-research',{body:{query}}); + if(error)throw new Error(error.message||'Research request failed.'); + if(data?.error)throw new Error(data.error); + const result=$('#research-result'); + const sources=(data.sources||[]).map(source=>{ + const url=/^https?:\/\//i.test(source.url)?source.url:'#'; + return `[${source.number}]
${esc(source.title)}${esc(source.url)}
↗
`; + }).join(''); + result.innerHTML=`
RESEARCH SUMMARY
${esc(data.summary||'No summary returned.').replace(/\n/g,'
')}
SOURCES (${(data.sources||[]).length})
${sources||'
No sources returned.
'}
`; + }catch(e){toast(e.message);} + finally{button.disabled=false;button.textContent='Research with sources ↗';} + }; + } async function renderIntegrations(){ let providers=[];try{const result=await invokeAgent(supabase,'capabilities');providers=result.providers||[];}catch(e){toast(e.message);} area.innerHTML=`

CONNECT YOUR SERVICES

Integrations

Provider credentials belong in secure server-side secrets, never in browser code or chat.

${INTEGRATIONS.map(item=>{const ready=providers.find(p=>p.id===item.id)?.configured||false;return `
${item.id==='github'?'⌘':item.id==='cloudflare'?'☁':item.id==='gemini'?'✳':'◇'}${ready?'Configured':item.setup?'Dashboard setup':'Needs setup'}

${esc(item.name)}

${esc(item.category)}

${item.capabilities.map(esc).join(' · ')}

${item.setup||item.instructions?`
${esc(item.setup||item.instructions)}
`:''}
${item.secrets.map(k=>`${esc(k)}`).join('')}
`}).join('')}
Where do API keys go?

Each provider has its own secure location: Supabase Edge Function Secrets, GitHub Actions Secrets, frontend public configuration, or OAuth provider settings. Follow the instructions shown on each card. Never put private tokens in browser code or chat.

Open Edge Function secrets ↗
`; From 731e8e0697d9bce5b860073d4671a8fa73c75fc1 Mon Sep 17 00:00:00 2001 From: Yashvant Date: Sat, 7 Nov 2026 09:15:28 +0530 Subject: [PATCH 123/204] Add Research navigation to JARVIS X --- index.html | 1 + 1 file changed, 1 insertion(+) diff --git a/index.html b/index.html index 4c658cf..344b4fa 100644 --- a/index.html +++ b/index.html @@ -27,6 +27,7 @@ + From 9274daa91916ac33af299c02e8943cb868253975 Mon Sep 17 00:00:00 2001 From: Yashvant Date: Sat, 7 Nov 2026 09:15:34 +0530 Subject: [PATCH 124/204] Configure Tavily as the JARVIS X web research provider --- src/lib/integrations.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/integrations.js b/src/lib/integrations.js index e35059b..74db98d 100644 --- a/src/lib/integrations.js +++ b/src/lib/integrations.js @@ -4,7 +4,7 @@ export const INTEGRATIONS = [ { id:'cloudflare', name:'Cloudflare', category:'Deployment', required:false, secrets:['CLOUDFLARE_API_TOKEN','CLOUDFLARE_ACCOUNT_ID','JARVIS_GATEWAY_URL'], instructions:'Add CLOUDFLARE_API_TOKEN and CLOUDFLARE_ACCOUNT_ID as GitHub Actions secrets. Use a scoped token for Pages deployment and Worker deployment.', capabilities:['Create Pages projects','Deploy production builds','Run the secure gateway Worker'] }, { id:'supabase', name:'Supabase', category:'Backend', required:true, secrets:[], setup:'Set VITE_SUPABASE_URL and VITE_SUPABASE_PUBLISHABLE_KEY in the frontend host. Keep service-role and secret keys server-side.', capabilities:['Auth, database and private storage','Edge Functions','Realtime and vector search'] }, { id:'google', name:'Google OAuth', category:'Identity', required:false, secrets:[], setup:'Configure the Google OAuth client in Supabase Auth → Providers → Google, then add the Supabase callback URL to Google Cloud Console. Request Gmail scopes only when that feature is enabled.', capabilities:['Google sign-in','Consent-based Gmail integrations'] }, - { id:'search', name:'Web search', category:'Research', required:false, secrets:['SEARCH_API_KEY'], instructions:'Add SEARCH_API_KEY under Supabase Edge Functions → Secrets after selecting the search provider.', capabilities:['Live web search','Source citations','Research reports'] }, + { id:'search', name:'Web search', category:'Research', required:false, secrets:['TAVILY_API_KEY'], instructions:'Add SEARCH_API_KEY under Supabase Edge Functions → Secrets after selecting the search provider.', capabilities:['Live web search','Source citations','Research reports'] }, { id:'image', name:'Image generation', category:'Creative', required:false, secrets:['IMAGE_PROVIDER_API_KEY'], instructions:'Add IMAGE_PROVIDER_API_KEY under Supabase Edge Functions → Secrets if using a separate image provider. Gemini image models may use the Gemini key.', capabilities:['Generate images','Edit reference images'] }, { id:'speech', name:'Voice provider', category:'Voice', required:false, secrets:['TTS_API_KEY'], instructions:'Add TTS_API_KEY under Supabase Edge Functions → Secrets when a dedicated speech provider is selected.', capabilities:['Natural speech output','Multilingual voice'] }, { id:'email', name:'Gmail access', category:'Productivity', required:false, secrets:[], setup:'Use Google OAuth consent for Gmail scopes. Read-only access is separate from draft/send permissions; sending always requires explicit approval.', capabilities:['Search and summarise email','Draft and send after approval'] } From e86d3e09b9e92234ca61caecc1a03e56755188f3 Mon Sep 17 00:00:00 2001 From: Yashvant Date: Sat, 7 Nov 2026 09:15:36 +0530 Subject: [PATCH 125/204] Configure Tavily as the JARVIS X web research provider --- supabase/functions/jarvis-agent/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/supabase/functions/jarvis-agent/index.ts b/supabase/functions/jarvis-agent/index.ts index 6b3cf55..f57e695 100644 --- a/supabase/functions/jarvis-agent/index.ts +++ b/supabase/functions/jarvis-agent/index.ts @@ -2,7 +2,7 @@ import "jsr:@supabase/functions-js/edge-runtime.d.ts"; import { createClient } from "https://esm.sh/@supabase/supabase-js@2.119.0"; const cors={"Access-Control-Allow-Origin":Deno.env.get("APP_ORIGIN")||"*","Access-Control-Allow-Headers":"authorization, x-client-info, apikey, content-type","Access-Control-Allow-Methods":"POST, OPTIONS","Content-Type":"application/json"}; const json=(body:unknown,status=200)=>new Response(JSON.stringify(body),{status,headers:cors}); -const required:Record={gemini:["GEMINI_API_KEY"],github:["GITHUB_TOKEN"],cloudflare:["CLOUDFLARE_API_TOKEN","CLOUDFLARE_ACCOUNT_ID"],search:["SEARCH_API_KEY"],image:["IMAGE_PROVIDER_API_KEY"],speech:["TTS_API_KEY"],email:["EMAIL_PROVIDER_API_KEY"]}; +const required:Record={gemini:["GEMINI_API_KEY"],github:["GITHUB_TOKEN"],cloudflare:["CLOUDFLARE_API_TOKEN","CLOUDFLARE_ACCOUNT_ID"],search:["TAVILY_API_KEY"],image:["IMAGE_PROVIDER_API_KEY"],speech:["TTS_API_KEY"],email:["EMAIL_PROVIDER_API_KEY"]}; const configured=(provider:string)=>(required[provider]||[]).every(key=>Boolean(Deno.env.get(key))); async function gatewayProviders(auth:string):Promise> { const gateway=Deno.env.get("JARVIS_GATEWAY_URL"); From 4cc459472cba885a120bb0a26b11df94a40ce5f8 Mon Sep 17 00:00:00 2001 From: Yashvant Date: Sat, 7 Nov 2026 09:15:39 +0530 Subject: [PATCH 126/204] Configure Tavily as the JARVIS X web research provider --- .env.example | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.env.example b/.env.example index 80d0384..9f2dcf9 100644 --- a/.env.example +++ b/.env.example @@ -7,7 +7,7 @@ VITE_CHAT_FUNCTION_URL=https://tmlrveloeirxsankeqrs.supabase.co/functions/v1/jar # GITHUB_TOKEN= # CLOUDFLARE_API_TOKEN= # CLOUDFLARE_ACCOUNT_ID= -# SEARCH_API_KEY= +# TAVILY_API_KEY= # IMAGE_PROVIDER_API_KEY= # TTS_API_KEY= # EMAIL_PROVIDER_API_KEY= From a55929467692537105462bc64e15b4f981bbd83a Mon Sep 17 00:00:00 2001 From: Yashvant Date: Sat, 7 Nov 2026 09:15:41 +0530 Subject: [PATCH 127/204] Configure Tavily as the JARVIS X web research provider --- docs/PROVIDER-SETUP.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/PROVIDER-SETUP.md b/docs/PROVIDER-SETUP.md index 7a1da5e..77e6f8c 100644 --- a/docs/PROVIDER-SETUP.md +++ b/docs/PROVIDER-SETUP.md @@ -10,7 +10,7 @@ Do not paste secrets into the chat, source code, issue comments or frontend envi ## Supabase Edge Function Secrets Add these under Supabase Dashboard → Edge Functions → Secrets: - GEMINI_API_KEY — required for chat and agent planning. -- SEARCH_API_KEY — selected web-search provider key. +- TAVILY_API_KEY — Tavily web search key used by the Research workspace. - IMAGE_PROVIDER_API_KEY — image provider key if using a separate provider. - TTS_API_KEY — speech provider key if using a separate provider. - APP_ORIGIN — exact deployed frontend origin for CORS, e.g. https://app.example.com. From 018355f1f2434f69398cd68fc4c2b0abcf9c9192 Mon Sep 17 00:00:00 2001 From: Yashvant Date: Sat, 7 Nov 2026 09:15:51 +0530 Subject: [PATCH 128/204] Style live research summaries and source cards --- style.css | 3 +++ 1 file changed, 3 insertions(+) diff --git a/style.css b/style.css index 3c5556d..64998ce 100644 --- a/style.css +++ b/style.css @@ -14,3 +14,6 @@ .message-attachments{display:flex;flex-wrap:wrap;gap:7px;margin-top:8px}.message-attachments span{border:1px solid #e8e1d5;background:#f5f1e8;border-radius:7px;padding:5px 8px;color:#88785c;font-size:9px;max-width:100%;overflow-wrap:anywhere} .plan-card{margin:0 0 24px;padding:17px 19px;background:#f6f1e7;border:1px solid #e8deca;border-radius:13px}.plan-card>b{display:block;color:#62543b;font:600 17px 'Playfair Display',serif}.plan-card ol{padding-left:20px;margin:12px 0 0}.plan-card li{padding:5px 0;color:#8e7a56;font-size:11px}.plan-card li strong{color:#5e584c}.plan-card li small{display:block;margin-top:4px;color:#969083;font-size:10px} + +.research-launch{display:flex;flex-direction:column;gap:11px;padding:17px;background:#f4f0e7;border:1px solid #e8e0d1;border-radius:15px;margin-bottom:25px}.research-launch textarea{width:100%;border:1px solid #e8e1d5;border-radius:10px;background:#fffefa;padding:13px;color:#514c42;font:12px/1.6 'DM Sans',sans-serif;resize:vertical;outline:0}.research-launch textarea:focus{border-color:#c6b28f}.research-launch .gold-button{align-self:flex-end}.research-answer{background:#fffefa;border:1px solid #e9e3d8;border-radius:14px;padding:18px 20px;margin-bottom:20px}.research-copy{font-size:12px;line-height:1.9;color:#514d45;overflow-wrap:anywhere}.source-list{display:grid;gap:8px}.source-card{display:flex;align-items:center;gap:11px;padding:12px 14px;background:#fffefa;border:1px solid #ebe5da;border-radius:10px;text-decoration:none;color:#5b564c}.source-card>span:first-child{color:#a58a5c;font-size:11px;font-weight:700}.source-card>span:last-child{margin-left:auto;color:#a58a5c}.source-card div{min-width:0;flex:1}.source-card b,.source-card small{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.source-card b{font-size:11px}.source-card small{margin-top:4px;color:#a19a8e;font-size:9px}.source-card:hover{border-color:#cbbb9e;background:#fffdf8} +@media(max-width:800px){.research-launch .gold-button{align-self:stretch}.research-answer{padding:14px}} From 24d495aa6c2355d4dc75030e9e93dd62c29f8cc9 Mon Sep 17 00:00:00 2001 From: Yashvant Date: Sat, 7 Nov 2026 09:16:06 +0530 Subject: [PATCH 129/204] Add private Gemini image generation endpoint --- supabase/functions/jarvis-image/index.ts | 39 ++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 supabase/functions/jarvis-image/index.ts diff --git a/supabase/functions/jarvis-image/index.ts b/supabase/functions/jarvis-image/index.ts new file mode 100644 index 0000000..72c8396 --- /dev/null +++ b/supabase/functions/jarvis-image/index.ts @@ -0,0 +1,39 @@ +import "jsr:@supabase/functions-js/edge-runtime.d.ts"; +import { createClient } from "https://esm.sh/@supabase/supabase-js@2.119.0"; +const cors={"Access-Control-Allow-Origin":Deno.env.get("APP_ORIGIN")||"*","Access-Control-Allow-Headers":"authorization, x-client-info, apikey, content-type","Access-Control-Allow-Methods":"POST, OPTIONS","Content-Type":"application/json"}; +const json=(body:unknown,status=200)=>new Response(JSON.stringify(body),{status,headers:cors}); +Deno.serve(async(req:Request)=>{ + if(req.method==="OPTIONS")return new Response("ok",{headers:cors}); + if(req.method!=="POST")return json({error:"Method not allowed"},405); + const auth=req.headers.get("Authorization")||"";if(!auth.startsWith("Bearer "))return json({error:"Sign in required."},401); + const key=Deno.env.get("SUPABASE_ANON_KEY")||Deno.env.get("SUPABASE_PUBLISHABLE_KEY")||""; + if(!key)return json({error:"Supabase publishable key is missing."},503); + const supabase=createClient(Deno.env.get("SUPABASE_URL")!,key,{global:{headers:{Authorization:auth}}}); + const {data:{user},error:authError}=await supabase.auth.getUser();if(authError||!user)return json({error:"Invalid session."},401); + const apiKey=Deno.env.get("GEMINI_API_KEY");if(!apiKey)return json({error:"GEMINI_API_KEY is not configured."},503); + try{ + const body=await req.json(),prompt=String(body.prompt||"").trim().slice(0,4000); + if(!prompt)return json({error:"Describe the image you want to create."},400); + const allowed=["1:1","3:2","2:3","3:4","4:3","4:5","5:4","9:16","16:9","21:9"]; + const aspectRatio=allowed.includes(body.aspectRatio)?body.aspectRatio:"1:1"; + const response=await fetch("https://generativelanguage.googleapis.com/v1beta/models/gemini-3.1-flash-image:generateContent",{method:"POST",headers:{"x-goog-api-key":apiKey,"Content-Type":"application/json"},body:JSON.stringify({contents:[{parts:[{text:prompt}]}],generationConfig:{responseFormat:{image:{aspectRatio}}}})}); + const result=await response.json();if(!response.ok)return json({error:result.error?.message||"Image generation failed."},502); + const parts=result.candidates?.[0]?.content?.parts||[]; + const imagePart=parts.find((part:any)=>part.inlineData?.data&&String(part.inlineData.mimeType||"").startsWith("image/")); + if(!imagePart)return json({error:"The image model returned no image. Try a clearer prompt."},502); + const binary=atob(imagePart.inlineData.data),bytes=new Uint8Array(binary.length); + for(let i=0;i Date: Sat, 7 Nov 2026 09:16:15 +0530 Subject: [PATCH 130/204] Use Gemini as the default image generation provider --- src/lib/integrations.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/integrations.js b/src/lib/integrations.js index 74db98d..a53dfa0 100644 --- a/src/lib/integrations.js +++ b/src/lib/integrations.js @@ -5,7 +5,7 @@ export const INTEGRATIONS = [ { id:'supabase', name:'Supabase', category:'Backend', required:true, secrets:[], setup:'Set VITE_SUPABASE_URL and VITE_SUPABASE_PUBLISHABLE_KEY in the frontend host. Keep service-role and secret keys server-side.', capabilities:['Auth, database and private storage','Edge Functions','Realtime and vector search'] }, { id:'google', name:'Google OAuth', category:'Identity', required:false, secrets:[], setup:'Configure the Google OAuth client in Supabase Auth → Providers → Google, then add the Supabase callback URL to Google Cloud Console. Request Gmail scopes only when that feature is enabled.', capabilities:['Google sign-in','Consent-based Gmail integrations'] }, { id:'search', name:'Web search', category:'Research', required:false, secrets:['TAVILY_API_KEY'], instructions:'Add SEARCH_API_KEY under Supabase Edge Functions → Secrets after selecting the search provider.', capabilities:['Live web search','Source citations','Research reports'] }, - { id:'image', name:'Image generation', category:'Creative', required:false, secrets:['IMAGE_PROVIDER_API_KEY'], instructions:'Add IMAGE_PROVIDER_API_KEY under Supabase Edge Functions → Secrets if using a separate image provider. Gemini image models may use the Gemini key.', capabilities:['Generate images','Edit reference images'] }, + { id:'image', name:'Image generation', category:'Creative', required:false, secrets:['GEMINI_API_KEY'], instructions:'Uses the same GEMINI_API_KEY as chat. Image generation may require paid API billing or an enabled model in your Google AI project.', capabilities:['Generate images','Edit reference images'] }, { id:'speech', name:'Voice provider', category:'Voice', required:false, secrets:['TTS_API_KEY'], instructions:'Add TTS_API_KEY under Supabase Edge Functions → Secrets when a dedicated speech provider is selected.', capabilities:['Natural speech output','Multilingual voice'] }, { id:'email', name:'Gmail access', category:'Productivity', required:false, secrets:[], setup:'Use Google OAuth consent for Gmail scopes. Read-only access is separate from draft/send permissions; sending always requires explicit approval.', capabilities:['Search and summarise email','Draft and send after approval'] } ]; From 17339bb87f2969ed79f6f25101800a5151475022 Mon Sep 17 00:00:00 2001 From: Yashvant Date: Sat, 7 Nov 2026 09:16:17 +0530 Subject: [PATCH 131/204] Report image generation ready with Gemini API key --- supabase/functions/jarvis-agent/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/supabase/functions/jarvis-agent/index.ts b/supabase/functions/jarvis-agent/index.ts index f57e695..257d17d 100644 --- a/supabase/functions/jarvis-agent/index.ts +++ b/supabase/functions/jarvis-agent/index.ts @@ -2,7 +2,7 @@ import "jsr:@supabase/functions-js/edge-runtime.d.ts"; import { createClient } from "https://esm.sh/@supabase/supabase-js@2.119.0"; const cors={"Access-Control-Allow-Origin":Deno.env.get("APP_ORIGIN")||"*","Access-Control-Allow-Headers":"authorization, x-client-info, apikey, content-type","Access-Control-Allow-Methods":"POST, OPTIONS","Content-Type":"application/json"}; const json=(body:unknown,status=200)=>new Response(JSON.stringify(body),{status,headers:cors}); -const required:Record={gemini:["GEMINI_API_KEY"],github:["GITHUB_TOKEN"],cloudflare:["CLOUDFLARE_API_TOKEN","CLOUDFLARE_ACCOUNT_ID"],search:["TAVILY_API_KEY"],image:["IMAGE_PROVIDER_API_KEY"],speech:["TTS_API_KEY"],email:["EMAIL_PROVIDER_API_KEY"]}; +const required:Record={gemini:["GEMINI_API_KEY"],github:["GITHUB_TOKEN"],cloudflare:["CLOUDFLARE_API_TOKEN","CLOUDFLARE_ACCOUNT_ID"],search:["TAVILY_API_KEY"],image:["GEMINI_API_KEY"],speech:["TTS_API_KEY"],email:["EMAIL_PROVIDER_API_KEY"]}; const configured=(provider:string)=>(required[provider]||[]).every(key=>Boolean(Deno.env.get(key))); async function gatewayProviders(auth:string):Promise> { const gateway=Deno.env.get("JARVIS_GATEWAY_URL"); From 121384d117ce79f0f6afd5d92d293f5a3e353dff Mon Sep 17 00:00:00 2001 From: Yashvant Date: Sat, 7 Nov 2026 09:16:29 +0530 Subject: [PATCH 132/204] Add private Gemini image studio UI --- src/lib/workspace.js | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/lib/workspace.js b/src/lib/workspace.js index 69c96aa..d118425 100644 --- a/src/lib/workspace.js +++ b/src/lib/workspace.js @@ -6,7 +6,7 @@ const esc = (s='') => String(s).replace(/[&<>"']/g,c=>({'&':'&','<':'<',' export function initializeWorkspace({supabase,getUser,toast,onChat}) { const area=$('#messages'); let lastPlan=null; - const labels={chat:'Chats',projects:'Projects',tools:'Agent studio',research:'Research',memory:'Memory',activity:'Activity',integrations:'Integrations'}; + const labels={chat:'Chats',projects:'Projects',tools:'Agent studio',research:'Research',images:'Image studio',memory:'Memory',activity:'Activity',integrations:'Integrations'}; async function refresh(){return await loadWorkspace(supabase);} function activate(view){ document.querySelectorAll('[data-view]').forEach(b=>b.classList.toggle('selected',b.dataset.view===view)); @@ -15,6 +15,7 @@ export function initializeWorkspace({supabase,getUser,toast,onChat}) { if(view==='chat'){onChat?.();return;} if(view==='integrations')return renderIntegrations(); if(view==='research')return renderResearch(); + if(view==='images')return renderImageStudio(); render(view); } async function render(view){ @@ -35,6 +36,20 @@ export function initializeWorkspace({supabase,getUser,toast,onChat}) { area.querySelectorAll('[data-execute]').forEach(b=>b.onclick=async()=>{try{const result=await invokeAgent(supabase,'execute_approved',{approvalId:b.dataset.execute});const url=result.execution?.repository?.url||result.execution?.deploymentUrl;toast(url?`Ready: ${url}`:(result.execution?.message||'Execution submitted.'));render(view);}catch(e){toast(e.message);}}); } } + async function renderImageStudio(){ + area.innerHTML=`

CREATIVE STUDIO

Image studio

Create images from a prompt and keep them in your private workspace.

Image generation uses your Gemini API key. Provider billing or model access may be required.
`; + $('#generate-image').onclick=async()=>{ + const prompt=$('#image-prompt').value.trim();if(!prompt)return toast('Describe the image first.'); + const button=$('#generate-image');button.disabled=true;button.textContent='Creating image…'; + try{ + const {data,error}=await supabase.functions.invoke('jarvis-image',{body:{prompt,aspectRatio:$('#image-ratio').value}}); + if(error)throw new Error(error.message||'Image generation failed.'); + if(data?.error)throw new Error(data.error); + $('#image-result').innerHTML=`
${esc(data.fileName||'Generated image')}
${esc(data.fileName||'Generated image')}Saved privately · ${Math.round((data.size||0)/1024)} KBOpen image ↗
`; + }catch(e){toast(e.message);} + finally{button.disabled=false;button.textContent='Generate image ↗';} + }; + } async function renderResearch(){ area.innerHTML=`

LIVE WEB KNOWLEDGE

Research

Search the web, compare sources and get a cited summary.

`; $('#run-research').onclick=async()=>{ From c31e451c9b69752ffc3992ad48a7e5a020c9a8ba Mon Sep 17 00:00:00 2001 From: Yashvant Date: Sat, 7 Nov 2026 09:16:33 +0530 Subject: [PATCH 133/204] Add Image studio navigation --- index.html | 1 + 1 file changed, 1 insertion(+) diff --git a/index.html b/index.html index 344b4fa..c772cc8 100644 --- a/index.html +++ b/index.html @@ -28,6 +28,7 @@ + From eed531218f2ffcb7a5be4c7bb2d7bbc2ab149af1 Mon Sep 17 00:00:00 2001 From: Yashvant Date: Sat, 7 Nov 2026 09:16:39 +0530 Subject: [PATCH 134/204] Style private image generation studio --- style.css | 3 +++ 1 file changed, 3 insertions(+) diff --git a/style.css b/style.css index 64998ce..7ec8ba6 100644 --- a/style.css +++ b/style.css @@ -17,3 +17,6 @@ .research-launch{display:flex;flex-direction:column;gap:11px;padding:17px;background:#f4f0e7;border:1px solid #e8e0d1;border-radius:15px;margin-bottom:25px}.research-launch textarea{width:100%;border:1px solid #e8e1d5;border-radius:10px;background:#fffefa;padding:13px;color:#514c42;font:12px/1.6 'DM Sans',sans-serif;resize:vertical;outline:0}.research-launch textarea:focus{border-color:#c6b28f}.research-launch .gold-button{align-self:flex-end}.research-answer{background:#fffefa;border:1px solid #e9e3d8;border-radius:14px;padding:18px 20px;margin-bottom:20px}.research-copy{font-size:12px;line-height:1.9;color:#514d45;overflow-wrap:anywhere}.source-list{display:grid;gap:8px}.source-card{display:flex;align-items:center;gap:11px;padding:12px 14px;background:#fffefa;border:1px solid #ebe5da;border-radius:10px;text-decoration:none;color:#5b564c}.source-card>span:first-child{color:#a58a5c;font-size:11px;font-weight:700}.source-card>span:last-child{margin-left:auto;color:#a58a5c}.source-card div{min-width:0;flex:1}.source-card b,.source-card small{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.source-card b{font-size:11px}.source-card small{margin-top:4px;color:#a19a8e;font-size:9px}.source-card:hover{border-color:#cbbb9e;background:#fffdf8} @media(max-width:800px){.research-launch .gold-button{align-self:stretch}.research-answer{padding:14px}} + +.image-controls{display:flex;align-items:center;justify-content:space-between;gap:12px}.image-controls label{font-size:10px;color:#8d8578}.image-controls select{margin-left:8px;border:1px solid #e5ded2;border-radius:8px;background:#fffefa;padding:8px 10px;color:#6f6657;font:11px 'DM Sans',sans-serif}.image-note{color:#a49c8f;font-size:9px;line-height:1.6}.generated-image-card{max-width:700px;background:#fffefa;border:1px solid #e9e3d8;border-radius:15px;overflow:hidden;box-shadow:0 8px 24px #766c5509}.generated-image-card img{display:block;width:100%;height:auto;max-height:680px;object-fit:contain;background:#f3efe7}.generated-image-card>div{display:flex;align-items:center;gap:12px;padding:12px 15px}.generated-image-card b{font-size:11px;color:#5a554a}.generated-image-card small{font-size:9px;color:#a39b8d;flex:1}.generated-image-card a{font-size:10px;color:#987b4e;text-decoration:none} +@media(max-width:800px){.image-controls{align-items:flex-start;flex-direction:column}.image-controls .gold-button{width:100%}.generated-image-card>div{flex-wrap:wrap}} From a492585a8bc5d779130075cafdcccbe5f4d30f8e Mon Sep 17 00:00:00 2001 From: Yashvant Date: Sat, 7 Nov 2026 09:16:45 +0530 Subject: [PATCH 135/204] Remove redundant image API key placeholder --- .env.example | 1 - 1 file changed, 1 deletion(-) diff --git a/.env.example b/.env.example index 9f2dcf9..95124e7 100644 --- a/.env.example +++ b/.env.example @@ -8,7 +8,6 @@ VITE_CHAT_FUNCTION_URL=https://tmlrveloeirxsankeqrs.supabase.co/functions/v1/jar # CLOUDFLARE_API_TOKEN= # CLOUDFLARE_ACCOUNT_ID= # TAVILY_API_KEY= -# IMAGE_PROVIDER_API_KEY= # TTS_API_KEY= # EMAIL_PROVIDER_API_KEY= # APP_ORIGIN= From 3b08dac28d49e1cc7574634925c6d434b0b2dade Mon Sep 17 00:00:00 2001 From: Yashvant Date: Sat, 7 Nov 2026 09:16:47 +0530 Subject: [PATCH 136/204] Document Gemini image generation setup --- docs/PROVIDER-SETUP.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/PROVIDER-SETUP.md b/docs/PROVIDER-SETUP.md index 77e6f8c..13657c1 100644 --- a/docs/PROVIDER-SETUP.md +++ b/docs/PROVIDER-SETUP.md @@ -11,7 +11,7 @@ Do not paste secrets into the chat, source code, issue comments or frontend envi Add these under Supabase Dashboard → Edge Functions → Secrets: - GEMINI_API_KEY — required for chat and agent planning. - TAVILY_API_KEY — Tavily web search key used by the Research workspace. -- IMAGE_PROVIDER_API_KEY — image provider key if using a separate provider. +- Image generation uses GEMINI_API_KEY with Gemini 3.1 Flash Image; the image model may require billing or model access in the Google AI project. - TTS_API_KEY — speech provider key if using a separate provider. - APP_ORIGIN — exact deployed frontend origin for CORS, e.g. https://app.example.com. - JARVIS_GATEWAY_URL — deployed Cloudflare Worker gateway URL used by the agent after approval. From 7c016afd86fb151f4f05af70ef2d483caa76be97 Mon Sep 17 00:00:00 2001 From: Yashvant Date: Sat, 7 Nov 2026 09:16:49 +0530 Subject: [PATCH 137/204] Update JARVIS X feature overview --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index ea38304..5e0c235 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,8 @@ A light-first, responsive personal AI workspace with a pearl, ivory and champagn - Supabase Auth (Google OAuth flow), private conversation history and owner-scoped database policies. - Projects, personal memories, agent task/approval screens and integration status panel. - Private file bucket with per-user storage policies. -- Gemini chat Edge Function and authenticated agent orchestration endpoint. +- Gemini chat, private image generation and Tavily research Edge Functions. +- Authenticated agent planning, approval queue and a Cloudflare Worker gateway for GitHub repository creation and Pages deployment. - Feature registry and integration secret checklist. - Build and unit-test workflow. From 04587c79bc9a25477d5bb26731a6a7ca2724203c Mon Sep 17 00:00:00 2001 From: Yashvant Date: Sat, 7 Nov 2026 09:17:00 +0530 Subject: [PATCH 138/204] Add persisted personal AI instructions --- .../migrations/202611070003_jarvis_x_personal_preferences.sql | 1 + 1 file changed, 1 insertion(+) create mode 100644 supabase/migrations/202611070003_jarvis_x_personal_preferences.sql diff --git a/supabase/migrations/202611070003_jarvis_x_personal_preferences.sql b/supabase/migrations/202611070003_jarvis_x_personal_preferences.sql new file mode 100644 index 0000000..89eed0f --- /dev/null +++ b/supabase/migrations/202611070003_jarvis_x_personal_preferences.sql @@ -0,0 +1 @@ +alter table public.jarvis_preferences add column if not exists custom_instructions text not null default ''; From a8f318a2a950f71693bebf3f3c696753b83e5a61 Mon Sep 17 00:00:00 2001 From: Yashvant Date: Sat, 7 Nov 2026 09:17:09 +0530 Subject: [PATCH 139/204] Add personal settings and custom instructions --- src/lib/workspace.js | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/src/lib/workspace.js b/src/lib/workspace.js index d118425..3f0d1e1 100644 --- a/src/lib/workspace.js +++ b/src/lib/workspace.js @@ -6,7 +6,7 @@ const esc = (s='') => String(s).replace(/[&<>"']/g,c=>({'&':'&','<':'<',' export function initializeWorkspace({supabase,getUser,toast,onChat}) { const area=$('#messages'); let lastPlan=null; - const labels={chat:'Chats',projects:'Projects',tools:'Agent studio',research:'Research',images:'Image studio',memory:'Memory',activity:'Activity',integrations:'Integrations'}; + const labels={chat:'Chats',projects:'Projects',tools:'Agent studio',research:'Research',images:'Image studio',memory:'Memory',activity:'Activity',integrations:'Integrations',settings:'Settings'}; async function refresh(){return await loadWorkspace(supabase);} function activate(view){ document.querySelectorAll('[data-view]').forEach(b=>b.classList.toggle('selected',b.dataset.view===view)); @@ -16,6 +16,7 @@ export function initializeWorkspace({supabase,getUser,toast,onChat}) { if(view==='integrations')return renderIntegrations(); if(view==='research')return renderResearch(); if(view==='images')return renderImageStudio(); + if(view==='settings')return renderSettings(); render(view); } async function render(view){ @@ -36,6 +37,23 @@ export function initializeWorkspace({supabase,getUser,toast,onChat}) { area.querySelectorAll('[data-execute]').forEach(b=>b.onclick=async()=>{try{const result=await invokeAgent(supabase,'execute_approved',{approvalId:b.dataset.execute});const url=result.execution?.repository?.url||result.execution?.deploymentUrl;toast(url?`Ready: ${url}`:(result.execution?.message||'Execution submitted.'));render(view);}catch(e){toast(e.message);}}); } } + async function renderSettings(){ + const user=getUser();let prefs=null; + const {data,error}=await supabase.from('jarvis_preferences').select('*').eq('user_id',user.id).maybeSingle(); + if(error){toast(error.message);return;}prefs=data||{}; + area.innerHTML=`

YOUR PERSONAL WORKSPACE

Settings

Set how JARVIS X communicates and works with you.

AppearancePearl · Ivory · Champagne — light theme only
`; + $('#pref-language').value=prefs.preferred_language||'auto'; + $('#pref-model').value=prefs.selected_model||'gemini-2.5-flash'; + $('#pref-instructions').value=prefs.custom_instructions||''; + $('#pref-voice').checked=prefs.settings?.voice_output!==false; + $('#save-preferences').onclick=async()=>{ + const selectedModel=$('#pref-model').value; + const {error:saveError}=await supabase.from('jarvis_preferences').upsert({user_id:user.id,preferred_language:$('#pref-language').value,selected_model:selectedModel,custom_instructions:$('#pref-instructions').value.trim(),settings:{voice_output:$('#pref-voice').checked},updated_at:new Date().toISOString()}); + if(saveError)return toast(saveError.message); + const modelSelect=$('#model-select');if(modelSelect){modelSelect.value=selectedModel;modelSelect.dispatchEvent(new Event('change'));} + toast('Preferences saved.'); + }; + } async function renderImageStudio(){ area.innerHTML=`

CREATIVE STUDIO

Image studio

Create images from a prompt and keep them in your private workspace.

Image generation uses your Gemini API key. Provider billing or model access may be required.
`; $('#generate-image').onclick=async()=>{ @@ -74,6 +92,6 @@ export function initializeWorkspace({supabase,getUser,toast,onChat}) { area.innerHTML=`

CONNECT YOUR SERVICES

Integrations

Provider credentials belong in secure server-side secrets, never in browser code or chat.

${INTEGRATIONS.map(item=>{const ready=providers.find(p=>p.id===item.id)?.configured||false;return `
${item.id==='github'?'⌘':item.id==='cloudflare'?'☁':item.id==='gemini'?'✳':'◇'}${ready?'Configured':item.setup?'Dashboard setup':'Needs setup'}

${esc(item.name)}

${esc(item.category)}

${item.capabilities.map(esc).join(' · ')}

${item.setup||item.instructions?`
${esc(item.setup||item.instructions)}
`:''}
${item.secrets.map(k=>`${esc(k)}`).join('')}
`}).join('')}
Where do API keys go?

Each provider has its own secure location: Supabase Edge Function Secrets, GitHub Actions Secrets, frontend public configuration, or OAuth provider settings. Follow the instructions shown on each card. Never put private tokens in browser code or chat.

Open Edge Function secrets ↗
`; } document.querySelectorAll('[data-view]').forEach(b=>b.addEventListener('click',()=>activate(b.dataset.view))); - $('.settings-button')?.addEventListener('click',()=>activate('integrations')); + $('.settings-button')?.addEventListener('click',()=>activate('settings')); window.addEventListener('jarvis:navigate',e=>activate(e.detail)); } From b0a3be70ef6a06b7876abe8a05bb39a6cd65a928 Mon Sep 17 00:00:00 2001 From: Yashvant Date: Sat, 7 Nov 2026 09:17:14 +0530 Subject: [PATCH 140/204] Add Settings navigation --- index.html | 1 + 1 file changed, 1 insertion(+) diff --git a/index.html b/index.html index c772cc8..825388a 100644 --- a/index.html +++ b/index.html @@ -32,6 +32,7 @@ +
RECENT