Inspiration and What it does
Before a film shoots, somebody sits down with the screenplay and reads it for objects. Not for story for things. A scene says he grabbed the cup and threw it at the mirror, and they write down: cup, mirror. The mirror breaks, so make it several mirrors. That is a real job on a real production, and it takes days. Then somebody has to find each of those objects, price them, and email strangers about the ones you cannot just buy.
This is Greenlit. It does that job, and then it keeps going — for days, over real email, while nobody is watching. And it never buys anything.
How we built it
We split Greenlit into two deliberately separate roles.
main-agent/ is the brain. It uses Gemini through Google ADK for five focused jobs: extracting props from a screenplay, researching suppliers and price bands, extracting quotes from replies, deciding the next negotiation move, and briefing the producer. It has no access to Gmail, Firestore, the clock, or purchase orders. It receives context and returns structured decisions.
orchestrator/ is the runtime. Built with Python, FastAPI, Firestore, Cloud Run, Cloud Scheduler, and the Gmail API, it owns the real-world work: storing project state, reading and sending email, scheduling follow-ups, handling supplier replies, and moving negotiations through a state machine. Cloud Scheduler calls a /tick endpoint every minute. Each tick checks Gmail, saves replies by Gmail thread ID, claims due work atomically, asks the brain what to do, and sends any approved next message.
That separation matters because negotiations can last days. Nothing important lives in memory: Firestore holds every item, supplier, email draft, quote, and next action. If a Cloud Run instance dies halfway through a task, the next tick resumes from the database. Conditional row-claiming prevents overlapping ticks from sending duplicate emails.
The web app is React, Vite, and TypeScript. It listens to Firestore snapshots, so the producer sees prop breakdowns, source links, email drafts, replies, negotiations, and savings update live without refreshing. Producers confirm the extracted props, choose whether an item should be bought or negotiated, review the first email to each stranger, and approve every final purchase decision.
Research uses the Parallel web API to find source-backed pricing and suppliers; Gemini reasons over that evidence. We store source URLs alongside price ranges so the producer can inspect where a recommendation came from instead of trusting an unsupported number.
We treated “never buys anything” as an infrastructure problem. Purchase orders live in a separate Firestore database. The agent’s service account has no IAM access to that database, and the approval endpoint runs as a separate Cloud Run service under a separate identity. Orders are created once, keyed by item ID, so duplicate orders are rejected by the storage layer itself.
Finally, we built the project to be testable under real timing constraints: one injected clock replaces wall-clock reads, Firestore emulator tests cover state transitions and rules, and end-to-end tests prove that a screenplay can become researched items and live negotiations without hand-seeding data.
contracts/ The Role A / Role B interface. Both sides import it.
main-agent/ Role A. The brain: five LLM-backed functions, parallel api.
orchestrator/ Role B. Clock, Firestore, Gmail, state machine, tick loop.
web/ Role B. React + Vite + TypeScript, live on Firestore snapshots.
scripts/ OAuth flow, seeding, the wall-clock guard.
docs/ Runbooks.
Challenges we ran into
The hard part was not making an agent write an email. It was making it survive the time between emails.
A real supplier might reply tomorrow, or next week. Cloud Run instances disappear; Cloud Scheduler ticks can overlap. So every step had to be resumable from Firestore, and every due item is claimed with a conditional write before the agent can email anyone twice.
Gmail also surfaced the sort of problems mocks hide: Gmail API IDs are not the RFC Message-ID values needed for real email threading, polling must paginate, and OAuth refresh tokens are an operational concern when negotiations last days.
The biggest challenge was proving the safety boundary. Firestore security rules do not constrain server SDKs, and IAM cannot limit access by collection. We moved purchase orders into a separate Firestore database, gave the agent no access to it, and deployed approval as a separate service with a different identity.
Accomplishments that we're proud of
- A screenplay upload becomes an auditable prop list, with the exact screenplay lines that justify each item.
- The producer approves both the prop list and every first outreach email before the system contacts anyone.
- Real Gmail round trips work end to end: send, receive, thread the reply, update the negotiation, and reflect it live in the UI.
- The agent continues negotiations unattended, but it always stops at
READY_FOR_HUMANbefore a purchase. - “The agent cannot spend money” is enforced by infrastructure, not a prompt: separate database, separate service account, immutable purchase orders keyed by item.
- The system has a deterministic clock, kill-mid-run recovery tests, overlap protection, and a full offline test suite—so a five-day workflow can be tested in seconds.
What we learned
Long-running agents are state machines, not chat sessions. Their memory needs to be durable, their actions need to be idempotent, and time needs to be injectable if you want to test reality without waiting for reality.
We also learned that human-in-the-loop is most useful at consequential boundaries. Producers should review the first message sent in their name and every purchase decision; requiring approval for every counteroffer would defeat the reason to automate a multi-day negotiation.
Most importantly, prompts are not security controls. If an agent must never spend money, its credentials and infrastructure need to make that action impossible.
What's next for Greenlit
Next, we want to run Greenlit through real production workflows: more screenplay formats, richer quantity and schedule planning, supplier verification, and better handoff into a production team’s purchasing process.
We also plan to build a compressed replay harness for safely demonstrating and testing week-long negotiations, expand the supplier and pricing research layer, and make the approval/audit trail useful beyond a hackathon demo.
Built With
- fastapi
- firestore
- gemini
- gmail
- google-adk
- parallel
- python
- react
- typescript
- vite
Log in or sign up for Devpost to join the conversation.