A key that OrcaRouter refuses is forgotten when it came from the key file, and asked for again; a key from the
environment is named by its variable and not forgotten. Ctrl+C leaves at every prompt, and leaving while a prompt
is open no longer raises NoMatches from the heading. Log lines wrap to the whole width, and Quit stays on the
footer at 80 columns. Database and model failures say what happened in plain words, with the detail after them.
Files saved with a byte-order mark are read. The README gives the install commands checked in new venvs, the
index size from the manifest, and how to change the key. CI runs the unit job on Python 3.11 and 3.12 on Ubuntu,
Windows and macOS; gate still needs all of it. The classifiers name the three systems.
The form model may click a captcha shown as a box to tick (Turnstile,
reCAPTCHA), usually at its coordinates, wait a few seconds, read the page
again and try once more if the box is still unticked. A puzzle or image
challenge, a login wall or a closed posting is still blocked. No solving
service and no script on a captcha.
Tests: a click on the captcha's frame passes the guard, a captcha frame
does not count as sending or as touching the form, and a captcha click
followed by a send is handled through fill_form.
The default form model moves to deepseek/deepseek-v4.1-flash: about 3 minutes and 1.5 US cents a form on 11 real sites,
85 of 100 on the hard local forms, level with the best within noise, where glm-5.3-flash took twice as long at three
times the cost.
Enter is refused everywhere except a bare Enter in a text input or textarea whose own placeholder or aria-label asks
for it (Paylocity's skills, Teamtailor's tags). Control+Enter and other combinations stay refused.
One question before a batch is sent (Enter sends, Escape cancels); no per-job confirmation and no --auto-submit.
A form's questions are asked after the others are sent, then the form is tried again.
After the click the verifying model gets the plugin's text as its rules and the page as untrusted text.
Each clicked job keeps its time, final address, verdict and evidence, page text and a screenshot under data/proofs.
Key s lists every job tried with its plain status; applied.csv is rewritten after each attempt, cells that start like a formula are made text.
The model may run script again, by the owner's decision, knowing a script could send a form without the person's yes.
NEVER_OFFERED is gone. The system prompt says script comes only after selectors, coordinates and a screenshot have
failed, and never to send the application. browser_evaluate counts as acting, so a script that leaves the form's host
stops the form as unconfirmed. The docstrings and the README say what holds.
Each of the 12 site plugins gets a last section: what follows a submit,
with the evidence level per site (real send, saved page, documentation or
nothing). No form was submitted for this.
A read that fails after a change says nothing: the page can be loading or still being typed into. It is read again a few
times, and only a page that is read and is on another host stops the form as unconfirmed. Personio's posting goes
straight to its form with apply_path /apply, so its apply link is no longer clicked.
Place words match whole parts of the location, so Wales is not New South Wales.
A location answered no scores 1, out of the top.
Progress counts while descriptions and jevos read; jevos is said to download or start only when it does.
Looking for shows the first wordings of the plan's nodes, not their ids.
A failed jevos verdict is counted once per batch, not taken for no opinion.
A scan's log lines reach the screen from the loop, not from its thread.
A judged posting with no description is marked no text in its status.
jevos weighs the next 100 postings by title with six closed questions; the best 25 by the title's fit and that evidence go to the judge model for a score, and the rest are kept as passed over. The title screen, its status and its tests are gone.
jevos is pinned to jevos-v5 and downloaded at first use, checked by SHA-256 before unpacking. Where it cannot run, the cloud model answers the same questions; where there is no key either, the title decides.
A review found them, each confirmed on the code. Script is no longer offered to the model: requestSubmit?.() passed every
check, and no check can tell what a script will do. Space on a focused control, a new line outside a text area, and an
image input are held back like the rest, and a renamed argument no longer lets Enter through.
The control the model names is clicked only if it is one that sends. A click that answers with an error, or a result that
cannot be read, is unconfirmed and never tried again: a real Ashby form disabled its button and was recorded as failed
when it may have gone.
The browser opens on one profile kept in the person's folder, and the key l opens it for them to sign in to their mail and
any site. Gmail and Outlook are plugins with `when: always`: they join the instructions of every form without making a
site count as supported, and say how to fetch a code or a link, only reading. Their pages come from public documentation
and have not been tried on a real mailbox.
The check on what sends a form knew few languages: Gmail's Senden passed it. It knows the main ones now.
The search plan is a graph of nodes and weighted forms with conditional arcs, after the owner's harrier engine, kept in
aihawk.yaml so it can be tuned. Search scores titles in Python from it, by rarity among the postings of the place, and a
town's name no longer matches where the country column says otherwise.
Judging asks closed questions and computes the score from the answers. Plugins now describe how each site's pages are
built; SmartRecruiters, Teamtailor, JazzHR, BambooHR and Personio are new, from rendered pages.
The browser window is visible. The model gets the server's own instructions, its rungs of coordinates and screenshots,
and any tool it marks read-only; a click at a point goes through the same check as a click on a selector. The form rules
now send certifications and unstated facts back as questions, after a real test ticked them. A daily run tries the
newest invisible-playwright-mcp.
`aihawk resume.pdf` is all a person types. The model reads the resume and works out what to search and the contact
details it states; the list of jobs, best first, fills in on a terminal screen as they are judged; space selects, a
applies, and what a form asks that the files do not say is asked there and remembered. Nothing is sent without y.
Searching the published files over the network took two minutes each time, so the database now publishes a light index
that is downloaded once a day and searched locally. A description is read from the database only for a job being judged.
Filters match whole words, so java is not javascript and roma is not Romania.
A plugin is a Markdown file: the sites it is for, how a posting's link becomes the form's, and what the model must know
about the form. AIHawk applies where a plugin exists and lists the rest, so adding a site takes no code, and a person
can add their own beside aihawk.yaml. The first seven were written from real forms read in the browser, with nothing
filled in. What protects the person stays in apply.py, ahead of any plugin's words.
Reading those forms showed the refusal was wrong for Ashby: its Yes and No answers are untyped buttons inside the form,
which count as submit buttons by HTML's rule, so the model could never have answered them.
roleBase's links ending in a 12-character hex code are not the boards' own ids: of 40 at random, 20 answered 404 and 6
answered 403, against none of 40 of its other links. Dropping them takes the database from 16.2 to 10.2 million postings.
A clone left half-deleted on Windows was read as if whole, so each run now clones to a folder of its own.
list: a Windows console's code page no longer stops it on a company's name.
This repository is AIHawk again. Dots moved to feder-cr/invisible_dots with its history.
Every day a job merges the public job databases into one (pipeline/), one row per posting, and publishes it as a
release. `aihawk scan` queries it in place, so a user scrapes nothing and downloads only what the search reads. `score`
judges jobs against the CV through OrcaRouter, `apply` tailors the CV and fills the form through invisible-playwright-mcp.
The model never sends an application: it fills the form and names the submit control, and AIHawk clicks it after the
person's yes, or by itself with --auto-submit. While the model works, a click on what would send the form and the Enter
key are refused. What the CV and the answers file do not say comes back as a question, never as a guess.
The units were enabled, so on every boot after the first systemd started them at multi-user.target, before
cloud-init's final stage ran install.sh (cloud-final.service is After=multi-user.target): a Dot's serial log shows
dot-agentd and the engine up about two seconds before install.sh, which is what sets the VM proxy's firewall and
tunnel. A Dot with a proxy could go out directly in that window, and install.sh's own comment said the opposite.
The units are no longer enabled (no [Install]): install.sh starts them on every boot after the proxy, as it already
did on the first, and removes the wants links an earlier runtime made. install.sh is also the first point of the
boot that has the seed's config.json, which the same final stage writes.
The engine smoke found its stand-in by fake_mcp_server.py on the command line; installed the way the real server is,
it runs as `python -I -B -m invisible_playwright_mcp`, and that command line is what the checks look for.
test_dependencies names server_capture.py as a module another interpreter runs: its invisible_playwright_mcp import
is the installed server's, not a dependency of the engine.
A Dot keeps the golden image it was made from and boots every new runtime disk, so the engine already talked to
servers other than the one its pinned copy of the interface described. The engine now reads the interface from the
installed server when it starts (nanobot/dots/server_interface.py, server_capture.py run as dot by the server's own
python): the page tools are what that server serves, a page tool it does not serve is not offered (GET /tools says
so), and a Dot whose server cannot be read offers no page tool and its launches say why.
With nothing in the engine tied to a version, the image no longer pins one: each build asks PyPI for the newest
invisible-playwright-mcp, the newest invisible-playwright it accepts and the invisible-core that one pins
(browser-release.ts). The three enter the inputs digest, pins.env and the manifest, which records what was installed.
mcp-requirements.lock and capture-mcp-interface.py go; the copy of the interface becomes a test fixture. The owner's
no-other-browser rule is checked at build on what was actually installed (builder/other-browsers.txt), and the golden
image workflow runs every six hours so a new release is published as a prebuilt image.
The server's snapshot and read_html read again on the page that replaced the one they went to (page.evaluate_read),
and browser_evaluate, the model's code, runs once when its page navigates under it (B285). Under them, through
firefox-45: a page that replaces itself keeps its main world (B286), the real mouse no longer turns a click into a
drag, a session keeps focus when another starts beside it, iframes stay in the frame tree after a few navigations,
go_back/go_forward to a same-document entry answer, a browser that dies ends every wait at once, every input event
is born a few ms before the page sees it, and the first enumerateDevices of a session takes a time the seed
declares. The tools' schemas and descriptions are unchanged: the recaptured interface differs only in its version.
OpenRouter routes a request by the answer it asks for but not by the prompt it carries, so a request sized for
the model's widest window could land on a provider with a narrower one (glm-5.3-flash: Reka at 262144 among
providers at 1048576) and fail there. The providers read with the limits on every turn now route the model's
requests: provider.only is the slugs of the widest window's providers. When none of them can serve, OpenRouter
refuses with "No allowed providers are available" and the turn fails with those words.
invisible-playwright-mcp 0.72.1 drops its last caps: browser_type quotes the value the field kept whole, and an
error detail in its chat and routes comes whole. The tools' schemas are unchanged.