dsh-web
zhu1090093659/dsh-webDeepSeek Harness(DSH)Web 插件聚合生态包 · 一切皆插件,创意工坊分发(dsh-market.com)
A DeepSeek Harness (DSH) plugin: an agent self-scheduler that wakes the agent at a future time to autonomously check on background jobs, remote tasks, or anything that needs a "come back later" look — without a human having to prompt it — plus a header reminder menu in the web UI.
schedule_reminder — schedule a one-shot reminder, relative (delay_seconds) or absolute (at: ISO 8601 / HH:MM[:SS]).subject — pass a subject (background subagent id / shell background job id); when that subagent or background job settles and its completion notice enters the parent inbox, the plugin cancels matching reminders automatically.list_reminders — list pending reminders.cancel_reminder — cancel a reminder by id.agent.followup() with a new turn; it acts and reports on its own, no human wake-up needed.$DSH_HOME/timer-reminders.json and re-armed on restart.--dsw-alias-* tokens, hidden when empty, never covering the send button.One package, two halves, in the standard dsh.client + dsh.bundle.patch shape. Everything is host-plane: composing this bundle into the web profile's host composition makes the model tools available to every agent regardless of preset, and the route serves the browser panel.
| File | Half | Role |
|---|---|---|
lib/index.js | Host | The three model tools (schedule_reminder / list_reminders / cancel_reminder), auto-wake, disk persistence, and GET /api/timer-reminders |
lib/client.js | Client | conversation.session.header.actions compact dropdown, polling every second |
cordis.patch.yml | bundle | Inserts the Host half into the web profile's host composition |
package.json | — | dsh.client (browser bundle) + dsh.bundle.patch (host row) |
Preset adaptation: the anchored-standard preset keeps these tools resident for its agents via its residentTools option; every other preset works without any configuration.
Not published to npm yet. Install from source:
Host-plane tools: once this bundle is composed, every agent on any preset can call
schedule_reminder/list_reminders/cancel_reminder. If a preset uses an aggressive tool-bootstrap filter (likeanchored-standard), keep the three tool names resident so its agents still see them:# inside the tool-bootstrap row's config residentTools: [schedule_reminder, list_reminders, cancel_reminder]
Install it into a profile (this registers both the dependency and the bundle entry, so the Host half is composed):
dsh plugin --profile web add /path/to/dsh-timer-scheduler
Manual equivalent, if you prefer editing the profile yourself — the profile's
package.json needs the package in both places:
{
"dependencies": { "dsh-timer-scheduler-ui": "file:./packages/dsh-timer-scheduler-ui" },
"dsh": { "profile": { "bundles": ["@deepseek-ai/dsh-base", "@deepseek-ai/dsh-web-app", "dsh-timer-scheduler-ui"] } }
}
Restart DSH (the Host half lives in the server process; a browser refresh is not enough), then hard-refresh the page.
Verify from a shell — the Host half must answer both the list and an action:
curl 'http://127.0.0.1:<port>/api/timer-reminders?sessionId=x' # → {"reminders":[]}
curl -X POST 'http://127.0.0.1:<port>/api/timer-reminders?action=retry&id=nope&sessionId=x'
# → 404 {"ok":false,"error":"not-found","id":"nope"} (an OLD host answers 200 with the reminder list here)
Then open any session: the session-header menu shows the countdown, and each row has 补触发 / 取消.
node test/delivery.mjs # 8 cases: same-session delivery, resume w/ preset, queue actions, dedupe
node --check lib/index.js lib/client.js
A full standalone proof installs into a disposable profile and boots it; the
exact commands and their recorded output are in PROFILE_EVIDENCE.md.
In an agent session, just say:
schedule_reminder(delay_seconds=1800, note=…, subject=<subagentId>)schedule_reminder(at="15:00", note=…)list_reminders / cancel_reminder.The header reminder menu shows the countdown while reminders are pending and hides when empty.
schedule_reminder; the host plugin arms a one-shot Cordis timer and writes {id, note, dueMs, sessionId, subject?} to ~/.dsh/timer-reminders.json.agent.followup() (a live agent already carries its own tools);ctx.agents.resume() with the session's own persisted preset mounted in the factory setup (header.agentPreset → agentPresets.mount), so the resumed agent gets the tool registry and prompt sections it was created with. Resuming without that setup composes no tools at all — the historical unknown tool "bash" failure after a wake;ctx.subagents.sendMessage() through its exact live direct parent, so the continuation seam cold-resumes the same child with the persona/toolFilter recorded in its durable subagent/descriptor and keeps the parent's ownership of it. Resuming such a child as a root instead breaks later parent→child delivery with "already owned by an active write handle".
A message is then built with source.kind = 'plugin' and delivered to that agent to wake its driver.
header.parentSession is durable fork lineage (or a child's direct parent) — installation history, never a delivery target.subject and a matching background subagent completion notice (source.kind = 'subagent-settled') or shell background job completion notice (source.plugin = 'tool-jobs') enters the parent session's inbox, the host plugin cancels the reminder automatically./api/timer-reminders?sessionId=… every second and renders the countdown in the session-header menu.POST /api/timer-reminders?action=retry|cancel&id=…&sessionId=…. Without them a parked or overdue entry was visible but unpushable from the browser — only an agent could call retry_reminder.已设定时提醒 vs 复用已存在的定时提醒), so an agent asked twice cannot leave two wakes that both fire.agentPreset; if that preset was deleted or will not mount, the reminder is parked for manual retry (list_reminders shows the reason) instead of being resumed under a different composition.delivering forever (which made it invisible to re-arming and unpushable).node scripts/sync-profile.mjs [profile-dir] copies lib/ into both installed copies (packages/… and node_modules/…) and into the root aliases index.js / client.js.
That last part is not cosmetic. The host loader resolves the bundle name to the package directory and can prefer a root index.js when one exists, so a stale root alias makes the profile keep running old host code after a restart while lib/ looks current — the visible symptom is a menu action that returns HTTP 200 and does nothing, because the old handler ignores the request method. Always sync through the script (or copy both layouts) after editing lib/.
node test/delivery.mjs runs the delivery-contract regression suite against a fake host: live session, cold forked session with preset restoration, deleted preset, transient factory-missing retry, cold subagent child with an offline parent, and cold subagent child with a live parent. It asserts that a reminder never reroutes to a parent/branch session.