@deepseek-ai/dsh-hook-protocol 0.2.1-alpha.1 → 0.2.1-alpha.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.i18n.yaml CHANGED
@@ -1,13 +1,13 @@
1
1
  # Bilingual-pair consistency record for README.md (docs/i18n/README.md): per heading
2
2
  # section, a hash of its English and Chinese blocks outside code blocks and generated regions.
3
3
  # After editing either side, bring the other along and re-record with:
4
- # pnpm run verify-translation-pairing --write packages/hooks/hook-protocol/README.md
4
+ # pnpm run verify-translation-pairing --write packages/experimental/hook-protocol/README.md
5
5
  /:
6
6
  en: 7cfe3761eedff848
7
7
  zh: 80b0bed5743d0924
8
8
  /deepseek-ai-dsh-hook-protocol:
9
- en: bcf8ec6c43311fb7
10
- zh: 4ef45c5aaced087d
9
+ en: 3092627c127b688d
10
+ zh: bbdb083e0a5dbc25
11
11
  /deepseek-ai-dsh-hook-protocol/summary:
12
12
  en: e8ab847d0db16043
13
13
  zh: 5c1e72f4b4a54fe1
@@ -30,8 +30,8 @@
30
30
  en: 08b3960150e9c5e2
31
31
  zh: 6bb2f236d697de01
32
32
  /deepseek-ai-dsh-hook-protocol/understand-the-implementation/processing-pipeline:
33
- en: b25d80f19b551f1a
34
- zh: 9c6602f008d89bce
33
+ en: f71878539d98392e
34
+ zh: 826a0dd95475fff3
35
35
  /deepseek-ai-dsh-hook-protocol/understand-the-implementation/hook-session-events:
36
36
  en: f01ab4ab98f052b3
37
37
  zh: 1da25fa50378bd19
@@ -39,11 +39,11 @@
39
39
  en: 7f9239eaea5a945c
40
40
  zh: 9ba6a2ea955d4934
41
41
  /deepseek-ai-dsh-hook-protocol/understand-the-implementation/source-map:
42
- en: aa4983adf89ea45c
43
- zh: cc83b0ede5ff156c
42
+ en: 0d8956a6d8608ee0
43
+ zh: 9f554284c4c0a36e
44
44
  /deepseek-ai-dsh-hook-protocol/further-exploration:
45
- en: 65a937c4c5f221d5
46
- zh: 3b9d6485493dfb8d
45
+ en: 8b585ce73c2be2cc
46
+ zh: c0270fd001e1352b
47
47
  /deepseek-ai-dsh-hook-protocol/model-experience:
48
48
  en: 53d2f59435a4a88f
49
49
  zh: 79c8fc2849b20718
package/README.md CHANGED
@@ -60,6 +60,8 @@ This section explains the design decisions behind the library and points at the
60
60
 
61
61
  ### Processing pipeline
62
62
 
63
+ `selectHookGroups` selects nonempty matching groups in config order. Each bridge resolves the current working directory only when this selection contains commands to run. `appendHookInvoked` omits absent or undefined matchers from the recorded event.
64
+
63
65
  The library is a chain of single-purpose steps, one function each: validate the matcher pattern, run the command through the `dsh-shell` executor, decode the outcome, merge every matched hook's outcome into one most-restrictive result, and record the durable `hook/*` event pair. The matcher's `mode` parameter is the single axis the dialects differ on — `claude-code` interprets a pattern as literal alternatives or a regex, `codex` always as an unanchored regex. Every step degrades to a contained outcome instead of throwing, so a hook can never crash the calling turn: an invalid regex is a non-match, an executor rejection becomes a `HookOutput` with no exit code, exit 2 blocks with stderr as the reason, and every other failure stays non-blocking. Merging applies `deny > ask > allow` precedence, keeps the first `continue: false` stop sticky, and accumulates context in hook order. Detached runs are tracked so `fiber.dispose()` reaches quiescence. The steps live in [`src/matcher.ts`](src/matcher.ts), [`src/runner.ts`](src/runner.ts), [`src/codec.ts`](src/codec.ts), [`src/merge.ts`](src/merge.ts), [`src/events.ts`](src/events.ts), and [`src/detached.ts`](src/detached.ts).
