DeepSeek Harness 插件

dsh-agent-messaging

Cross-session agent-to-agent messaging for DeepSeek Harness: address another session by name and deliver a message into its inbox.(英文原文)

跳到安装方式

来源信息

GitHub 仓库
happyren/dsh-agent-messaging
最近更新
2026年8月15日
分类
自动化与任务
GitHub stars
5
载体类型
plugin
目录证据
上游声明已找到 dsh.bundle
证据路径
package.json#dsh.bundle
核对版本
0.1.0-rc.8
上游核对日期
2026-08-20

该证据由上游目录提供。本站没有安装、运行或安全审核这个插件。

安装

默认先复制一段 Prompt,让 Agent 读 GitHub 仓库和源码;需要自己装时再切到命令。

复制这段 Prompt,发给 DSH、Codex 或其他 Agent,让它先读 GitHub 仓库和源码。

请先不要安装或执行任何命令。阅读这个插件的 GitHub 仓库、README 和关键源码,然后用清楚、直接的方式回答以下问题,帮助我判断它是否适合我的需求:

1. 这个插件是什么,解决什么问题;
2. 适合哪些用户和典型使用场景;
3. 安装后如何使用,并给出一个最小使用示例;
4. 有哪些已知限制,以及隐私、安全、兼容性或维护风险;
5. 给出“推荐 / 有条件推荐 / 不推荐”的明确建议和理由。

请区分仓库明确说明、根据源码推断和未知信息。证据不足时请明确说明,不要猜测或照抄 README。

GitHub:https://github.com/happyren/dsh-agent-messaging
插件名:dsh-agent-messaging
作者:happyren

检查来源文件

安装前先看这个插件目录里的 README 和其他文件。

文件资源管理器3 个文件
README.md来源说明 · 只读预览

<p align="center"> <img src="docs/media/social-card.png" width="900" alt="dsh-agent-messaging — the payments-api session sends a steer to the checkout-client session, carrying &quot;tenant_id is now required&quot;. Delivery modes: steer interrupts, followup opens a new turn, context does not wake."> </p>

<p align="center"> <a href="https://github.com/happyren/dsh-agent-messaging/releases/latest"><img src="https://img.shields.io/github/v/release/happyren/dsh-agent-messaging?color=5B7CFF&label=release" alt="Release"></a> <a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-5B7CFF" alt="MIT"></a> <a href="https://github.com/topics/dsh-plugin"><img src="https://img.shields.io/badge/dsh--plugin-topic-5B7CFF" alt="dsh-plugin topic"></a> </p>

dsh-agent-messaging

Cross-session verification, claims and a decision ledger for DeepSeek Harness — so two agent sessions don't repeat, contradict or deadlock each other.

Two sessions you started yourself — in the Web UI, in a headless run, in separate worktrees, in separate dsh processes — cannot tell each other anything. When one discovers a breaking change the other is about to trip over, you are the transport: you read it in one terminal and retype it in the other.

This plugin gives them an address and a mailbox. One session names another and delivers a message into its inbox; the harness schedules it like any other model-facing input.

session "payments-api"                      session "checkout-client"
        │                                              │
        │  peer_send  to: checkout-client              │
        │             mode: steer                      │
        ├─────────────────────────────────────────────►│  interrupts at the next step
        │  "tenant_id is now required on ChargeRequest │
        │   — your call site will break"               │

An arriving message is its own card in the transcript, so a reader can tell at a glance that another agent spoke — not the human, and not the harness injecting context:

![A peer message rendered as its own card: sender payments-api, interrupted this step, information only](docs/media/card.png)

![The same card in dark mode](docs/media/card-dark.png)

It names the sender, what the delivery cost (interrupted this step, next turn, or delivered quietly), what this session was told it may do about it, and the message itself rather than the framing around it. The accent colour is derived from the sender's session id, so one peer keeps one colour even if its title changes.

One real run, end to end

Everything below is a single live run: four sessions with real models in one dsh web host, over a repo where each session owns a different directory. No mock-ups — these are screenshots of the run that produced the numbers further down.

1 · Who's who. Each session publishes a capability card: an alias, what it owns, and what it is not responsible for.

peer_card  alias: "payments-api"
           role: "Owns api/ and the charge contract. I do NOT own client code."
           owns: [{ resource: "api" }]
           groups: ["backend"]

