dsh-hook-protocol/packages/hooks/hook-protocol官方

175.3kStar19.0kFork0Issue752Watching

提供Claude Code与Codex钩子协议的共享核心实现,包括matcher验证、stdin/退出码/stdout编解码、多钩子合并及hook/*会话事件,供dsh-hooks-claude-code和dsh-hooks-codex桥接插件复用

机审证据安装命令仓库已核验dsh-plugin Topic许可证READMEAI 百科

此插件是大仓库 deepseek-ai/deepseek-harness 的子包,星数与活跃度统计的是整个仓库。

语言
TypeScript
License
MIT
分支
master
ai-agentscordisdshdsh-plugin

安装

$ dsh plugin --profile web add npm:@deepseek-ai/dsh-hook-protocol

在终端中运行以上命令,通过 dsh CLI 安装此插件。可在右上角切换 Profile。 第一次用 dsh?看这篇新手教程

对话式安装

帮我安装 DeepSeek Harness 插件 deepseek-ai/deepseek-harness/packages/hooks/hook-protocol:先查看仓库 https://github.com/deepseek-ai/deepseek-harness 确认安全性,然后执行安装命令并验证插件加载成功。

把这段指令粘贴给 DSH Web GUI 里的助手,由它代你完成安装与验证。

English | 中文

The shared core of the Claude Code / Codex hook wire protocol. NOT a cordis plugin — it registers nothing and injects nothing. It is a library of dialect-neutral primitives the two bridge plugins (@deepseek-ai/dsh-hooks-claude-code, @deepseek-ai/dsh-hooks-codex) import so neither re-implements the identical halves of the protocol.

Codex deliberately reimplements a subset of the Claude Code hook protocol — the same hooks.json matcher-group shape, the same exit-code/stdout output contract, the same command-hook execution model. The genuinely-shared parts live here; each bridge owns only what differs.

What's shared (here) vs. per-dialect (the bridges)

ConcernHere (dsh-hook-protocol)The bridge (dsh-hooks-claude-code / -codex)
Matcher validation + testmatcherDiagnostic(pattern, mode) for parse-time diagnostics; matchesMatcher(pattern, query, mode) for contained runtime matchingpicks its mode (claude = literal-or-regex, codex = always regex) and rejects a config group carrying a diagnostic
Run a hookrunHook(bash, hook, opts, now) — stdin payload + env via ctx.shell, decodebuilds the per-event stdin payload + the dialect's env
Decode outputparseHookOutput(exit, stdout, stderr) → neutral HookOutputmaps the neutral HookOutput onto an extension-point-specific typed Decision
Merge N hooksmergeHookOutputs(outputs) → most-restrictive MergedHookOutcome
Durable recordappendHookInvoked / appendHookResult (hook/* session events; the result's decision/stderrSummary derive from the HookOutput here)calls them around each invocation
Detached-run quiescencecreateDetachedRuns() — track fire-and-forget run chains; drain() aborts, then awaits thempasses signal to each detached runHook, registers drain as its effect disposer

Primitives

  • matcherDiagnostic(matcher, mode) / matchesMatcher(matcher, query, mode) — match-all on absent/''/'*'; claude mode treats a pure [A-Za-z0-9_|]+ pattern as a literal (pipe = exact-match alternation) and anything else as a regex; codex mode is always an unanchored regex. Bridge parsers discard matcher fields for events without matcher subjects, then use matcherDiagnostic to reject an invalid consumed regex with a stable diagnostic before registering any hooks. The runtime predicate still contains an invalid pattern as a non-match, so a direct library caller cannot throw into the agent loop.
  • runHook(bash, hook, options, now) — require and forward the caller-owned options.signal, serialize options.payload to the hook's stdin (with a trailing newline iff options.trailingNewline), merge options.env after the executor's credential scrub (the dsh-shell trusted-plugin API), honor the hook's timeoutSec (else options.defaultTimeoutMs — the bridge owns the default, its config defaulting to the lib's DEFAULT_HOOK_TIMEOUT_MS 10-minute reference), and decode the result (threading options.expectedEventName to the codec). Cancellation therefore reaches the executor's process-group kill and join boundary. Never throws: an executor rejection (infra fault) becomes a HookOutput with exitCode: undefined (a non-blocking error). now is injected for testable durations.
  • parseHookOutput(exitCode, stdout, stderr, expectedEventName?) decodes exit status and structured stdout. Exit 2 blocks with stderr; other failures are non-blocking. A matching hook-specific permission decision overrides the legacy top-level decision; mismatched or missing event discriminators suppress only event-specific fields. Top-level fields remain event-agnostic, and successful non-JSON output is left to the bridge.
  • mergeHookOutputs(outputs) — fold the results of every hook that matched one point: permission precedence deny > ask > allow, halt sticky on the first continue:false, block reasons joined with \n\n, additionalContext/systemMessages accumulated in order.
  • createDetachedRuns() — quiescence tracking for the emit-shaped points, which run detached (no extension point awaits them). The bridge tracks each run chain — the hook run PLUS its continuation — and registers drain() as its effect disposer: drain fires the tracker's abort signal (so a still-running hook process is killed via runHook, not awaited out to its timeout), then resolves once every tracked chain has settled. fiber.dispose() resolving therefore means no detached hook work is left to fire into a disposed context (defensive patterns: dispose must reach quiescence).

hook/* session events

Declaration-merged into SessionEventMap (log-only, like compaction/* — NOT a SurfaceEventType, no surfaceOp): hook/invoked (a hook command ran) and hook/result (its outcome, paired by handlerId, with appendHookResult owning the decision rule). Payloads and per-event JSDoc are in the generated persistence log event catalog; stderrSummary is truncated to the record's stderrSummaryMaxChars (the bridge's config, reference default DEFAULT_STDERR_SUMMARY_MAX_CHARS = 500; omitted when empty).

Hook invocation/result records must sit inside an open turn. UserPromptSubmit, PreToolUse, PostToolUse, and Stop satisfy that owner-defined relation by construction. SessionStart runs before turn 1 and gets no hook/* record; its allowed context remains pending in the inbox until a waking delivery opens a turn — see the hooks Agent Note.

Model Experience

Indirectly, through dsh-hooks-claude-code and dsh-hooks-codex, which can turn parsed hook output into prompt context, blocked outcomes, or continuation feedback.

KV Cache effect

No direct invalidation; the named consumer owns any request-prefix changes.

Known Limitations and Deferred Work

  • HookOutput.updatedInput is parsed but not honored — input rewrite is a deferred consistency-design problem (the pre-tool-input-rewrite Agent Note); a bridge logs + warns when a hook sets it. See src/types.ts for the full contracts.

收录徽章

Listed on deepseek-plugin.org
[![Listed on deepseek-plugin.org](https://img.shields.io/badge/listed_on-deepseek--plugin.org-007EC6)](https://deepseek-plugin.org/plugins/deepseek-ai/deepseek-harness/packages/hooks/hook-protocol)

把这段 markdown 粘贴到你的 GitHub README,链接回本插件详情页。徽章只声明已被本站收录,不代表安全认证。

返回插件目录
dsh-hook-protocol/packages/hooks/hook-protocol — DeepSeek Harness 插件 | deepseek-plugin.org