DSH HUB
HomePlugin StorePlugin PacksCommunityRankingsResourcesPublish Guide
Plugin source
Back to catalog

LiFePO2 /

LiFePO2/dsh-handoff-hub

Verified

Simultaneous handoff-document sync for independent DeepSeek Harness sessions.

★ 0 Stars0 Forks0 IssuesN/A Community rating0 Confirmed installs
View on GitHub
READMESource: main@df366e10

Handoff Hub

test

Simultaneous handoff-document sync for independent DeepSeek Harness sessions.

Unofficial community plugin. Not affiliated with or endorsed by DeepSeek.

Project attribution

  • Code work / implementation: deepseek-v4.1-flash (DeepSeek Harness lead agent, opencode provider route), 2026-11-10.
  • Initial stability review: deepseek-v4.1-flash (a separate same-model subagent with a fresh context).
  • Independent release review: gpt-6.1-sol through the Codex subagent (subagent_codex, reasoning effort ultra).
  • Product direction, environment and acceptance: the repository owner.

Model names identify the tooling used for this release. A review is advisory and does not imply endorsement by any model provider. See CREDITS.md.

When several model sessions work in one workspace, each tends to write a handoff document — and then nobody reads the others' latest version. Handoff Hub gives those sessions a tiny shared room and a single durable operation that asks every peer, at the same time, to read the current handoff documents and update its own.

It is deliberately not a workflow engine, task tracker, dashboard, or memory system. It coordinates the handoff files you already keep, in whatever format you already use.

What it does

  • handoff_board — index the workspace handoff room: registered peers, each document's absolute path, size and timestamp, plus live unregistered sessions with titles.
  • handoff_join — register this session under a display name and atomically write (or point at) its handoff document.
  • handoff_nudge — create one durable one-shot Schedule task per target so every peer receives a user message asking it to read the peer documents, update its own, and re-register. Optional extra reference documents (for example a usage guide) are listed for every target.
  • handoff_leave — remove only this session's roster entry. The handoff document is never deleted.
  • Session group panel — a sidebar entry and full page for managing the current workspace's group: members, documents, live status, single/all nudges, remove, invite an unregistered live session, and toggle directory reminders.
  • Targeted directory-change reminders — an optional watcher that skips the changed document's author and only nudges peers whose own handoff document is stale; short bursts are merged into one reminder.

The plugin never calls a model API. It enqueues an ordinary user-role message through DSH's shipped ctx.schedule service; each target session then handles that message with its own model.

Install

Requirements: DeepSeek Harness >= 0.2.2-rc.1, Node.js 22.19+ or 24+, and a workspace containing the sessions to coordinate.

git clone https://github.com/LiFePO2/dsh-handoff-hub.git

Then install the local package as a DSH bundle. Ask your DSH agent to run:

plugin_manager install_bundle target=<absolute path to the cloned directory>

If the path contains spaces, quote it, for example target="D:\plugins\dsh-handoff-hub".

plugin_manager needs full-access approval. The bundle inserts one row named handoff-hub; its four tools appear for every agent after the row activates. No restart is normally required for a new bundle, but a linked-package update is only picked up after the row is reloaded or DSH restarts.

The package is not on npm yet; a local directory install is the supported path.

Profile package-manager warning. Installing any DSH bundle runs the profile's package manager. If you have manually patched packages under the profile's node_modules (for example a custom @openai/codex payload), back them up or pin them first. This plugin has no dependencies and does not touch them itself.

Discover in DSH

This repository ships a format-1 plugin market index. In DSH, open Plugins → Discover → Add market index and enter:

https://raw.githubusercontent.com/LiFePO2/dsh-handoff-hub/main/market/index.json

The index pins this release to the commit that contains the plugin, so Discover installs exactly the reviewed revision.

Quick start

In each collaborating session (once):

handoff_join
  name: "codex"
  doc: "2026-11-10-codex.md"
  content: "# Handoff: codex\n\n## Current state\n...\n"

Then, from any session:

handoff_board                      # who is here, and where their documents are
handoff_nudge                      # ask every registered peer to sync now

