Still Open

Your past weeks of Chrome history left you with a mess of still-open tabs with unknown status. StillOpen classifies the unfinished jobs, files what should survive, and closes those pesky tabs you've been avoiding - out of sight, out of mind. And now you can enjoy better performance that those 30 tabs were eating up!

All Things Agentic · Taskmaster · Live API Medium article: https://medium.com/@gregbenja308/your-tabs-arent-a-topic-they-re-unfinished-work-953d17744e8a

Inspiration

Almost everyone has opened their laptop to the dreaded 30+ Chrome tabs. Some tabs stay out of sentiment - I too am often guilty of hanging on to weeks-old tabs just because I get attached. After all, what if I still need that tab later and I delete it? I can't confidently close my tabs without knowing I truly don't need them anymore. This issue is exacerbated by grouping tabs by topic, like "Online Books", "Coding Tools", and "Taxes".

Because conceptually, a topic is never "finished", so how do we easily know when we can close that group? But unlike a "topic", a "task" can be finished and conceptually resolved. Instead of grouping tabs by topic, lets group by actionable tasks. Ex: “Finish biology reading” can close, while “Online Books” cannot.

That is the friction brought to this hackathon. Chrome’s Tab Organizer asks what a pile is about. OneTab hides URLs. Summarizers chat over the mess and leave forty tabs open. I wanted an agent that groups tabs by task (not topic), asks if the task is done, writes down what should outlive the strip, and closes that pile of tabs. When a task is done, so are the related tabs.

Features

StillOpen is a Chrome extension backed by a Cloud Run agent. It has the following features:

  • Scanning a window's tabs and auto-classifying them into Task groups. Ex: "Finding an Austin Appt to rent" has all your tabs where you browsed Zillow, RedFin, Austin climate, etc.

  • Users can drag and drop tabs into different/new Task groups if desired. Users can also rename a task to their liking.

  • A "Finished" section in the extension for tasks that are done and closed. Hitting "Done" on a task closes all of the tabs under it, which stay in memory for 30 days to restore if needed.

  • Notes for each task, so that you can safely document final decisions (ex: "Chose <some appt URL> for my Austin rented apartment"). That way, you don't have to dig through Chrome history to find relevant tabs to remind you of your takeaways.

  • A chatbox where you can ask other-heuristic questions like "Close any tabs that haven't been used in the last 30 days". Users get a preview of tabs that fit the criteria => they can remove some of them from the purge => StillOpen will close the rest.

  • A "Memory" section for judges/users to see what stillOpen learns from the user

  • "Demo Seed" button to seed example tab data (includes a diverse array of tab topics like house hunting, word definition searches, stackOverflow Qs)

What it Does

StillOpen reads only title, URL, and lastAccessed from tabs — never page HTML, cookies, or history. Gemini 3.5 names tasks from that snapshot: “Find a place in Austin,” not zillow.com. You drag tabs, type notes, hit Done, close!

That click is not a local tabs.remove. The extension calls POST /v1/tasks/finish and an ADK graph runs:

  1. Surveyor redacts the snapshot. Bank / health / gov / school / auth hosts never reach a model.
  2. Framer builds a plan. Intent is inferred on the server from sanitized tabs — a hostile client cannot force a filing by lying about intention.
  3. Clerk (ADK LlmAgent, Gemini 3.5 Flash) drafts the artifact. Your notes go in as trusted text; a Python guard puts them back verbatim if the model rewrites them.
  4. Runner files into Firestore and returns a permalink at /v1/filings/{id}.
  5. Verifier re-reads the filing. It requires a citation (a tab host in the body) and the user’s notes in the body. If either fails, close_tab_ids is empty — the tabs stay open.
  6. Only then does Chrome close the pile. Finished keeps titles, notes, Restore, and links to the filing and an append-only audit: proposed → clerk_draft → runner_file → verifier_ok → close_applied.

How I built it

Four packages, one GCP project, tools isolated by a gateway allowlist.

Piece Role
packages/ext Chrome MV3 workbench (WXT / React)
packages/core Surveyor, Framer, ADK Clerk, Runner, Verifier, MemoryBank, Watch
packages/api FastAPI on Cloud Run
packages/jobs Scheduler → POST /v1/jobs/watch

Gemini 3.5 on Vertex (global) clusters tabs, chats, and drafts. Google ADK is the SequentialAgent clerk → runner → verifier — Clerk may draft; only Python may write; only the Verifier may green-light a close. Firestore holds plans, filings, watches, and plan events. Secret Manager, Cloud Trace, and inline Model Armor sit on the model path. Tab titles are untrusted data (TITLE_IS_DATA). Undo URLs never leave chrome.storage.session.

Judges can ping the same facts the code believes:

  • /healthenv: cloud, gemini: vertex, bank: firestore, run_graph: clerk>runner>verifier
  • /v1/agents/registry — introspected from RUN_GRAPH and the loaded gateway policy (cannot drift from a slide)
  • /v1/filings/{id} — the exact bytes Clerk drafted, with citations
  • /v1/plans/{id}/audit — the chain for that close

Optional second models (Gemma 2 for labels, text-embedding-004 for tab vectors) plug in behind flags. I did not stand up Vertex Vector Search serving — an always-on e2-standard-16 is the wrong cost for a solo hackathon Memory Bank.

Hosted API: https://stillopen-tqodm6o6za-uc.a.run.app (use /health; Cloud Run intercepts /healthz). The unpacked extension talks to that URL by default.

Challenges I ran into

The model wanted to lecture. “Which tabs haven’t been opened in 30 days?” came back as a blurb about an inactive group on the board. There is no such group. Listing unused tabs is now a match list under chat, even when Gemini prefers to explain stale cutoffs.

Close is a privilege, not a side effect. Taskmaster wants autonomy. Auto-closing someone’s bank tab is the wrong kind. The human says done; the Verifier can veto. Watch is the loop that runs without a click. Holding that line was harder than calling Gemini.

Deletion is dangerous! Because deletion is more dangerous than adding tabs, for example, I needed to make a "Restore" option and keep the closed tasks for 30 days in case. Past this, the user can always go into Chrome history if needed.

Accomplishments that we're proud of

  • A Verifier veto: no citation, no verbatim notes, no close.
  • A filing you can click and an audit trail you can read - not a screenshot of “the agent did something.”
  • Bank sites (like Chase) stay blocked_from_model for privacy
  • An agent registry generated from the running gateway
  • A trust model we would load on our own profile: redact first, deny-list, session-only undo URLs, SHA-256 user tokens on write paths, job token for Scheduler.

What we learned

Agents that only write text fail this track. Agents that close tabs without a named task fail the user. The split that works: Gemini names and drafts; Python files and proves; the human says done; Watch runs later.

A 30-day Finished search does not need an embedding model on every keystroke. houseapartments is a synonym. Dedicated Vector Search serving is for another scale — and it will eat the credit grant silently.

What's next for StillOpen

  • Surface Watch outcomes in the side panel when a tracked page actually changes (the tick already runs).
  • Chrome Web Store, so users do not need to load unpacked.
  • Finished search across notes and tab titles - synonyms first, Vertex embeddings at close time if the archive grows.

Built With

  • fastapi
  • firestore
  • gemini
  • gemma
  • model-armor
  • vertex
Share this project:

Updates

Submission history