@cleocode/core 2026.3.72 → 2026.3.73

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.
Files changed (48) hide show
  1. package/dist/hooks/handlers/agent-hooks.d.ts +48 -0
  2. package/dist/hooks/handlers/agent-hooks.d.ts.map +1 -0
  3. package/dist/hooks/handlers/context-hooks.d.ts +53 -0
  4. package/dist/hooks/handlers/context-hooks.d.ts.map +1 -0
  5. package/dist/hooks/handlers/error-hooks.d.ts +4 -4
  6. package/dist/hooks/handlers/error-hooks.d.ts.map +1 -1
  7. package/dist/hooks/handlers/file-hooks.d.ts +3 -3
  8. package/dist/hooks/handlers/file-hooks.d.ts.map +1 -1
  9. package/dist/hooks/handlers/index.d.ts +8 -1
  10. package/dist/hooks/handlers/index.d.ts.map +1 -1
  11. package/dist/hooks/handlers/mcp-hooks.d.ts +29 -7
  12. package/dist/hooks/handlers/mcp-hooks.d.ts.map +1 -1
  13. package/dist/hooks/handlers/session-hooks.d.ts +5 -5
  14. package/dist/hooks/handlers/session-hooks.d.ts.map +1 -1
  15. package/dist/hooks/handlers/task-hooks.d.ts +5 -5
  16. package/dist/hooks/handlers/task-hooks.d.ts.map +1 -1
  17. package/dist/hooks/handlers/work-capture-hooks.d.ts +7 -7
  18. package/dist/hooks/handlers/work-capture-hooks.d.ts.map +1 -1
  19. package/dist/hooks/payload-schemas.d.ts +177 -11
  20. package/dist/hooks/payload-schemas.d.ts.map +1 -1
  21. package/dist/hooks/provider-hooks.d.ts +33 -7
  22. package/dist/hooks/provider-hooks.d.ts.map +1 -1
  23. package/dist/hooks/registry.d.ts +26 -6
  24. package/dist/hooks/registry.d.ts.map +1 -1
  25. package/dist/hooks/types.d.ts +132 -38
  26. package/dist/hooks/types.d.ts.map +1 -1
  27. package/dist/index.js +335 -59
  28. package/dist/index.js.map +4 -4
  29. package/dist/sessions/snapshot.d.ts.map +1 -1
  30. package/package.json +6 -6
  31. package/src/hooks/handlers/__tests__/hook-automation-e2e.test.ts +634 -0
  32. package/src/hooks/handlers/agent-hooks.ts +148 -0
  33. package/src/hooks/handlers/context-hooks.ts +156 -0
  34. package/src/hooks/handlers/error-hooks.ts +8 -5
  35. package/src/hooks/handlers/file-hooks.ts +6 -4
  36. package/src/hooks/handlers/index.ts +12 -1
  37. package/src/hooks/handlers/mcp-hooks.ts +74 -9
  38. package/src/hooks/handlers/session-hooks.ts +7 -7
  39. package/src/hooks/handlers/task-hooks.ts +7 -7
  40. package/src/hooks/handlers/work-capture-hooks.ts +12 -12
  41. package/src/hooks/payload-schemas.ts +96 -26
  42. package/src/hooks/provider-hooks.ts +50 -9
  43. package/src/hooks/registry.ts +86 -23
  44. package/src/hooks/types.ts +175 -39
  45. package/src/sessions/index.ts +4 -4
  46. package/src/sessions/snapshot.ts +4 -2
  47. package/src/store/json.ts +2 -2
  48. package/src/task-work/index.ts +4 -4
