Passer au contenu principal
D

dsh-ego-browser

dshworks/dsh-ego-browser

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.

Installer

dsh plugin --profile web add github:dshworks/dsh-ego-browser

README

    ___  __ _  ___
   / _ \/ _` |/ _ \    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.1-rc.2 · 74 tests, no browser needed · host-only · MIT

CI npm MIT Node >= 20 verified against dsh 0.1.1-rc.2

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.

What it does

  • Recalls before it actsego_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.
  • Writes one script per taskego_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.
  • Promotes what workedego_learn turns a working step into a real site tool, in ego's format, and refuses the ones that will not survive to tomorrow.
  • Runs what it learnedego_site_run calls a stored tool by name and counts the call, so a tool nobody uses is visible as unused.
  • Hands you the keyboard for realego_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.
  • Tells the model which ego you haveego_doctor probes the installed command instead of trusting the docs, because there are two incompatible runtimes behind one command name.
  • Starts non-empty — on first boot the store inherits the learnings/ from the ego skill already on your machine.

How it works (30 seconds)

  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.

Get started (60 seconds)

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

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.

Proof

On a real dsh boot (0.1.1-rc.2, installed from npm into a profile, booted):

ClaimHow it was checked
The published package installs and loadsdsh plugin add -w @dshworks/dsh-ego-browser, restart, and all seven tools register — GET /dsh-ego-browser/memory lists them; [] when the tools service never arrived
The store self-seedscame up holding github (3 tools), google (2), x-com (3) from ~/.claude/skills/ego-browser/learnings/
Our validator agrees with ego's formatzero problems reported against ego's own shipped sites — the useful direction of that check
The route is fenced200 on loopback, 403 for a Host header naming anywhere else

Against the genuine built ego-browser bundle and a real subprocess seam:

ClaimHow it was checked
The argv probe finds the working shapepicks bare on the current CLI, nodejs on the older one, and reports both transcripts when neither runs
The surface probe is accuratereturns facade with page, browser, taskSpaces, site, fetch, cdp, help against the real bundle — matching its source exactly
Output survives a failurea thrown error does not swallow what the script printed first; a top-level return still produces a verdict
A takeover is named as a takeovernot as a missing result — see Two runtimes
The promotion gate holdssnapshot refs, bad schemas, missing exports, and unparseable source are all refused, writing nothing
npm install && npm test    # 74 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 needs the live browser: 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; what is on the far side of that wire 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.

The gate that makes the memory worth keeping

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".

Two runtimes, one command name

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.

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.

When to use · When to skip

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:

  • You want to watch the agent browse. This plugin has no viewer. Use Fisfzy/dsh-ego-browser — a live screencast panel you can click through, and a vendored Linux port so it works without the macOS app.
  • You are not on macOS. ego lite ships for macOS today. Same recommendation.
  • You want granular 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.
  • You do browser work once and never again. The memory is the point. Without repetition it is overhead over the plain ego-browser skill.
  • You need this in CI, headless. ego lite is a desktop app with a real profile. A Playwright plugin is the right tool.

The seven tools

toolwhat it does
ego_runRun 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_recallNotes and tool signatures for a URL, read off disk.
ego_site_runCall a learned tool by site and name, and count the call.
ego_learnPromote a working step into a real site tool — and refuse it when it will not survive.
ego_forgetRemove a tool or a whole site. A memory that is wrong is worse than none.
ego_handoffHand the browser over with a real Continue / Finish task prompt, and take control back on Continue.
ego_doctorWhich ego, which argv shape, which helper surface, which store, and what is in it.
The store is ego's format, not ours
<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
Configuration

Every field is optional.

fielddefaultwhat it is
binego-browserThe command. An absolute path when it is not on the PATH.
workspace~/.dsh/ego-browser/workspaceWhere learned sites live. Point it at an existing ego skill directory to share that store instead of keeping a copy.
seedtrueInherit an existing ego workspace's learnings/ on first boot.
cwdharness cwdWorking directory for the ego-browser process.
extraArgs / env[] / {}Appended to every invocation.
timeoutMs120000How long one script may run.
probeTimeoutMs20000How long a capability probe may take.
maxOutputBytes1048576Script output retained; overflow keeps the tail.
routetrueServe 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.

The handoff, in full

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.

Prior art

Two other people wired ego into dsh first, and both are worth your time:

  • Fisfzy/dsh-ego-browser — 32 granular 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.
  • Da1dr1em/dsh-ego-browser — three tools around the Windows preview host: run, help, status. The smallest honest thing that works.

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 · Security · License

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.

Plugins associés