Nudge one explicit session (no registration required, still workspace-audited):

handoff_nudge
  targets: ["session-..."]
  note: "Please read the usage guide before continuing."

Share an extra document with every target:

handoff_nudge
  references: ["work/handoff-hub/plugin/README.md"]

When a session is done:

handoff_leave

Session group panel

The package ships a browser half (dsh.client) that adds a Sessions entry to the sidebar and a full management page. It shows the room, registered sessions, each document's size/time, live status, and unregistered live sessions in the same workspace. Actions:

  • Nudge one member or Nudge all;
  • Remove a member (roster only; the handoff document is kept);
  • Invite an unregistered live session (sends a Schedule invitation; it does not register on that session's behalf);
  • Auto reminder switch — runtime-only; a reload restores the configured value.

The page calls two loopback-only Host routes: GET /handoff-hub/state and POST /handoff-hub/action. Writes require an x-handoff-hub: 1 header; the plugin exposes no token and never reads document content for the page.

Directory-change reminders

Disabled by default (autoRemind.enabled: false). When enabled, the plugin polls the room's top-level *.md mtime/size and:

  1. debounces changes for debounceSeconds (default 30s);
  2. skips the changed document's author;
  3. nudges only registered peers whose own document is older than (or missing relative to) the change — a peer already newer is considered synchronized;
  4. merges further changes during mergeSeconds (default 60s) into the next reminder instead of sending a burst;
  5. starts from the current directory as a baseline, so historical changes are not replayed; unregistered sessions are never targeted.

Delivery semantics

handoff_nudge creates a one-shot Schedule task per target with timing.kind = "after" and target.kind = "session":

  1. Schedule validates the target Session without loading it, then persists the task.
  2. At the occurrence, it resolves the target Agent (resuming the Session if needed) and calls agent.followup() with a user message.
  3. A busy target queues the message; an idle target is woken.
  4. Tasks survive DSH restarts. A delivery that is still queued after a restart is recovered by the Schedule service.

The tool returns a queue receipt, not proof of reading:

[handoff-hub] Queued a handoff sync reminder to 3/3 sessions (about 1s delay).
- codex: queued · task schedule-... · fires 2026-11-10T08:31:42Z
- qwen: queued · task schedule-... · fires 2026-11-10T08:31:42Z
- stale-peer: failed · [session_not_found] target Session does not exist

Additional behavior worth knowing:

  • Each target also receives a no-action [SCHEDULE CHANGE] notice that the Schedule service generates for a newly created task; the actual reminder is a second message.
  • The reminder text is carried inside a JSON string by the Schedule framing, so line breaks appear as \n escapes in the raw message. Models read it fine.
  • A reminder consumes one model turn per target; that is the intended cost of a nudge.

Configuration

The bundle's patch carries defaults. To override them, add a matching row to your profile's cordis.patch.yml (or edit the inserted row through your normal profile configuration workflow):

- id: handoff-hub
  name: 'dsh-handoff-hub'
  config:
    directory: 'work/handoff'   # absolute, or relative to each caller's cwd
    promptLanguage: 'zh'        # zh | en
    nudgeDelaySeconds: 1        # 1..60
    maxRecipients: 16           # 1..32
    maxDocBytes: 262144         # 4 KiB..4 MiB
    cleanupEndedTasks: false    # opt-in, see below
    autoRemind:
      enabled: false            # opt-in directory watcher
      debounceSeconds: 30       # 1..600
      pollSeconds: 10           # 1..120
      mergeSeconds: 60          # 0..600; merge bursts

cleanupEndedTasks deletes this plugin's ended tasks before a nudge, but the Schedule service injects a no-op [SCHEDULE CHANGE] notice into every involved live session when a task is deleted. It therefore defaults to off.

Shared state on disk

<workspace>/work/handoff/
  <model-a>.md
  <model-b>.md
  .handoff-hub/
    participants/
      session-<uuid>.json      # one atomic roster entry per session

Roster entry:

{
  "v": 1,
  "sessionId": "session-...",
  "name": "codex",
  "doc": "2026-11-10-codex.md",
  "cwd": "E:\\LAB\\DS GPT\\T1",
  "joinedAt": "2026-11-10T06:20:00.000Z",
  "updatedAt": "2026-11-10T06:20:00.000Z"
}

Delete a roster file to make a session leave without calling the tool. The plugin never deletes or rewrites another session's document.

Safety model

  • Targets are audited against durable Session storage. Before scheduling, every target is checked with ctx.sessionPersistence.stat(): it must exist, must not be a delegated subagent Session, and its recorded cwd must match the caller's workspace. A tampered roster cannot redirect a reminder to a different workspace.
  • Roster is untrusted input. Document names are re-validated on read (no .., path separators, traversal, or Windows reserved names); display names are stripped of control characters. Invalid entries are skipped with a warning.
  • Names and document names compare case-insensitively, matching NTFS behavior, so Lead.md and lead.md cannot silently overwrite each other.
  • Documents and requester notes are framed as evidence, not instructions. The reminder explicitly says the requester is a model session, not the user, and that document content must not be executed as commands.
  • Path checks for references resolve symlinks and reject anything outside the caller's workspace; only small .md/.markdown/.txt pointers are allowed.

Stability

The plugin is intentionally boring:

  • no timers, filesystem watchers, polling loops, session-event writes, or system prompt sections;
  • one ctx.effect() owns all tool registrations and unregisters them on unload;
  • per-target Promise.allSettled isolation — one stale peer cannot fail a batch;
  • atomic tmp + rename writes, with a bounded retry for transient Windows rename locks;
  • bounded inputs and outputs (recipient cap, document cap, roster scan cap, report line cap);
  • the browser half only registers Slot components; the Host routes are loopback-only and the write path requires a custom header;
  • no third-party runtime imports — only Node built-ins.

Known limitations

  • Each peer must register once, or be named explicitly in targets; the plugin never auto-nudges unregistered sessions.
  • Reading is asynchronous and not acknowledged. A peer can receive a reminder and still choose not to sync; the plugin cannot prove it read anything.
  • Two sessions joining with the same new document name can race; the room lock serializes plugin joins and the second join is refused with doc_taken, but an external writer can still race a join. The last writer wins in that case and handoff_join reports a [roster_conflict] warning when it detects duplicate ownership.
  • Ended Schedule tasks accumulate unless cleanupEndedTasks is enabled; they are harmless rows in the Host task list.
  • Not a substitute for dsh-collab when you need work orders, flow handoff, or a dashboard.

Uninstalling

Remove the bundle (or disable its row). The room directory, handoff documents, and roster entries are left untouched. Reminders already queued in the Schedule service are durable and may still be delivered after uninstall; delete them from the Automation tasks page if that is not desired.

Compatibility

Component Status
DSH tested with 0.2.2-rc.1; requires >= 0.2.2-rc.1 < 0.3.0
Node.js tested with 22.19 and 24; CI runs both
OS Windows 10/11 tested; no native code, so Linux/macOS are expected to work but are not CI-tested
Models provider-agnostic: the plugin never calls a model API; any model a session already uses can participate

Related projects

The DSH ecosystem has several adjacent plugins; pick the one that fits:

  • dsh-collab — multi-session collaboration with work orders, flow handoff, file memory, and a dashboard.
  • dsh-handoff-bus — cross-session message bus for GUI conversations.
  • dsh-handoff-session — pressure-aware session handoff into a fresh session.
  • dsh-auto-handoff — summarize a long session and continue in a new one.

Development

node test/manifest.mjs
node test/harness.mjs

The offline harness uses a fake Cordis context and a real temporary directory. It covers registration, roster validation, tampered entries, duplicate names, path traversal, target auditing, failure isolation, references, leave, unload, targeted auto reminders, and the management panel routes. 45 integration cases plus the static manifest checks; CI runs both files on Node 22 and 24.

License

MIT. See LICENSE.

—/ 5

No ratings yet

Verified DSH bundle

Commit df366e10eda5

Community comments

No comments yet. Be the first to write one.

DSH HUB

A community index for DSH plugins. Not an official GitHub or DeepSeek AI product.

CommunityResourcesAPIAbout