@@ -0,0 +1,148 @@
1
+ /**
2
+ * Agent (Subagent) Lifecycle Hook Handlers
3
+ *
4
+ * Captures SubagentStart and SubagentStop events to BRAIN so that
5
+ * multi-agent orchestration runs leave an auditable trail of which
6
+ * subagents were spawned, what tasks they were assigned, and how
7
+ * they completed.
8
+ *
9
+ * Gated behind brain.autoCapture config. Never throws — all errors are
10
+ * swallowed so that brain capture never blocks agent orchestration.
11
+ *
12
+ * Auto-registers on module load.
13
+ *
14
+ * @task T166
15
+ * @epic T134
16
+ */
17
+
18
+ import { hooks } from '../registry.js';
19
+ import type { SubagentStartPayload, SubagentStopPayload } from '../types.js';
20
+
21
+ // ---------------------------------------------------------------------------
22
+ // Shared helpers
23
+ // ---------------------------------------------------------------------------
24
+
25
+ function isMissingBrainSchemaError(err: unknown): boolean {
26
+ if (!(err instanceof Error)) return false;
27
+ const message = String(err.message || '').toLowerCase();
28
+ return message.includes('no such table') && message.includes('brain_');
29
+ }
30
+
31
+ /**
32
+ * Check whether brain auto-capture is enabled.
33
+ *
34
+ * Resolution order (first truthy wins):
35
+ * 1. brain.autoCapture project config value (via loadConfig cascade)
36
+ *
37
+ * Defaults to false when config is unreadable.
38
+ *
39
+ * @param projectRoot - Absolute path to the project root directory.
40
+ */
41
+ async function isAutoCaptureEnabled(projectRoot: string): Promise<boolean> {
42
+ try {
43
+ const { loadConfig } = await import('../../config.js');
44
+ const config = await loadConfig(projectRoot);
45
+ return config.brain?.autoCapture ?? false;
46
+ } catch {
47
+ return false;
48
+ }
49
+ }
50
+
51
+ // ---------------------------------------------------------------------------
52
+ // Handlers
53
+ // ---------------------------------------------------------------------------
54
+
55
+ /**
56
+ * Handle SubagentStart — log subagent spawn as a BRAIN observation.
57
+ *
58
+ * Records the agent ID, role, and task assignment so orchestrators can
59
+ * trace which agents were active in a given session.
60
+ *
61
+ * Gated behind brain.autoCapture config. Never throws.
62
+ *
63
+ * @param projectRoot - Absolute path to the project root directory.
64
+ * @param payload - SubagentStart event payload.
65
+ *
66
+ * @task T166
67
+ * @epic T134
68
+ */
69
+ export async function handleSubagentStart(
70
+ projectRoot: string,
71
+ payload: SubagentStartPayload,
72
+ ): Promise<void> {
73
+ if (!(await isAutoCaptureEnabled(projectRoot))) return;
74
+
75
+ const { observeBrain } = await import('../../memory/brain-retrieval.js');
76
+
77
+ const rolePart = payload.role ? ` role=${payload.role}` : '';
78
+ const taskPart = payload.taskId ? ` task=${payload.taskId}` : '';
79
+
80
+ try {
81
+ await observeBrain(projectRoot, {
82
+ text: `Subagent spawned: ${payload.agentId}${rolePart}${taskPart}`,
83
+ title: `Subagent start: ${payload.agentId}`,
84
+ type: 'discovery',
85
+ sourceSessionId: payload.sessionId,
86
+ sourceType: 'agent',
87
+ });
88
+ } catch (err) {
89
+ if (!isMissingBrainSchemaError(err)) throw err;
90
+ }
91
+ }
92
+
93
+ /**
94
+ * Handle SubagentStop — log subagent completion result as a BRAIN observation.
95
+ *
96
+ * Records the agent ID, completion status, assigned task, and optional
97
+ * summary reference so orchestrators can audit subagent outcomes.
98
+ *
99
+ * Gated behind brain.autoCapture config. Never throws.
100
+ *
101
+ * @param projectRoot - Absolute path to the project root directory.
102
+ * @param payload - SubagentStop event payload.
103
+ *
104
+ * @task T166
105
+ * @epic T134
106
+ */
107
+ export async function handleSubagentStop(
108
+ projectRoot: string,
109
+ payload: SubagentStopPayload,
110
+ ): Promise<void> {
111
+ if (!(await isAutoCaptureEnabled(projectRoot))) return;
112
+
113
+ const { observeBrain } = await import('../../memory/brain-retrieval.js');
114
+
115
+ const statusPart = payload.status ? ` status=${payload.status}` : '';
116
+ const taskPart = payload.taskId ? ` task=${payload.taskId}` : '';
117
+ const summaryPart = payload.summary ? `\nSummary: ${payload.summary}` : '';
118
+
119
+ try {
120
+ await observeBrain(projectRoot, {
121
+ text: `Subagent completed: ${payload.agentId}${statusPart}${taskPart}${summaryPart}`,
122
+ title: `Subagent stop: ${payload.agentId}`,
123
+ type: 'change',
124
+ sourceSessionId: payload.sessionId,
125
+ sourceType: 'agent',
126
+ });
127
+ } catch (err) {
128
+ if (!isMissingBrainSchemaError(err)) throw err;
129
+ }
130
+ }
131
+
132
+ // ---------------------------------------------------------------------------
133
+ // Auto-registration
134
+ // ---------------------------------------------------------------------------
135
+
136
+ hooks.register({
137
+ id: 'brain-subagent-start',
138
+ event: 'SubagentStart',
139
+ handler: handleSubagentStart,
140
+ priority: 100,
141
+ });
142
+
143
+ hooks.register({
144
+ id: 'brain-subagent-stop',
145
+ event: 'SubagentStop',
146
+ handler: handleSubagentStop,
147
+ priority: 100,
148
+ });
@@ -0,0 +1,156 @@
1
+ /**
2
+ * Context Compaction Hook Handlers
3
+ *
4
+ * Captures PreCompact and PostCompact events to BRAIN so that agents
5
+ * can later observe when context compactions occurred and what state
6
+ * preceded them. This is especially useful for long sessions where
7
+ * context is compacted multiple times.
8
+ *
9
+ * PreCompact saves a session context snapshot before compaction begins.
10
+ * PostCompact records that compaction occurred and the resulting token counts.
11
+ *
12
+ * Gated behind brain.autoCapture config. Never throws — all errors are
13
+ * swallowed so brain capture never blocks context compaction.
14
+ *
15
+ * Auto-registers on module load.
16
+ *
17
+ * @task T166
18
+ * @epic T134
19
+ */
20
+
21
+ import { hooks } from '../registry.js';
22
+ import type { PostCompactPayload, PreCompactPayload } from '../types.js';
23
+
24
+ // ---------------------------------------------------------------------------
25
+ // Shared helpers
26
+ // ---------------------------------------------------------------------------
27
+
28
+ function isMissingBrainSchemaError(err: unknown): boolean {
29
+ if (!(err instanceof Error)) return false;
30
+ const message = String(err.message || '').toLowerCase();
31
+ return message.includes('no such table') && message.includes('brain_');
32
+ }
33
+
34
+ /**
35
+ * Check whether brain auto-capture is enabled.
36
+ *
37
+ * Resolution order (first truthy wins):
38
+ * 1. brain.autoCapture project config value (via loadConfig cascade)
39
+ *
40
+ * Defaults to false when config is unreadable.
41
+ *
42
+ * @param projectRoot - Absolute path to the project root directory.
43
+ */
44
+ async function isAutoCaptureEnabled(projectRoot: string): Promise<boolean> {
45
+ try {
46
+ const { loadConfig } = await import('../../config.js');
47
+ const config = await loadConfig(projectRoot);
48
+ return config.brain?.autoCapture ?? false;
49
+ } catch {
50
+ return false;
51
+ }
52
+ }
53
+
54
+ // ---------------------------------------------------------------------------
55
+ // Handlers
56
+ // ---------------------------------------------------------------------------
57
+
58
+ /**
59
+ * Handle PreCompact — snapshot current session memory context to BRAIN.
60
+ *
61
+ * Fires immediately before context compaction begins. Records the token
62
+ * count and compaction reason so the brain retains context about what
63
+ * was in scope before compaction.
64
+ *
65
+ * Gated behind brain.autoCapture config. Never throws.
66
+ *
67
+ * @param projectRoot - Absolute path to the project root directory.
68
+ * @param payload - PreCompact event payload.
69
+ *
70
+ * @task T166
71
+ * @epic T134
72
+ */
73
+ export async function handlePreCompact(
74
+ projectRoot: string,
75
+ payload: PreCompactPayload,
76
+ ): Promise<void> {
77
+ if (!(await isAutoCaptureEnabled(projectRoot))) return;
78
+
79
+ const { observeBrain } = await import('../../memory/brain-retrieval.js');
80
+
81
+ const tokensPart =
82
+ payload.tokensBefore != null ? ` (~${payload.tokensBefore.toLocaleString()} tokens)` : '';
83
+ const reasonPart = payload.reason ? ` Reason: ${payload.reason}` : '';
84
+
85
+ try {
86
+ await observeBrain(projectRoot, {
87
+ text: `Context compaction about to begin${tokensPart}.${reasonPart}`,
88
+ title: 'Pre-compaction context snapshot',
89
+ type: 'discovery',
90
+ sourceSessionId: payload.sessionId,
91
+ sourceType: 'agent',
92
+ });
93
+ } catch (err) {
94
+ if (!isMissingBrainSchemaError(err)) throw err;
95
+ }
96
+ }
97
+
98
+ /**
99
+ * Handle PostCompact — record compaction completion to BRAIN.
100
+ *
101
+ * Fires immediately after context compaction completes. Records the
102
+ * before/after token counts so agents can correlate observations made
103
+ * before compaction with those made after.
104
+ *
105
+ * Gated behind brain.autoCapture config. Never throws.
106
+ *
107
+ * @param projectRoot - Absolute path to the project root directory.
108
+ * @param payload - PostCompact event payload.
109
+ *
110
+ * @task T166
111
+ * @epic T134
112
+ */
113
+ export async function handlePostCompact(
114
+ projectRoot: string,
115
+ payload: PostCompactPayload,
116
+ ): Promise<void> {
117
+ if (!(await isAutoCaptureEnabled(projectRoot))) return;
118
+
119
+ const { observeBrain } = await import('../../memory/brain-retrieval.js');
120
+
121
+ const statusPart = payload.success ? 'succeeded' : 'failed';
122
+ const beforePart =
123
+ payload.tokensBefore != null ? ` before=${payload.tokensBefore.toLocaleString()}` : '';
124
+ const afterPart =
125
+ payload.tokensAfter != null ? ` after=${payload.tokensAfter.toLocaleString()}` : '';
126
+
127
+ try {
128
+ await observeBrain(projectRoot, {
129
+ text: `Context compaction ${statusPart}${beforePart}${afterPart}`,
130
+ title: 'Post-compaction record',
131
+ type: 'change',
132
+ sourceSessionId: payload.sessionId,
133
+ sourceType: 'agent',
134
+ });
135
+ } catch (err) {
136
+ if (!isMissingBrainSchemaError(err)) throw err;
137
+ }
138
+ }
139
+
140
+ // ---------------------------------------------------------------------------
141
+ // Auto-registration
142
+ // ---------------------------------------------------------------------------
143
+
144
+ hooks.register({
145
+ id: 'brain-pre-compact',
146
+ event: 'PreCompact',
147
+ handler: handlePreCompact,
148
+ priority: 100,
149
+ });
150
+
151
+ hooks.register({
152
+ id: 'brain-post-compact',
153
+ event: 'PostCompact',
154
+ handler: handlePostCompact,
155
+ priority: 100,
156
+ });
@@ -7,17 +7,20 @@
7
7
  */
