A browser agent you talk to in Hinglish — that stops and asks before it ever writes anything.
Not a wrapper around one. TrueForge owns the tool-calling loop, the model connection, the MCP connection, and the approval gate. This repository never calls Gemini directly and never drives Playwright directly.
gemini-3-flash-preview is registered as a provider through
TrueForge's settings API. A real Playwright MCP server is registered as a
connector — TrueForge's connector manifest has only a remote
variant, with no stdio transport, so the MCP server runs as its own HTTP
process and the harness dials it.
The gate itself is require_approval_for_tools in the agent
manifest. The harness emits tool.approval_required and will not
proceed until the client resumes the turn with a
user.tool_approval decision.
Most of the thinking in this project went here. Splitting tools into “allowed” and “gated” is not enough, because some tools can reach a write without looking like one.
These execute arbitrary JavaScript in the page. The model could submit a form with a scripted click and the harness would only ever see a read-shaped tool call.
Everything left that can change a page. Listed by name rather than through
TrueForge's @write preset, whose meaning comes from tool
metadata we do not control.
Reads are not gated. Asking a person to approve every page view trains them to approve on reflex — which is how approval gates quietly stop working.
A gate only means something if every route to a write passes through a tool it covers.
Qodo reviews every pull request through a pr-agent workflow. On
PR #4 it landed on the exact line the whole project rests on.
The retry logic refuses to re-run a turn that already called a gated tool, so a transient error can never cause a double submission. It decided that by reading the turn's transcript — and swallowed request failures.
// An empty event list reads as "no gated tools were called". const events = await this.get(/* … */) .catch(() => []); ← fails open
So a network blip while checking whether a turn had already submitted something would let the retry proceed. The guard against double submission could be removed by exactly the kind of transient error it was written to survive.
// Fixed: not knowing is not the same as knowing it was safe. } catch (error) { console.warn(/* … could not read the transcript … */); return true; ← fails closed }
Being wrong in that direction costs one un-retried turn. Being wrong in the other costs a real, unretractable write.
Three further findings were accepted in the same review: an unbounded retry wait, an unguarded turn id, and URL casing on the search keyword.
Stated plainly, because a judge is going to clone the repository and find out anyway.
| # | Milestone | State |
|---|---|---|
| 1 | TrueForge harness + Gemini 3 Flash responding | Works |
| 1b | Browser-control MCP navigating and extracting text | Works |
| 2 | Read the top listings back, typed input | Runs; blocked on naukri by a cookie modal |
| 3 | Draft → approval gate → submit | Policy enforced; runtime flow unscripted |
| 4 | Voice: speech in, speech out | Not built |
| 5 | “Jarvis” status UI | Not built |
The name says voice and the architecture is designed around it, but the audio client is not written. What exists is the agent and the safety model underneath it, driven by typed text.
Its migrations hand import() a bare C:\ path;
Node's ESM loader rejects it and the server dies before it ever listens.
The repository ships a postinstall patch that routes the path through
pathToFileURL().
Not per minute. One agent turn spent three to five, so a model lasted about six turns. Two were exhausted in a single afternoon. Getting a turn from nine requests down to three is why milestones four and five are not here.
There is no hosted instance, and there should not be. TrueForge's standalone mode prints its own warning that it is not hardened for shared internet access, and the agent drives a real Chrome carrying a real logged-in session. Putting that behind a public URL would hand strangers a signed-in browser.
# three terminals npm run harness # TrueForge → localhost:8790 npm run browser # Playwright MCP → localhost:8931 npm run bootstrap # register model + connector # then npm run smoke:browser # agent drives Chrome through MCP npm run read -- 3 # reads the top three listings back tools called: browser_navigate, browser_snapshot model requests: 3 # free tier allows 20/day/model