64
66
 
65
67
  ### `hook/*` session events
@@ -82,7 +84,7 @@ The [hook-protocol-lib Agent Note](../../../.agents/notes/archived/feature/2026-
82
84
  | File | Role |
83
85
  |---|---|
84
86
  | [`src/index.ts`](src/index.ts) | Public exports of every primitive and event helper |
85
- | [`src/matcher.ts`](src/matcher.ts) | Match-all sentinels, literal-vs-regex mode, validation and runtime matching |
87
+ | [`src/matcher.ts`](src/matcher.ts) | Matcher validation, dialect matching, and nonempty command-group selection |
86
88
  | [`src/runner.ts`](src/runner.ts) | `runHook` execution through `ctx.shell` and `DEFAULT_HOOK_TIMEOUT_MS` |
87
89
  | [`src/codec.ts`](src/codec.ts) | Exit-code and structured-stdout decoding into `HookOutput` |
88
90
  | [`src/merge.ts`](src/merge.ts) | Most-restrictive merge and the `MergedHookOutcome` type |
package/README.zh.md CHANGED
@@ -60,6 +60,8 @@ kind: "package-library"
60
60
 
61
61
  ### 处理流水线
62
62
 
63
+ `selectHookGroups` 按配置顺序选出包含命令的匹配组。仅当所选组中有待运行的命令时,各桥接才解析当前工作目录。`appendHookInvoked` 不会将缺失或值为 undefined 的 matcher 写入事件。
64
+
63
65
  本库是一串单一用途的步骤,每个步骤一个函数:校验 matcher pattern、通过 `dsh-shell` 执行器运行命令、解码结果、把每个匹配 hook 的结果合并为最严格的一个结果,并记录持久的 `hook/*` 事件对。matcher 的 `mode` 参数是两个方言唯一的差异轴——`claude-code` 把 pattern 解释为字面量备选或正则,`codex` 始终解释为未锚定正则。每个步骤都会降级为受控结果而不是抛异常,因此钩子永远不会使调用轮次崩溃:无效正则是运行时的不匹配,执行器拒绝会变成没有退出码的 `HookOutput`,退出码 2 以 stderr 作为原因阻塞,其他失败均不阻塞。合并应用 `deny > ask > allow` 优先级,保持首个 `continue: false` 停止的粘性,并按 hook 顺序累积上下文。脱离运行会被跟踪,因此 `fiber.dispose()` 能达到完全停稳。这些步骤位于 [`src/matcher.ts`](src/matcher.ts)、[`src/runner.ts`](src/runner.ts)、[`src/codec.ts`](src/codec.ts)、[`src/merge.ts`](src/merge.ts)、[`src/events.ts`](src/events.ts) 与 [`src/detached.ts`](src/detached.ts)。
64
66
 
65
67
  ### `hook/*` 会话事件
@@ -82,7 +84,7 @@ kind: "package-library"
82
84
  | 文件 | 职责 |
83
85
  |---|---|
84
86
  | [`src/index.ts`](src/index.ts) | 每个原语与事件辅助函数的公开导出 |
85
- | [`src/matcher.ts`](src/matcher.ts) | 匹配全部哨兵、字面量或正则模式、校验与运行时匹配 |
87
+ | [`src/matcher.ts`](src/matcher.ts) | matcher 校验、方言匹配,以及包含命令的匹配组选择 |
86
88
  | [`src/runner.ts`](src/runner.ts) | 通过 `ctx.shell` 的 `runHook` 执行与 `DEFAULT_HOOK_TIMEOUT_MS` |
87
89
  | [`src/codec.ts`](src/codec.ts) | 退出码与结构化 stdout 解码为 `HookOutput` |
88
90
  | [`src/merge.ts`](src/merge.ts) | 最严格合并与 `MergedHookOutcome` 类型 |
package/lib/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  //#region lib/types/matcher.js
2
2
  /**
3
- * Matcher shared by both hook dialects. Claude treats alphanumeric/underscore/
3
+ * Matching and command-group selection shared by both hook dialects. Claude treats alphanumeric/underscore/
4
4
  * pipe patterns as literal alternatives and other patterns as regex; Codex
5
5
  * treats every non-empty pattern as an unanchored regex. Missing, empty, and
6
6
  * `*` match all. Runtime matching contains invalid regexes as non-matches;
@@ -50,6 +50,16 @@ function matchesMatcher(matcher, query, mode) {
50
50
  if (mode === "claude-code" && CLAUDE_LITERAL.test(pattern)) return pattern.split("|").includes(query);
51
51
  return compileRegex(pattern)?.test(query) ?? false;
52
52
  }
53
+ /**
54
+ * Select configured groups that have command hooks and match the event subject.
55
+ * @param groups - groups for one hook point; absent points select no groups.
56
+ * @param query - the event's matcher subject.
57
+ * @param mode - dialect controlling literal or regex matching.
58
+ * @returns Selected groups in config order, retaining their original references.
59
+ */
60
+ function selectHookGroups(groups, query, mode) {
61
+ return (groups ?? []).filter((group) => group.hooks.length > 0 && matchesMatcher(group.matcher, query, mode));
62
+ }
53
63
  //#endregion
54
64
  //#region lib/types/codec.js
55
65
  /**
@@ -311,7 +321,7 @@ function summarizeStderr(stderr, maxChars) {
311
321
  /**
312
322
  * Append a `hook/invoked` event naming the handler and hook point to `session`.
313
323
  * @param session - the session whose open turn records the event.
314
- * @param invocation - the invocation identity; an absent `matcher` is omitted from the payload.
324
+ * @param invocation - the invocation identity; absent or `undefined` matchers are omitted from the payload.
315
325
  */
316
326
  function appendHookInvoked(session, invocation) {
317
327
  session.append("hook/invoked", {
@@ -374,4 +384,4 @@ function createDetachedRuns() {
374
384
  };
375
385
  }
376
386
  //#endregion
377
- export { DEFAULT_HOOK_TIMEOUT_MS, DEFAULT_STDERR_SUMMARY_MAX_CHARS, appendHookInvoked, appendHookResult, createDetachedRuns, matcherDiagnostic, matchesMatcher, mergeHookOutputs, parseHookOutput, runHook, summarizeStderr };
387
+ export { DEFAULT_HOOK_TIMEOUT_MS, DEFAULT_STDERR_SUMMARY_MAX_CHARS, appendHookInvoked, appendHookResult, createDetachedRuns, matcherDiagnostic, matchesMatcher, mergeHookOutputs, parseHookOutput, runHook, selectHookGroups, summarizeStderr };
@@ -17,8 +17,8 @@ export interface HookInvocation {
17
17
  dialect: HookDialect;
18
18
  /** A stable id correlating the invoked event with its result. */
19
19
  handlerId: string;
20
- /** The matcher-group pattern that selected it (absent for match-all). */
21
- matcher?: string;
20
+ /** The matcher-group pattern; absent or `undefined` is omitted from the event. */
21
+ matcher?: string | undefined;
22
22
  }
23
23
  /** The decided outcome half of the pair. */
24
24
  export interface HookResultRecord {
@@ -60,7 +60,7 @@ export declare function summarizeStderr(stderr: string, maxChars: number): strin
60
60
  /**
61
61
  * Append a `hook/invoked` event naming the handler and hook point to `session`.
62
62
  * @param session - the session whose open turn records the event.
63
- * @param invocation - the invocation identity; an absent `matcher` is omitted from the payload.
63
+ * @param invocation - the invocation identity; absent or `undefined` matchers are omitted from the payload.
64
64
  */
65
65
  export declare function appendHookInvoked(session: Session, invocation: HookInvocation): void;
66
66
  /**
@@ -6,7 +6,7 @@
6
6
  * @module @deepseek-ai/dsh-hook-protocol
7
7
  */
8
8
  export type { CommandHook, HookDialect, HookOutput, MatcherGroup, MatcherMode, } from './types.ts';
9
- export { matcherDiagnostic, matchesMatcher } from './matcher.ts';
9
+ export { matcherDiagnostic, matchesMatcher, selectHookGroups } from './matcher.ts';
10
10
  export { parseHookOutput } from './codec.ts';
11
11
  export { DEFAULT_HOOK_TIMEOUT_MS, runHook } from './runner.ts';
12
12
  export type { RunHookOptions, RunHookResult } from './runner.ts';
@@ -1,12 +1,12 @@
1
1
  /**
2
- * Matcher shared by both hook dialects. Claude treats alphanumeric/underscore/
2
+ * Matching and command-group selection shared by both hook dialects. Claude treats alphanumeric/underscore/
3
3
  * pipe patterns as literal alternatives and other patterns as regex; Codex
4
4
  * treats every non-empty pattern as an unanchored regex. Missing, empty, and
5
5
  * `*` match all. Runtime matching contains invalid regexes as non-matches;
6
6
  * config parsers use {@link matcherDiagnostic} to reject them with a diagnostic.
7
7
  * @module @deepseek-ai/dsh-hook-protocol/matcher
8
8
  */
9
- import type { MatcherMode } from './types.ts';
9
+ import type { MatcherGroup, MatcherMode } from './types.ts';
10
10
  /**
11
11
  * Validate one matcher before a bridge accepts its config group.
12
12
  * @param matcher - configured pattern; match-all sentinels are valid.
@@ -26,4 +26,12 @@ export declare function matcherDiagnostic(matcher: string | undefined, mode: Mat
26
26
  * regex.
27
27
  */
28
28
  export declare function matchesMatcher(matcher: string | undefined, query: string, mode: MatcherMode): boolean;
29
+ /**
30
+ * Select configured groups that have command hooks and match the event subject.
31
+ * @param groups - groups for one hook point; absent points select no groups.
32
+ * @param query - the event's matcher subject.
33
+ * @param mode - dialect controlling literal or regex matching.
34
+ * @returns Selected groups in config order, retaining their original references.
35
+ */
36
+ export declare function selectHookGroups(groups: readonly MatcherGroup[] | undefined, query: string, mode: MatcherMode): MatcherGroup[];
29
37
  //# sourceMappingURL=matcher.d.ts.map
package/package.json CHANGED
@@ -1,14 +1,14 @@
1
1
  {
2
2
  "name": "@deepseek-ai/dsh-hook-protocol",
3
3
  "description": "Shared Claude Code / Codex hook wire protocol: matcher engine, stdin/exit-code/stdout codec, multi-hook merge, and hook/* session events",
4
- "version": "0.2.1-alpha.1",
4
+ "version": "0.2.1-alpha.3",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
8
8
  "repository": {
9
9
  "type": "git",
10
10
  "url": "git+https://github.com/deepseek-ai/deepseek-harness.git",
11
- "directory": "packages/hooks/hook-protocol"
11
+ "directory": "packages/experimental/hook-protocol"
12
12
  },
13
13
  "type": "module",
14
14
  "main": "lib/index.js",
@@ -27,13 +27,13 @@
27
27
  ],
28
28
  "license": "MIT",
29
29
  "peerDependencies": {
30
- "@deepseek-ai/dsh-shell": "0.2.1-alpha.1",
31
- "@deepseek-ai/dsh-session": "0.2.1-alpha.1",
32
- "@deepseek-ai/cordis": "~4.0.5-alpha.1"
30
+ "@deepseek-ai/dsh-shell": "0.2.1-alpha.3",
31
+ "@deepseek-ai/dsh-session": "0.2.1-alpha.3",
32
+ "@deepseek-ai/cordis": "~4.0.5-alpha.2"
33
33
  },
34
34
  "devDependencies": {
35
- "@deepseek-ai/dsh-session": "0.2.1-alpha.1",
36
- "@deepseek-ai/cordis": "~4.0.5-alpha.1",
37
- "@deepseek-ai/dsh-shell": "0.2.1-alpha.1"
35
+ "@deepseek-ai/dsh-shell": "0.2.1-alpha.3",
36
+ "@deepseek-ai/dsh-session": "0.2.1-alpha.3",
37
+ "@deepseek-ai/cordis": "~4.0.5-alpha.2"
38
38
  }
39
39
  }