8
8
 
9
9
  import { hooks } from '../registry.js';
10
- import type { OnErrorPayload } from '../types.js';
10
+ import type { PostToolUseFailurePayload } from '../types.js';
11
11
 
12
12
  /**
13
- * Handle onError - capture operation errors to BRAIN
13
+ * Handle PostToolUseFailure - capture operation errors to BRAIN
14
14
  *
15
15
  * Includes infinite-loop guard: if the payload has _fromHook marker,
16
- * the handler skips to prevent onError -> observeBrain -> onError loops.
16
+ * the handler skips to prevent PostToolUseFailure -> observeBrain -> PostToolUseFailure loops.
17
17
  * Additionally, ALL observeBrain errors are silently suppressed to prevent
18
18
  * re-entrant hook firing.
19
19
  */
20
- export async function handleError(projectRoot: string, payload: OnErrorPayload): Promise<void> {
20
+ export async function handleError(
21
+ projectRoot: string,
22
+ payload: PostToolUseFailurePayload,
23
+ ): Promise<void> {
21
24
  // Infinite-loop guard: skip if this error originated from a hook
22
25
  if (payload.metadata?.['_fromHook']) return;
23
26
 
@@ -42,7 +45,7 @@ export async function handleError(projectRoot: string, payload: OnErrorPayload):
42
45
  // Register handler on module load
43
46
  hooks.register({
44
47
  id: 'brain-error',
45
- event: 'onError',
48
+ event: 'PostToolUseFailure',
46
49
  handler: handleError,
47
50
  priority: 100,
48
51
  });
@@ -12,7 +12,7 @@
12
12
 
13
13
  import { isAbsolute, relative } from 'node:path';
14
14
  import { hooks } from '../registry.js';
15
- import type { OnFileChangePayload } from '../types.js';
15
+ import type { NotificationPayload } from '../types.js';
16
16
 
17
17
  function isMissingBrainSchemaError(err: unknown): boolean {
18
18
  if (!(err instanceof Error)) return false;
@@ -65,7 +65,7 @@ async function isFileCaptureEnabled(projectRoot: string): Promise<boolean> {
65
65
  }
66
66
 
67
67
  /**
68
- * Handle onFileChange - capture file changes to BRAIN
68
+ * Handle Notification (file-change variant) - capture file changes to BRAIN
69
69
  *
70
70
  * Gated behind brain.captureFiles config or CLEO_BRAIN_CAPTURE_FILES env var.
71
71
  * Env var takes precedence over config for backward compatibility.
@@ -75,8 +75,10 @@ async function isFileCaptureEnabled(projectRoot: string): Promise<boolean> {
75
75
  */
76
76
  export async function handleFileChange(
77
77
  projectRoot: string,
78
- payload: OnFileChangePayload,
78
+ payload: NotificationPayload,
79
79
  ): Promise<void> {
80
+ // Only handle file-change notifications
81
+ if (!payload.filePath || !payload.changeType) return;
80
82
  // Opt-in gate: disabled by default to prevent observation noise
81
83
  if (!(await isFileCaptureEnabled(projectRoot))) return;
82
84
 
@@ -111,7 +113,7 @@ export async function handleFileChange(
111
113
  // Register handler on module load
112
114
  hooks.register({
113
115
  id: 'brain-file-change',
114
- event: 'onFileChange',
116
+ event: 'Notification',
115
117
  handler: handleFileChange,
116
118
  priority: 100,
117
119
  });
@@ -3,6 +3,9 @@
3
3
  *
4
4
  * Barrel export for all hook handlers. Importing this module will
5
5
  * auto-register all handlers with the hook registry.
6
+ *
7
+ * @task T166
8
+ * @epic T134
6
9
  */
7
10
 
8
11
  // Import handlers to trigger auto-registration on module load
@@ -12,10 +15,18 @@ import './error-hooks.js';
12
15
  import './file-hooks.js';
13
16
  import './mcp-hooks.js';
14
17
  import './work-capture-hooks.js';
18
+ import './agent-hooks.js';
19
+ import './context-hooks.js';
15
20
 
21
+ export { handleSubagentStart, handleSubagentStop } from './agent-hooks.js';
22
+ export { handlePostCompact, handlePreCompact } from './context-hooks.js';
16
23
  export { handleError } from './error-hooks.js';
17
24
  export { handleFileChange } from './file-hooks.js';
18
- export { handlePromptSubmit, handleResponseComplete } from './mcp-hooks.js';
25
+ export {
26
+ handlePromptSubmit,
27
+ handleResponseComplete,
28
+ handleSystemNotification,
29
+ } from './mcp-hooks.js';
19
30
  // Re-export handler functions for explicit use
20
31
  export { handleSessionEnd, handleSessionStart } from './session-hooks.js';
21
32
  export { handleToolComplete, handleToolStart } from './task-hooks.js';
@@ -1,16 +1,26 @@
1
1
  /**
2
2
  * MCP Prompt/Response Hook Handlers - Wave 2 of T5237
3
3
  *
4
- * Handlers for onPromptSubmit and onResponseComplete events that capture
5
- * ALL gateway operations (read and write) to BRAIN.
4
+ * Handlers for onPromptSubmit, onResponseComplete, and system Notification
5
+ * events that capture ALL gateway operations (read and write) to BRAIN.
6
6
  * By default, NO brain capture (too noisy). Enable via:
7
7
  * - Config: brain.captureMcp = true (checked first)
8
8
  * - Env: CLEO_BRAIN_CAPTURE_MCP=true (overrides config)
9
9
  * Auto-registers on module load.
10
+ *
11
+ * Note: File-change Notification events (those with filePath + changeType) are
12
+ * handled by file-hooks.ts. This module handles message-bearing system
13
+ * notifications (Notification payloads with a message field).
14
+ *
15
+ * @task T166
10
16
  */
11
17
 
12
18
  import { hooks } from '../registry.js';
13
- import type { OnPromptSubmitPayload, OnResponseCompletePayload } from '../types.js';
19
+ import type {
20
+ NotificationPayload,
21
+ PromptSubmitPayload,
22
+ ResponseCompletePayload,
23
+ } from '../types.js';
14
24
 
15
25
  function isMissingBrainSchemaError(err: unknown): boolean {
16
26
  if (!(err instanceof Error)) return false;
@@ -42,14 +52,14 @@ async function isBrainCaptureEnabled(projectRoot: string): Promise<boolean> {
42
52
  }
43
53
 
44
54
  /**
45
- * Handle onPromptSubmit - optionally capture ALL gateway prompt events to BRAIN.
55
+ * Handle PromptSubmit - optionally capture ALL gateway prompt events to BRAIN.
46
56
  *
47
57
  * No-op by default. Enable via brain.captureMcp config or CLEO_BRAIN_CAPTURE_MCP env.
48
58
  * For selective mutation-only capture, use work-capture-hooks.ts instead.
49
59
  */
50
60
  export async function handlePromptSubmit(
51
61
  projectRoot: string,
52
- payload: OnPromptSubmitPayload,
62
+ payload: PromptSubmitPayload,
53
63
  ): Promise<void> {
54
64
  if (!(await isBrainCaptureEnabled(projectRoot))) return;
55
65
 
@@ -68,14 +78,14 @@ export async function handlePromptSubmit(
68
78
  }
69
79
 
70
80
  /**
71
- * Handle onResponseComplete - optionally capture ALL gateway response events to BRAIN.
81
+ * Handle ResponseComplete - optionally capture ALL gateway response events to BRAIN.
72
82
  *
73
83
  * No-op by default. Enable via brain.captureMcp config or CLEO_BRAIN_CAPTURE_MCP env.
74
84
  * For selective mutation-only capture, use work-capture-hooks.ts instead.
75
85
  */
76
86
  export async function handleResponseComplete(
77
87
  projectRoot: string,
78
- payload: OnResponseCompletePayload,
88
+ payload: ResponseCompletePayload,
79
89
  ): Promise<void> {
80
90
  if (!(await isBrainCaptureEnabled(projectRoot))) return;
81
91
 
@@ -93,17 +103,72 @@ export async function handleResponseComplete(
93
103
  }
94
104
  }
95
105
 
106
+ /**
107
+ * Handle Notification — capture system notifications as BRAIN observations.
108
+ *
109
+ * Only fires for Notification payloads that carry a message field (i.e. system
110
+ * notifications). File-change notifications (filePath + changeType) are
111
+ * handled exclusively by file-hooks.ts and are skipped here to avoid
112
+ * double-capture.
113
+ *
114
+ * Gated behind brain.autoCapture config. Never throws.
115
+ *
116
+ * @param projectRoot - Absolute path to the project root directory.
117
+ * @param payload - Notification event payload.
118
+ *
119
+ * @task T166
120
+ */
121
+ export async function handleSystemNotification(
122
+ projectRoot: string,
123
+ payload: NotificationPayload,
124
+ ): Promise<void> {
125
+ // File-change notifications are handled by file-hooks.ts
126
+ if (payload.filePath || payload.changeType) return;
127
+ // Only handle message-bearing system notifications
128
+ if (!payload.message) return;
129
+
130
+ try {
131
+ const { loadConfig } = await import('../../config.js');
132
+ const config = await loadConfig(projectRoot);
133
+ if (!config.brain?.autoCapture) return;
134
+ } catch {
135
+ return;
136
+ }
137
+
138
+ const { observeBrain } = await import('../../memory/brain-retrieval.js');
139
+
140
+ try {
141
+ await observeBrain(projectRoot, {
142
+ text: `System notification: ${payload.message}`,
143
+ title: `Notification: ${payload.message.slice(0, 60)}`,
144
+ type: 'discovery',
145
+ sourceSessionId: payload.sessionId,
146
+ sourceType: 'agent',
147
+ });
148
+ } catch (err) {
149
+ if (!isMissingBrainSchemaError(err)) throw err;
150
+ }
151
+ }
152
+
96
153
  // Register handlers on module load
97
154
  hooks.register({
98
155
  id: 'brain-prompt-submit',
99
- event: 'onPromptSubmit',
156
+ event: 'PromptSubmit',
100
157
  handler: handlePromptSubmit,
101
158
  priority: 100,
102
159
  });
103
160
 
104
161
  hooks.register({
105
162
  id: 'brain-response-complete',
106
- event: 'onResponseComplete',
163
+ event: 'ResponseComplete',
107
164
  handler: handleResponseComplete,
108
165
  priority: 100,
109
166
  });
167
+
168
+ // Lower priority (90) so file-hooks.ts (100) runs first for Notification events
169
+ hooks.register({
170
+ id: 'brain-system-notification',
171
+ event: 'Notification',
172
+ handler: handleSystemNotification,
173
+ priority: 90,
174
+ });
@@ -10,7 +10,7 @@
10
10
  */
11
11
 
12
12
  import { hooks } from '../registry.js';
13
- import type { OnSessionEndPayload, OnSessionStartPayload } from '../types.js';
13
+ import type { SessionEndPayload, SessionStartPayload } from '../types.js';
14
14
  import { maybeRefreshMemoryBridge } from './memory-bridge-refresh.js';
15
15
 
16
16
  function isMissingBrainSchemaError(err: unknown): boolean {
@@ -20,14 +20,14 @@ function isMissingBrainSchemaError(err: unknown): boolean {
20
20
  }
21
21
 
22
22
  /**
23
- * Handle onSessionStart - capture initial session context
23
+ * Handle SessionStart - capture initial session context
24
24
  *
25
25
  * T138: Refresh memory bridge on session start.
26
26
  * T139: Regenerate bridge with session scope context.
27
27
  */
28
28
  export async function handleSessionStart(
29
29
  projectRoot: string,
30
- payload: OnSessionStartPayload,
30
+ payload: SessionStartPayload,
31
31
  ): Promise<void> {
32
32
  const { observeBrain } = await import('../../memory/brain-retrieval.js');
33
33
 
@@ -48,14 +48,14 @@ export async function handleSessionStart(
48
48
  }
49
49
 
50
50
  /**
51
- * Handle onSessionEnd - capture session summary
51
+ * Handle SessionEnd - capture session summary
52
52
  *
53
53
  * T138: Refresh memory bridge after session ends.
54
54
  * T144: Extract transcript observations via cross-provider adapter.
55
55
  */
56
56
  export async function handleSessionEnd(
57
57
  projectRoot: string,
58
- payload: OnSessionEndPayload,
58
+ payload: SessionEndPayload,
59
59
  ): Promise<void> {
60
60
  const { observeBrain } = await import('../../memory/brain-retrieval.js');
61
61
 
@@ -107,14 +107,14 @@ export async function handleSessionEnd(
107
107
  // Register handlers on module load
108
108
  hooks.register({
109
109
  id: 'brain-session-start',
110
- event: 'onSessionStart',
110
+ event: 'SessionStart',
111
111
  handler: handleSessionStart,
112
112
  priority: 100,
113
113
  });
114
114
 
115
115
  hooks.register({
116
116
  id: 'brain-session-end',
117
- event: 'onSessionEnd',
117
+ event: 'SessionEnd',
118
118
  handler: handleSessionEnd,
119
119
  priority: 100,
120
120
  });
@@ -8,7 +8,7 @@
8
8
  */
9
9
 
10
10
  import { hooks } from '../registry.js';
11
- import type { OnToolCompletePayload, OnToolStartPayload } from '../types.js';
11
+ import type { PostToolUsePayload, PreToolUsePayload } from '../types.js';
12
12
  import { maybeRefreshMemoryBridge } from './memory-bridge-refresh.js';
13
13
 
14
14
  function isMissingBrainSchemaError(err: unknown): boolean {
@@ -18,11 +18,11 @@ function isMissingBrainSchemaError(err: unknown): boolean {
18
18
  }
19
19
 
20
20
  /**
21
- * Handle onToolStart (maps to task.start in CLEO)
21
+ * Handle PreToolUse (maps to task.start in CLEO, canonical: was onToolStart)
22
22
  */
23
23
  export async function handleToolStart(
24
24
  projectRoot: string,
25
- payload: OnToolStartPayload,
25
+ payload: PreToolUsePayload,
26
26
  ): Promise<void> {
27
27
  const { observeBrain } = await import('../../memory/brain-retrieval.js');
28
28
 
@@ -39,13 +39,13 @@ export async function handleToolStart(
39
39
  }
40
40
 
41
41
  /**
42
- * Handle onToolComplete (maps to task.complete in CLEO)
42
+ * Handle PostToolUse (maps to task.complete in CLEO, canonical: was onToolComplete)
43
43
  *
44
44
  * T138: Refresh memory bridge after task completion.
45
45
  */
46
46
  export async function handleToolComplete(
47
47
  projectRoot: string,
48
- payload: OnToolCompletePayload,
48
+ payload: PostToolUsePayload,
49
49
  ): Promise<void> {
50
50
  const { observeBrain } = await import('../../memory/brain-retrieval.js');
51
51
 
@@ -67,14 +67,14 @@ export async function handleToolComplete(
67
67
  // Register handlers
68
68
  hooks.register({
69
69
  id: 'brain-tool-start',
70
- event: 'onToolStart',
70
+ event: 'PreToolUse',
71
71
  handler: handleToolStart,
72
72
  priority: 100,
73
73
  });
74
74
 
75
75
  hooks.register({
76
76
  id: 'brain-tool-complete',
77
- event: 'onToolComplete',
77
+ event: 'PostToolUse',
78
78
  handler: handleToolComplete,
79
79
  priority: 100,
80
80
  });