
Security News
upm Launches as a Fast, Tiny Package Manager Written in TypeScript
upm uses Node.js to deliver fast npm installs in about 250 KB, with a JavaScript API and security defaults.
@dshworks/dsh-ego-browser
Advanced tools
ego lite for DeepSeek Harness, with a memory it keeps: the agent recalls what it learned about a site before it acts, promotes a script that worked into a reusable site tool, and hands you the keyboard through a real dsh prompt when a page needs a human.
___ __ _ ___
/ _ \/ _` |/ _ \ dsh-ego-browser
| __/ (_| | (_) |
\___|\__, |\___/ ego lite for DeepSeek Harness,
|___/ with a memory it keeps
The site-skill memory for dsh browser agents.
7 tools · a store in ego lite's own learnings/ format · verified on dsh 0.1.7-rc.2 · 79 tests, no browser needed · host-only · MIT
What it does · 60-second start · Proof · When to skip · Prior art · llms.txt · 中文
AI agents / LLMs: read llms.txt in this repo — it carries the tool signatures, the store layout, and the three wire facts about ego that the docs get wrong.
ego lite is one browser a human and an agent share: the agent works in its own Space with the user's real logins, the user's tabs stay theirs. This plugin brings it into dsh — and adds the half ego's own README still lists as coming soon.
"Experience accumulation that makes your agent faster the more you use it (coming soon)" — ego lite README
The reader already ships. site.learnContext, site.runTool and
site.runBrowserTool load a learnings/ directory out of whatever
EGO_BROWSER_AGENT_WORKSPACE points at. What was missing is the half that
fills it.
ego_recall ──▶ ego_run ──▶ ego_learn ──▶ ego_site_run
what this one script promote the one call, next
agent already for the whole step that session, instead
learned about browser task worked of rediscovering
the site
│
└──▶ ego_handoff — the page needs a human;
ask them for real, then take control back
The loop closes. The second time you scrape that dashboard, the agent calls a tool it wrote last week instead of rediscovering the page.
ego_recall returns the notes and tool
signatures for a hostname off disk. No browser, no page load, no tokens spent
rediscovering what worked last month.ego_run hands the whole browser task to
ego's runtime in a single script, which is the design ego's own benchmark is
built on.ego_learn turns a working step into a real site
tool, in ego's format, and refuses the ones that will not survive to tomorrow.ego_site_run calls a stored tool by name and
counts the call, so a tool nobody uses is visible as unused.ego_handoff raises a genuine dsh
Continue / Finish task prompt, which is what ego's own hard-stop message
asks the harness to do, and takes control back only when you say so.ego_doctor probes the installed
command instead of trusting the docs, because there are two incompatible
runtimes behind one command name.learnings/ from
the ego skill already on your machine. dsh agent
│ ego_run / ego_site_run ego_recall / ego_learn
▼ │
┌──────────────────────────────┐ ▼
│ dsh-ego-browser (host half) │ ┌──────────────────────────┐
│ · argv + surface probes │ │ learnings/<site>/ │
│ · script wrap + classify │─────────────▶│ manifest.json │
│ · promotion gate │ writes │ notes/*.md │
└──────────────┬───────────────┘ │ tools/*.js │
│ stdin heredoc │ browser-tools/*.js │
▼ └──────────┬───────────────┘
`ego-browser` │ EGO_BROWSER_
│ │ AGENT_WORKSPACE
▼ ▼
ego lite app ◀───────── the same store, read back by ego itself
(your logins, its own Space)
The only wire is the ego-browser command the ego lite app installs. Nothing
here vendors, patches, or launches a browser.
Needs ego lite installed and onboarded on the machine running dsh
(lite.ego.app), so ego-browser is on the PATH.
# 1. install
dsh plugin --profile web add -w @dshworks/dsh-ego-browser
# 2. restart dsh
# 3. verify it took — seven names, or [] if it did not
curl -s localhost:8090/dsh-ego-browser/memory | jq .tools
Running dsh inside a deva container with
the ego bridge? dsh strips every credential-shaped variable (KEY, SECRET,
TOKEN, PASSWORD) from the processes it spawns, so the bridge token has to be
forwarded on purpose, in the profile's cordis.patch.yml:
- id: dsh-ego-browser
config:
env:
DEVA_EGO_TOKEN: !!js process.env.DEVA_EGO_TOKEN
add registers the bundle in the profile roster for you. Then ask the agent to
run ego_doctor once — it is how it finds out whether to write cliLog() or
console.log() for the ego you installed:
command: ego-browser invoked as: ego-browser <<'JS'
ego runtime surface: facades (console.log / page / browser / taskSpaces); 7 helper names visible.
store: ~/.dsh/ego-browser/workspace (3 sites)
github — github.com, *.github.com — 3 tool(s): search_repos (0x), get_open_issues (0x), get_repo_stats (0x)
google — google.com, *.google.com, www.google.com — 2 tool(s): search_and_extract (0x), get_autocomplete_suggestions (0x)
x-com — x.com, *.x.com, twitter.com, *.twitter.com — 3 tool(s): get_timeline_posts (0x), search_users (0x), post_from_active_element (0x)
Three sites and eight tools before the agent has browsed anything — that is the store inheriting what ego already shipped.
On a real dsh boot (0.1.7-rc.2, and again on 0.2.0-rc.1: packed, added to a fresh profile with dsh plugin add, booted):
| Claim | How it was checked |
|---|---|
| It installs through dsh's own plugin path | adding the packed package with dsh plugin add exits 0, so dsh's compatibility gate admits it, and the profile holds no @deepseek-ai/* package of its own to shadow the host's harness. scripts/check-dsh-release.mjs repeats this on every pull request and daily |
| All seven tools register | GET /dsh-ego-browser/memory lists them; [] when the tools service never arrived |
| A call goes through the host into a real ego | a headless turn called ego_doctor, ego_recall and ego_run through dsh's tool pipeline and its subprocess seam. ego lite 0.5.1.13 on a Mac, reached from a Linux container through the deva bridge, answered: nodejs argv, flat globals, ego runtime v24.18.1 |
| The store self-seeds | came up holding google (2 tools) and x-com (3) from ego's own shipped skills/ego-browser/learnings/ |
| Our validator agrees with ego's format | zero problems reported against ego's own shipped sites — the useful direction of that check |
| The route is fenced | 200 on loopback, 403 for a Host header naming anywhere else |
Against the genuine built ego-browser bundle and a real subprocess seam:
| Claim | How it was checked |
|---|---|
| The argv probe finds the working shape | picks bare on the current CLI, nodejs on the older one, and reports both transcripts when neither runs |
| The surface probe is accurate | returns facade with page, browser, taskSpaces, site, fetch, cdp, help against the real bundle — matching its source exactly |
| Output survives a failure | a thrown error does not swallow what the script printed first; a top-level return still produces a verdict |
| A takeover is named as a takeover | not as a missing result — see Two runtimes |
| The promotion gate holds | snapshot refs, bad schemas, missing exports, and unparseable source are all refused, writing nothing |
npm install && npm test # 79 tests, no browser needed, ~2s
The CLI fixtures in fixtures/ are transcribed from ego's own source — argv
handling from src/run.ts, the output sink from src/output-sink.ts, the
helper surface from src/helpers.ts — with the upstream file named in each
header, because a double weaker than production tests nothing.
Not verified. Every path that touches a page: an actual page load, a real
task space, a real user takeover. ego lite is a macOS app and this was built in a
Linux container. The wire into it is exercised end to end against the real CLI
bundle, and against a real ego install through the deva bridge, but only with
scripts that open no page; what is on the far side of that is not. Run ego_doctor
on a real install and open an issue
with its output if anything below the wire disagrees with this page.
ego_learn refuses code containing a snapshot ref:
this is not storable yet:
- code contains a snapshot ref (@21 / ref=21). Those are rebuilt on every
snapshotText() call and mean nothing on the next run — re-express the step
with a stable locator (a CSS selector, or the loc=... value from the
snapshot) before promoting it.
This is the whole difference between a memory and a pile of dead selectors. The
script that just worked is full of @21s, because that is how the agent found
the element five seconds ago. Storing it verbatim stores nothing. The rule is
ego's own — its validator rejects the same pattern — and enforcing it at write
time is what turns "it worked once" into "it works again".
Worth stating plainly, because it will bite anyone writing against ego.
The argv shape is not stable. The shipped skill documents
ego-browser nodejs <<'EOF'. The CLI in citrolabs/ego-lite@main takes no argv
at all and answers a stray nodejs with its usage banner and exit 2. The
community Linux port swallows nodejs as a no-op prefix. One command name,
three behaviours.
The helper surface is not stable either. One generation installs flat
globals — cliLog, snapshotText, useOrCreateTaskSpace. The other installs
Playwright-shaped facades — page, browser, taskSpaces, site — and drops
cliLog for console.log. A script written for one throws ReferenceError on
the other, and no amount of documentation fixes it because both documents are
true somewhere. Even inside ego's own repo at HEAD, SKILL.md and
references/install.md disagree.
Neither is the output stream. ego's runtime flushes a script's output to
stdout. ego 0.5.1.13 reached through the deva bridge returns every byte on
stderr instead, --version included. The plugin reads each run from whichever
stream carries the marker the script printed, stdout first.
So this plugin does not assume. It asks, once per boot, and hands the answer to the model.
And the output sink can eat your result. When the user takes a task space back, ego marks a hard stop and discards every line the script logged, printing only its own guidance. Any integration that parses a result sentinel out of stdout finds nothing there and reports a parse failure. Here the sentinel's absence is itself the signal, and the run comes back classified:
hardStop: true — the user has taken control of this task space; ego paused the
agent. Do not retry and do not take control back on your own — ask the user, and
resume with ego_handoff only after they say to continue.
Use it if you want browser work that gets cheaper the second time; you are on macOS with ego lite installed; you want the agent to reach sites you are already logged into; or you want the login wall to become a prompt you can answer instead of a dead turn.
Skip it if:
click / fill / scroll tools. Deliberately absent:
ego's own benchmark attributes its speed to one-script-per-task, so thirty
small verbs would spend that advantage to look thorough. If your model writes
poor JavaScript, the granular design will serve you better.ego-browser skill.| tool | what it does |
|---|---|
ego_run | Run one script in ego's runtime. Pass url and it prepends everything already learned about that site. Pass taskSpace and it opens the space first, in the right dialect. |
ego_recall | Notes and tool signatures for a URL, read off disk. |
ego_site_run | Call a learned tool by site and name, and count the call. |
ego_learn | Promote a working step into a real site tool — and refuse it when it will not survive. |
ego_forget | Remove a tool or a whole site. A memory that is wrong is worse than none. |
ego_handoff | Hand the browser over with a real Continue / Finish task prompt, and take control back on Continue. |
ego_doctor | Which ego, which argv shape, which helper surface, which store, and what is in it. |
<workspace>/learnings/<site-id>/manifest.json id, name, domains, notes, tools
<workspace>/learnings/<site-id>/notes/*.md what the agent figured out, in prose
<workspace>/learnings/<site-id>/tools/*.js node tool: export async function f(ctx, args)
<workspace>/learnings/<site-id>/browser-tools/*.js page tool: evaluated in the tab
Anything learned here loads in the stock ego-browser skill outside dsh, and in
any other agent pointed at the same workspace. There is no private format and
nothing to migrate out of.
On first boot the store inherits an existing ego skill workspace's learnings/
if it finds one, so it starts with the sites ego ships rather than empty. Once.
A site you delete stays deleted.
Everything is validated before a byte is written, so a refused promotion leaves
the store exactly as it was: manifest shape, domain patterns, argument and
return schemas, the declared export, and the source's syntax — parsed
out-of-process with node --check, never imported, because that code is written
for the browser runtime and has no business running in the harness.
Read the store at any time:
curl -s localhost:8090/dsh-ego-browser/memory | jq
Every field is optional.
| field | default | what it is |
|---|---|---|
bin | ego-browser | The command. An absolute path when it is not on the PATH. |
workspace | ~/.dsh/ego-browser/workspace | Where learned sites live. Point it at an existing ego skill directory to share that store instead of keeping a copy. |
seed | true | Inherit an existing ego workspace's learnings/ on first boot. |
cwd | harness cwd | Working directory for the ego-browser process. |
extraArgs / env | [] / {} | Appended to every invocation. |
timeoutMs | 120000 | How long one script may run. |
probeTimeoutMs | 20000 | How long a capability probe may take. |
maxOutputBytes | 1048576 | Script output retained; overflow keeps the tail. |
route | true | Serve the store at /dsh-ego-browser/memory. |
trustedHosts | [] | Authorities besides loopback that may read that route. |
subprocess is the only required service. tools and webServer are picked up
when present, so a headless profile loads cleanly and simply has no route.
ego's own hard-stop message asks the harness for this, in as many words:
"Offer the user choices like "Continue" or "Finish task" if your harness supports it"
dsh supports it. ego_handoff hands the space over, raises a genuine dsh
question with those two options, and on Continue takes control back so the
next ego_run resumes — no "reply continue and I'll try again" dance, no agent
grabbing the keyboard back from a user who is still typing their password.
Where nobody can be asked — headless, no question UI, or the caller is a
subagent, which the harness refuses with DELEGATED_CALLER rather than blocking
forever — it says so plainly rather than pretending an answer arrived.
Two other people wired ego into dsh first, and both are worth your time:
ego_* tools plus a live screencast panel you can click through,
with a vendored Linux port of the ego runtime so it works without the macOS
app. Genuinely ambitious work, honest about its own limits, and the one to
pick if you want to watch the agent browse. This plugin does not compete
with it and ships no viewer.Neither fills the learnings store, and neither turns ego's hard stop into a real prompt. That is the gap this one is for.
And the thing itself: ego lite by CitroLabs is the good idea here — one browser for the human and the agent, instead of a framework driving a second one. The store format, the temp-ref rule, and the handoff protocol are all theirs; this plugin only fills them in.
CONTRIBUTING.md — what needs a real browser, and what does
not. If you have ego lite on a Mac, ego_doctor output from a real install is
the most useful thing you can send.
SECURITY.md — the agent drives your real logged-in sessions. Worth two minutes before you install.
MIT. ego lite is a separate, free download under its own MIT license; nothing from it is vendored here. Not affiliated with DeepSeek or CitroLabs.
FAQs
ego lite for DeepSeek Harness, with a memory it keeps: the agent recalls what it learned about a site before it acts, promotes a script that worked into a reusable site tool, and hands you the keyboard through a real dsh prompt when a page needs a human.
The npm package @dshworks/dsh-ego-browser receives a total of 63 weekly downloads. As such, @dshworks/dsh-ego-browser popularity was classified as not popular.
We found that @dshworks/dsh-ego-browser demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.

Security News
upm uses Node.js to deliver fast npm installs in about 250 KB, with a JavaScript API and security defaults.

Company News
Socket is joining the OpenJS Security Stewardship Program to fund Node.js vulnerability research, maintainer remediation, and security releases.

Security News
Two compromised GitHub Actions were re-enabled with malicious tags intact, exposing thousands of downstream repositories to Mini Shai-Hulud.