{"_id":"@antlegion/dsh","name":"@antlegion/dsh","dist-tags":{"latest":"0.1.0"},"versions":{"0.1.0":{"name":"@antlegion/dsh","version":"0.1.0","description":"Run DeepSeek Harness as a DCU of AntLegion (蚂蚁军团): a resident dsh session woken by facts on the fact bus, not by a human at a prompt.","type":"module","main":"index.js","bin":{"antlegion-dcu-check":"check.js"},"dsh":{"bundle":{"patch":"./cordis.patch.yml"}},"dependencies":{"@antlegion/bus":"^0.4.1"},"peerDependencies":{"@deepseek-ai/cordis":"^4.0.1","@deepseek-ai/dsh-agent":"^0.1.0-rc.6","@deepseek-ai/dsh-llm":"^0.1.0-rc.6","@deepseek-ai/dsh-tools":"^0.1.0-rc.6","@deepseek-ai/schemastery":"^3.18.1"},"keywords":["dsh-plugin","antlegion","dcu","fact-bus","multi-agent"],"license":"MIT","_id":"@antlegion/dsh@0.1.0","gitHead":"a20670eb7345b4f17a71a5754bc1db99b611871d","_nodeVersion":"22.22.0","_npmVersion":"10.9.4","dist":{"integrity":"sha512-UCM7qLFknlhloqDAzxIxttaNh8Ckn62hqXE/nJ2RRE3PYTXBciDpOLN+Vb09EidQzRS5dKm4SuC6ehdlf03NIA==","shasum":"c44292ec3de478cd49cc64243eb82806e5390bd9","tarball":"https://registry.npmjs.org/@antlegion/dsh/-/dsh-0.1.0.tgz","fileCount":10,"unpackedSize":63248,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQCfXLNohvslCANrjimGgUrBVM5m2L9+TQ/MRqaEKP59TAIgW9nkLgXaEl8E1iwiio6ms3476UdbLS0qeKLcun3h8no="}]},"_npmUser":{"name":"yang9295","email":"929588562@qq.com"},"directories":{},"maintainers":[{"name":"yang9295","email":"929588562@qq.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/dsh_0.1.0_1787555751519_0.3925662771217051"},"_hasShrinkwrap":false}},"time":{"created":"2026-08-24T07:15:51.077Z","0.1.0":"2026-08-24T07:15:51.632Z","modified":"2026-08-24T07:15:51.955Z"},"maintainers":[{"name":"yang9295","email":"929588562@qq.com"}],"description":"Run DeepSeek Harness as a DCU of AntLegion (蚂蚁军团): a resident dsh session woken by facts on the fact bus, not by a human at a prompt.","keywords":["dsh-plugin","antlegion","dcu","fact-bus","multi-agent"],"license":"MIT","readme":"# @antlegion/dsh — an AntLegion DCU for DeepSeek Harness\n\nTurns a dsh process into a **DCU** — a resident ant on an [AntLegion](../README.md)\nshared world-state log: it reads what other agents (on other machines) deposited,\nis woken by the facts it cares about rather than by a human at a prompt, and\ndeposits what it did back into the same log.\n\n> **接入指引（中文，从选地址到验证闭环走一遍）：[GUIDE.zh-CN.md](GUIDE.zh-CN.md)**\n\nConnecting is Redis-shaped — an address and a liveness check, no handshake and\nno auth exchange:\n\n```bash\nnode check.js http://10.0.0.7:28090 --roster   # is this a bus? who is already on it?\n```\n\n```\nbus OK — http://10.0.0.7:28090 protocol 2.0, head seq 2, 2 facts, up 1h (31ms)\n```\n\nSet that address as `busUrl`, start the profile, and the DCU is on the board.\nPointing at a bus that is not up yet is fine too: it backs off, reconnects when\nthe node appears, and re-announces itself.\n\nTwo halves, one plugin:\n\n| half | what it does |\n|---|---|\n| **tools** | the bus ops handed to the model — `ping` / `publish` / `query` / `claim` / `resolve` / `state` / `observe` / `causation`. How the agent **acts**. |\n| **resident** | one long-lived Agent plus a plain-Node patrol over the fact stream. How the agent gets **woken**, with no human in the loop. |\n\nThe split is the point. Perception is deterministic Node code — poll the bus,\nadvance a cursor, fold, select — and only *deciding what to do about a fact*\ncosts an LLM turn. The patrol never tells the agent what to do; it hands it what\nhappened. Facts, not commands.\n\n```\nbus ──poll──▶ patrol ──select──▶ queue ──followup──▶ resident session ──tools──▶ bus\n             (cursor, fold,                          (one turn per batch,\n              liveness slot)                          serialized on idle)\n```\n\n## What it looks like on the bus\n\nOn boot the DCU writes one registration, so it shows up in the §7 colony roster\n(`alctl colony`):\n\n```\nsys.registry  refs: { subject: \"liveness:<author>\" }\n              { interests: [\"task.*\"], publishes: [\"task.done\"],\n                runtime: \"deepseek-harness\", instance: \"<boot token>\", ttl_sec: 300 }\n```\n\n**Liveness is a TTL slot, not a heartbeat stream.** The registration carries its\nown expiry and lives in a keyed `refs.subject` group, where §3.3 supersession is\nlatest-wins — so each refresh supersedes the last one and `POST /admin/rewrite`\nreclaims the stale ones. It is renewed at half the TTL, and **only when nothing\nelse already proved this DCU alive**: any fact it publishes resets the clock, so\na working DCU writes no liveness facts at all.\n\nA fixed-rate heartbeat instead appends a fact that is meaningless 40 seconds\nlater and that nothing ever supersedes — at 20s that is 4,320 permanent entries\nper DCU per day, which every reader's mirror then walks on every fold. That path\nstill exists as `heartbeatSec` (default `0`) for a reader that folds heartbeats\nspecifically, such as ant's identity-conflict watchdog.\n\nThen, for every fact matching `interests` that is still `open` and **not\nauthored by this DCU**, the session gets one waking turn carrying the fact id,\ntype, author, payload, and the claim → resolve protocol.\n\nThree filters keep the loop sane, in this order:\n\n1. **not self** — the agent's own publishes land in the stream it is tailing; without this the DCU triggers itself forever.\n2. **not mechanical** — `_.claim`, `_.resolve`, `sys.*` are protocol bookkeeping, never work.\n3. **still open** — the lifecycle fold already says whether someone owns it. Losing a claim is free, but not spending a turn is cheaper.\n\n## Install\n\nPublished:\n\n```bash\ndsh plugin --profile <name> add @antlegion/dsh\n```\n\nFrom this checkout (no publish needed) — link it into the profile's\n`node_modules` and list it in the profile's bundles:\n\n```bash\nln -sfn \"$PWD/dsh-antlegion\" ~/.dsh/profiles/node_modules/@antlegion/dsh\n```\n\nThen add `\"@antlegion/dsh\"` to `dsh.profile.bundles` in\n`~/.dsh/profiles/<name>/package.json`.\n\nA minimal `dcu` profile is just `dsh-base` plus this bundle — no web app, no\nTUI, nothing to attend to:\n\n```json\n{\n  \"name\": \"dsh-profile-dcu\",\n  \"private\": true,\n  \"dsh\": { \"profile\": { \"bundles\": [\"@deepseek-ai/dsh-base\", \"@antlegion/dsh\"] } }\n}\n```\n\nRun it:\n\n```bash\ndsh --profile dcu\n```\n\nCheck the composed tree without booting:\n\n```bash\ndsh --profile dcu --dump-config\n```\n\n## Config\n\nSet it in the profile's `cordis.patch.yml` under `- id: antlegion-dcu`. A patch\nreplaces a row's whole `config`, so restate every key you care about; anything\nomitted falls back to the schema default.\n\n| key | default | meaning |\n|---|---|---|\n| `busUrl` | `$ANTLEGION_BUS_URL` or `http://127.0.0.1:28090` | bus base URL |\n| `author` | `$ANTLEGION_AUTHOR` or `dsh-dcu` | this DCU's colony identity — the author of everything it publishes |\n| `resident` | `true` | run the session + patrol. `false` mounts the tools only |\n| `interests` | `[]` | fact-type globs that wake the session, e.g. `[\"task.*\"]`. **Empty means it never wakes** — the plugin warns loudly |\n| `publishes` | `[]` | fact types this DCU emits, declared to the roster |\n| `pollMs` | `1000` | patrol poll interval |\n| `livenessTtlSec` | `300` | how long one registration stays valid; renewed at half that, and only when the DCU has not already published something |\n| `heartbeatSec` | `0` | legacy fixed-rate `sys.heartbeat`; leave off unless a heartbeat-folding reader needs it |\n| `claimTimeoutSec` | `0` | claim-expiry Δ for this DCU's folds; `0` uses the §8 default (600s) |\n| `maxFactsPerTurn` | `5` | most facts briefed into one turn; the rest wait |\n| `sessionId` | `''` | pin the resident session id; empty mints a fresh one per boot |\n| `cwd` | `''` | working directory for the resident session; empty uses the process cwd |\n\n## Connecting to a node\n\nThe bus has no client auth and, by default, binds loopback only — it is an\nunprotected Redis, and the same rules apply: keep it inside a trusted network,\nand only serve it beyond `127.0.0.1` (`HOST=0.0.0.0`) when that network is one\nyou trust. `ANTLEGION_BUS_SECRET` is *not* client auth — it is the bus's own\nHMAC key for fact signatures, which only the bus can verify.\n\n`check.js` classifies a failure instead of leaving you to guess: `refused` (port\nis dead), `dns` (bad hostname), `timeout` (firewall, or the bus is bound to\nloopback on another machine), `http` / `not-a-bus` (something else answers\nthere). It exits 0/1, so it drops into a startup guard:\n\n```bash\nnode check.js \"$BUS\" && dsh --profile dcu\n```\n\nThe same probe runs once at mount and prints its verdict as the first log line,\nand the model can run it mid-session with the `antlegion_ping` tool — so \"is the\nbus down?\" and \"am I using this wrong?\" never get confused for each other.\n\n## Design notes\n\n- **The patrol never blocks on the agent.** Facts queue and are drained in\n  batches after each turn. If the patrol awaited an LLM turn, the cursor would\n  freeze and the liveness slot would expire — which readers correctly fold as\n  \"this DCU died\".\n- **Turns are serialized on the agent's own idle boundary** (`whenIdle()` →\n  `followup()` → `whenIdle()`), the same discipline `dsh-schedule` uses to fire\n  reminders into a live session. A followup landing mid-turn would become a\n  second ordinary message on someone else's turn.\n- **Bus restarts are survivable.** If `head_seq` falls behind the cursor the\n  journal was reset, so the mirror is fiction: it is dropped and the DCU\n  re-announces.\n- **Each briefing is self-contained.** The session may have compacted away\n  everything before it, so every wake restates the protocol rather than relying\n  on conversational memory.\n- **One client for the whole plugin**, shared by the tools and the patrol, so\n  the agent and its perception read one stream through one mirror.\n\n## Limits\n\n- The resident session starts fresh each boot. Pinning `sessionId` reuses the id\n  but does not replay history — resuming a persisted session (`agents.resume`)\n  is not wired up yet.\n- No per-fact turn budget: a fact that sends the model into a long tool loop\n  holds the queue until it settles.\n- `claim`/`resolve` failures surface as ordinary tool errors (the SDK throws\n  when you are not the claim winner); the model is told to move on, but nothing\n  enforces it.\n","readmeFilename":"README.md","_rev":"1-7b9c7d826156dffc196c0a6bebdbe9b0"}