Back to home

PeterBon

dsh-hooks

Config-driven lifecycle hooks plugin for DeepSeek Harness

Stars
2
Language
TypeScript
Created
Aug 15, 2026
Updated
Aug 15, 2026

Introduction

dsh-hooks

Config-driven lifecycle hooks plugin for DeepSeek Harness (dsh).

Declare event -> command hooks directly in your profile's cordis.patch.yml — like Codex CLI / OpenCode hooks, but for dsh. No plugin code required.

中文文档 | Design | Feishu example

Install

dsh plugin --profile web add dsh-hooks           # from npm
# or straight from git:
dsh plugin --profile web add github:PeterBon/dsh-hooks

Restart dsh web.

Configure

Add a config block to your profile's cordis.patch.yml:

- id: dsh-hooks
  name: dsh-hooks
  config:
    hooks:
      - on: 'turn/end'
        when: 'completed'            # optional: only completed turns
        run: 'node examples/notify-feishu.mjs'
        timeoutMs: 10000             # optional, default 10000
      - on: 'approval/asked'
        run: 'powershell -Command "Write-Output approval-requested >> hooks.log"'

Events (v1)

EventWhen it firesUseful context
turn/startA turn beginssession id, turn
turn/endA turn ends (completed / error / aborted / blocked / max-tokens / interrupted)reason, turn, duration
approval/askedA tool call requests user approvaltool name, call id, reason
agent/createdAn agent is publishedsession id
agent/disposedAn agent leaves the registrysession id
agent/errorThe agent loop reports an errorerror text
agent/statusAgent status transitionstatus

The when filter for turn/end matches the reason.kind value (completed, error, …). Hooks for other events run unconditionally.

Command execution

  • Each matching hook spawns run through the platform shell, fire-and-forget: failures only console.warn, never retried, never block the agent loop.
  • Context is passed via environment variables (no shell injection through data):
VariableMeaning
DSH_HOOK_EVENTevent type, e.g. turn/end
DSH_HOOK_SESSION_IDsession id
DSH_HOOK_SESSION_NAMEreadable session title (latest session/title log event, or first human prompt)
DSH_HOOK_TURNturn number (turn events)
DSH_HOOK_REASONturn end reason kind
DSH_HOOK_TOOLtool name (approval events)
DSH_HOOK_CALL_IDtool call id (approval events)
DSH_HOOK_DURATION_MSturn duration ms (turn/end)
DSH_HOOK_STATUSagent status (agent/status)
DSH_HOOK_ERRORerror text (agent/error, and the failure message on turn/end error)
DSH_HOOK_CONTENTthe turn's final assistant text (turn events)
DSH_HOOK_TIMESTAMPISO timestamp
  • {{var}} placeholders inside run are substituted from the same context, e.g. run: 'echo {{DSH_HOOK_SESSION_ID}} >> log.txt'.

Feishu notification example

The fastest path is the one-shot setup CLI — it creates the Feishu app for you via a QR-code scan and writes all hook config:

dsh-hooks feishu-setup                 # default profile: web
dsh-hooks feishu-setup --profile work  # another profile
dsh-hooks feishu-test                  # send a test card with the stored credentials

feishu-setup prints a QR code (and opens it in your browser), waits for you to scan it with Feishu, then creates an app named 「DSH 通知机器人」 with message-send permission and writes:

FilePurpose
~/.dsh/dsh-hooks/feishu-config.jsonapp id/secret + your open_id as the notification target (0600, never committed); result_max_chars sets the card content truncation (default 300)
~/.dsh/dsh-hooks/notify-feishu.mjsstable copy of the notify script the hooks reference
~/.dsh/profiles/<profile>/cordis.patch.ymldsh-hooks block: turn/end (completed/error/aborted) + approval/asked + agent/error card hooks

Restart dsh web afterwards — you will get cards when turns finish, approvals are asked, or the agent errors.

Manual configuration

Prefer wiring it by hand? See examples/notify-feishu.mjs — a zero-dependency script that posts turn-completion / approval notices through the Feishu app API (works without a group custom bot). Configure it like:

- id: dsh-hooks
  name: dsh-hooks
  config:
    hooks:
      - on: 'turn/end'
        when: 'completed'
        run: 'node D:/path/to/examples/notify-feishu.mjs'
      - on: 'approval/asked'
        run: 'node D:/path/to/examples/notify-feishu.mjs --approval'

with DSH_HOOKS_FEISHU_APP_ID / DSH_HOOKS_FEISHU_APP_SECRET / DSH_HOOKS_FEISHU_TO in the process environment (never in config files).

Security

Hooks execute arbitrary commands with the dsh process privileges. Only configure commands you trust. Secrets belong in environment variables or the dsh credential store — never in cordis.patch.yml.

Design

Follows the dsh plugin conventions: dsh.bundle.patch mounts the plugin row, the plugin listens to the durable session/event firehose plus agent lifecycle events, and emissions are irreversible side effects that compensate rather than block (failures warn, never retry).

Development

pnpm install
pnpm run check     # typecheck + test + build

Releasing and CI operations (Trusted Publishing, security scanning, gotchas): see docs/RELEASING.md.

License

MIT