2 · A collision, refused. payments-api claims api/charges.ts. Moments later the checkout session tries to claim api/ — and is told who holds what beneath it, and why.

![The checkout session's claim on api/ is refused, naming payments-api as the holder](docs/media/claim-refused.png)

The interesting part is the last paragraph: without being asked, it decides not to edit in parallel and to coordinate first. That is the largest single failure mode in the MAST taxonomy — step repetition, 15.7% of observed multi-agent failures — not happening.

3 · A breaking change, delivered mid-task. payments-api edits the file for real, then steers the peer whose call site it just broke. The receiving session does not take the claim on trust: it reads both files, confirms the change is real, and only then acts — on a file it owns.

![The receiving session gets the card, replies, reads both files, and claims its own file before editing](docs/media/card-in-context.png)

4 · A false belief, caught before it ships. The checkout session is about to drop the currency field, believing the API rejects non-USD. It asks the peer that owns that file to check — and is refuted.

![payments-api refutes the claim after reading the file, and declines to send an acknowledgement](docs/media/verify-refuted.png)

Two things in one screenshot. The refutation is the point: self-verification is known to fail, and a peer that didn't write the code has to go and look. The second thing is the model declining to send a courtesy reply — "sending a 'noted' back would just cost them a turn" — which is [the fix described below](#what-it-cost) doing its job.

5 · A mutual wait, made visible. The docs session declares itself blocked on checkout; checkout is already blocked on docs. The cycle is reported the moment it closes.

![The docs session declares itself blocked and is told it is in a deadlock cycle](docs/media/deadlock.png)

Without this, a deadlock is silent: every participant looks merely idle, nobody is finished, and nothing reports it.

6 · A newcomer that reads the history it was never told. A fifth session, started fresh and told only to add currency validation, finds the recorded decision, checks it against the current file, and refuses — offering supersession as the only correct route.

![A new session finds the recorded decision and declines to reopen it](docs/media/newcomer-ledger.png)

What it cost

The same scenario, run twice, on the same models — with one sentence changed in peer_send's description between the runs:

beforeafter
messages delivered207
dropped by loop control20
collisions avoided11
false claims caught11
deadlocks detected11

The first run's transcripts showed why: once the work was done the sessions kept going — "Noted, thanks.""Anytime — good luck.""Thanks, will keep you posted.""Perfect — I'm here." — until loop control dropped a duplicate and one of them observed, in its own words, that the exchange had wound down.

Autonomous peers are polite, and politeness costs a turn each time. The fix was one sentence telling them not to be:

> Every message costs the receiver a turn, so send only what changes what it will > do. Do NOT send acknowledgements, thanks, sign-offs, or "noted" — a peer that > has nothing to act on is better left working.

65% less traffic, identical catches. That is what the accounting is for: it made a prompt-level regression visible, and then showed the fix worked. Run it on your own work with npx dsh-agent-messaging report.

What it is not

If you wantUse
To pull another session's history into your next messagedsh-session-reference (@[label](dsh-session:…))
A coordinator that spawns and supervises workersthe subagent subsystem
To continue one conversation elsewhereresume the session
To tell another independent session something, nowthis plugin

A message is text. Never conversation history, never files.

Install

npx -p @deepseek-ai/dsh dsh plugin --profile web add dsh-agent-messaging

Restart the profile, then check the install from inside or outside a session:

npx dsh-agent-messaging doctor
OK    node                v24.13.1
OK    build               host and browser bundles present
OK    state-root          /Users/you/.dsh/agent-messaging (writable)
OK    presence            2 live hosts, 0 stale records
OK    socket-permissions  owner-only (0600)
OK    accounting          recording; run `npx dsh-agent-messaging report` to see what this cost and caught

It exits non-zero on anything that would stop messaging working, and every line that reports a problem also says what to do about it — so a session that suspects its own messaging is broken can run this and read the answer.

Nothing else to configure. A session is addressable and informative from the moment it starts: peers see what directory it works in, and what the humans wrote about that directory in AGENTS.md or README.md. peer_card upgrades that from inferred to declared; it is not a prerequisite.

The transcript card needs the Web UI. Everything else works headless, and without the browser half a message renders as the harness's ordinary context row.

<details> <summary>Installing from git instead</summary>

npx -p @deepseek-ai/dsh dsh plugin --profile web add github:happyren/dsh-agent-messaging

dsh plugin shells out to pnpm, and pnpm blocks build scripts from git dependencies until you allow them. The first add will fail and print the package key; add it to the profile's pnpm-workspace.yaml:

allowBuilds:
  dsh-agent-messaging: true

then re-run the add. Pin a commit (github:happyren/dsh-agent-messaging#<sha>) so a later push cannot change what runs on your machine.

</details>

Tools

Nine tools register by default. That is a lot of competition for a model's attention, so a deployment only pays for what it uses:

- id: agent-messaging
  config:
    capabilities:
      claims: true         # peer_claim
      verification: false  # peer_verify, peer_verify_reply
      identity: false      # peer_card, peer_status
      decisions: false     # peer_decide, peer_decisions

That leaves three: peer_list, peer_send, peer_claim. Addressing and delivery are always registered — without them nothing else has a point. Everything defaults on, so upgrading never silently removes a tool a workflow depends on.

peer_inbox registers only under inbound: hold, because held messages do not exist otherwise and a tool that always reads an empty list is pure overhead.

peer_list

Sessions this one can address — name, state, title, directory. Identities only; never their contents.

payments-api [running] "Add tenant_id to charges" — /repo/test-project
    "payments-api" — Owns api/ and the charge contract. I do NOT own client code. · owns api · groups: #backend
    working on: api/charges.ts (adding a required tenant_id to ChargeRequest)
checkout-client [idle] "Wire up checkout submit" — /repo/test-project
    task: blocked on docs-writer: waiting on billing wording before updating checkout
    "checkout-client" — Owns client/ and the checkout flow. · owns client · groups: #backend
ready-57a1 [not running] "ready." — /repo/test-project

A session that published a card is listed by its alias — the last line above is one that did not, folded from a session whose first reply happened to be "ready.", which is exactly why an alias is worth publishing. Names are collision-disambiguated, so an address you read in one listing still resolves in the next. A wait is stored as a session id, because that is the only form a deadlock cycle can be walked in, but it is shown as the address you would use to break it.

peer_send

Deliver one message. The sender's identity comes from the executing agent, so a model cannot send a message claiming to be another session.

modeArrivesUse for
steerAt the receiver's next step boundary, interrupting itSomething that makes its current work wrong
followup (default)As its own later turnThe ordinary handoff
contextFolded into whatever it does next, without waking itBackground it should know but need not act on

These map onto Agent.steer(), Agent.followup() and Agent.inject() — the inbox boundaries the harness already owns. Choosing is the sender's job, because only the sender knows whether the news invalidates work already in progress.

A session that is not running still accepts messages: they are spooled and delivered when it next starts, within the configured age and depth bounds.

Replies correlate through reply_to. The tool tells senders not to send acknowledgements — [the measured reason](#what-it-cost) is above.

Groups. Address #backend to reach a whole set at once. Membership is declared on each session's peer_card, and the shape is an operator decision in config — because denser is not automatically better and every extra recipient costs a turn:

- id: agent-messaging
  config:
    groups:
      backend: { topology: star, lead: payments-api }
    maxFanout: 8

mesh reaches everyone; star routes a member's message to the lead alone and lets the lead broadcast — one message in costs one turn instead of N. Each recipient is an ordinary send, so inbound policy, loop control and accounting apply per recipient: a group address is a convenience for the sender, never a way around the receiver.

Configure the lead against a session's alias (peer_card alias: "payments-api"), not its display name — display names are folded from session titles and move.

peer_card

Declare what this session is for and what it owns, so peers route work correctly instead of guessing from a folded title.

This is an upgrade, not a prerequisite. A session that never calls it is still listed with what can be read off the workspace — the directory it works in, and the headline of that directory's AGENTS.md or README.md — marked inferred from the workspace, not declared so nobody mistakes an inference for a statement. Models do not do reliable setup, and a listing that says nothing until one makes a tool call is a listing that is usually empty of meaning.

peer_card  alias: "payments-api"
           role: "Owns api/ and the charge contract. I do NOT own client code."
           owns: [{ resource: "api" }, { resource: "charge validation rules", scope: "topic" }]
           skills: ["payments-api", "validation-rules"]
           groups: ["backend"]

An alias is a stable address, not decoration. Display names are folded from session titles, so they move — and read like an accident (ready-57a1) when a title is short. An alias is chosen and stays put. Every address a peer can use — peer_send, peer_verify, a group lead, a blocked_on — resolves an alias ahead of a derived name, and a session that published one is referred to by it in every record a peer reads: refused claims, decisions, waits, and the card on a delivered message.

This targets FM-1.2 disobey role specification and FM-2.3 task derailment (7.4%); role specification was one of only two interventions MAST measured directly, at +9.4%. Shaped after A2A Agent Cards so the same declaration can later serve cross-vendor discovery.

Ownership here is standing responsibility, not a reservation — it never conflicts and reserves nothing. peer_claim is the short-lived "I am editing this right now" signal. Saying what you don't own is as useful as what you do, since it stops peers sending you work that isn't yours.

peer_claim

Announce what you are working on, and find out whether a peer is already on it.

peer_claim  resource: "api"  intent: "adding tenant support to the charge call"
→ refused: "api" overlaps a claim held by another session.
  payments-api holds "api/charges.ts" — adding a required tenant_id to
  ChargeRequest (expires in ~30 min)
  Message the holder with peer_send instead of working in parallel.

This targets the largest single failure mode in the MAST taxonomy: step repetition, 15.7% of observed multi-agent failures — whose concrete instance in coding is two sessions editing the same file, or re-deriving what a sibling already knows.

Path claims nest, so holding client covers client/checkout.ts, and sibling names never collide (src/app does not contain src/apple). Topics don't nest. Claims expire on their own, and are dropped when the holding session ends.

Claims are advisory, not locks. The plugin cannot stop another process writing a file, and a lock that can't be enforced is worse than an honest hint — it invites callers to skip the check they'd otherwise make. Claimed resources show up in peer_list under working_on.

peer_verify and peer_verify_reply

Ask a differently-situated peer to check a claim you're about to act on.

peer_verify  to: "payments-api"
             claim: "createCharge rejects any currency other than usd"
             evidence: [{ locator: "api/charges.ts" }]
→ REFUTED — createCharge only validates amount_cents and tenant_id;
  currency is never checked, so non-USD currencies are accepted.

The peer is told to check, not agree — "go and look before answering; do not take the claim on trust" — and replies with a typed verdict: confirmed, refuted, inconclusive, or declined, plus what it actually examined.

This targets MAST's task-verification category (24.5% of failures) and is the intervention with its largest measured gain (+15.6%). It belongs in a messaging plugin rather than an agent's own loop because self-verification is known to fail — a model largely cannot check its own reasoning. A peer is a different verifier in the way that matters: it didn't produce the artefact, so it has to go and look.

A refuted verdict comes back as a steer, because the asker is probably acting on the claim right now and a queued turn would arrive too late.

peer_status

Say what your work is doing — working, blocked, done, abandoned — and find out if you have just deadlocked.

peer_status  phase: "blocked"  blocked_on: "checkout-client"
             summary: "waiting on the final checkout field list"
→ published: blocked
  DEADLOCK — you are in a mutual wait:
  docs-writer → checkout-client → docs-writer
  Nobody in this cycle will proceed on their own. Break it: message one of them
  with peer_send, do the part you can without waiting, or ask your user to decide.

The agent registry already reports idle/running, but that describes a driver, not a task. A session is idle both when it has finished and when it is waiting on a peer — indistinguishable from outside, and the difference is exactly what a peer needs to decide whether to wait.

This targets FM-1.5 unaware of termination (12.4%) and FM-3.1 premature termination (6.2%), and is common ground in Klein's sense — a teammate that cannot signal completion or blockage cannot be coordinated with.

Because blocked carries who it is blocked on, a mutual wait becomes representable and therefore detectable. The check runs when a session declares itself blocked, which is the moment a cycle can first close.

peer_decide and peer_decisions

Record what was settled, so a session that starts later doesn't reopen it.

peer_decisions  about: "api/charges.ts"
→ 2026-08-15 20:40 · payments-api [api/charges.ts]
    Multi-currency is deferred until tenant billing lands; createCharge accepts
    any currency string for now.
    why: Validating currency needs the tenant billing profile, which does not
         exist yet.
    id: bd408a8e…

Messages are ephemeral — delivered once, folded into a transcript, gone when that session compacts or ends. Common ground has to outlive them, which needs a record rather than a conversation. This targets FM-1.4 loss of conversation history and FM-2.1 conversation reset.

It's the transactive-memory direction: rather than replicating every session's context into every other, publish the