@mnemonik/cursor-hooks 0.7.65 → 0.7.68

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.
@@ -0,0 +1,356 @@
1
+ import { createHash, randomUUID } from 'node:crypto';
2
+ import { chmodSync, closeSync, existsSync, lstatSync, mkdirSync, openSync, readFileSync, readdirSync, renameSync, rmSync, writeFileSync, } from 'node:fs';
3
+ import { dirname, join } from 'node:path';
4
+ import { sessionStateFilePath } from './sessionState.js';
5
+ const RESULT_CUE_CHARS = 1_024;
6
+ const RESULT_CUE_FILE_RE = /^result-cue-[a-f0-9]{64}\.json$/u;
7
+ const MAX_RESULT_CUE_FILE_BYTES = 8 * 1_024;
8
+ const READ_COMMAND_PREFIX = /^\s*(?:cat(?:\s|$)|head(?:\s|$)|sed\s+-n(?:\s|$)|grep(?:\s|$)|rg(?:\s|$))/u;
9
+ export const RESULT_CUE_TTL_MS = 2 * 60 * 1_000;
10
+ export const MAX_PENDING_RESULT_CUES = 16;
11
+ function hash(value) {
12
+ return createHash('sha256').update(value).digest('hex');
13
+ }
14
+ function hasDisallowedTextControl(text) {
15
+ for (let index = 0; index < text.length; index += 1) {
16
+ const code = text.charCodeAt(index);
17
+ const disallowedC0 = code < 0x20 && code !== 0x09 && code !== 0x0a && code !== 0x0d;
18
+ if (disallowedC0 || (code >= 0x7f && code <= 0x9f))
19
+ return true;
20
+ }
21
+ return false;
22
+ }
23
+ function isRecognizableBinaryText(text) {
24
+ const leading = text.trimStart();
25
+ const lower = leading.toLowerCase();
26
+ return (leading.startsWith('GIF87a') ||
27
+ leading.startsWith('GIF89a') ||
28
+ leading.startsWith('%PDF-') ||
29
+ /^(?:iVBORw0KGgo|\/9j\/|R0lGOD|UklGR[\s\S]{4}WEBP)/u.test(leading) ||
30
+ /^(?:<\?xml[\s\S]*?\?>\s*)?(?:(?:<!--[\s\S]*?-->|<!doctype\s+svg(?:\s[^>]*)?>)\s*)*<svg(?:\s|>)/iu.test(leading) ||
31
+ lower.startsWith('data:image') ||
32
+ lower.startsWith('data:application/octet-stream'));
33
+ }
34
+ /** Bound and validate text before it may become a server-side recall cue. */
35
+ export function safeCursorResultText(value) {
36
+ if (typeof value !== 'string')
37
+ return undefined;
38
+ if (value.trim().length === 0 ||
39
+ value.includes('\ufffd') ||
40
+ hasDisallowedTextControl(value) ||
41
+ isRecognizableBinaryText(value)) {
42
+ return undefined;
43
+ }
44
+ // The server's z.string().max(1024) counts UTF-16 code units, as does
45
+ // String#slice. Keep the client bound identical so astral text cannot turn a
46
+ // locally valid excerpt into a rejected request.
47
+ return value.slice(0, RESULT_CUE_CHARS);
48
+ }
49
+ function hasBinaryResultShape(value, depth = 0) {
50
+ if (!value || typeof value !== 'object' || depth > 3)
51
+ return false;
52
+ if (Array.isArray(value))
53
+ return value.some((item) => hasBinaryResultShape(item, depth + 1));
54
+ const record = value;
55
+ if (record.isImage === true)
56
+ return true;
57
+ const source = record.source;
58
+ const tags = [
59
+ record.type,
60
+ record.mimeType,
61
+ record.mime_type,
62
+ record.media_type,
63
+ record.content_type,
64
+ source && typeof source === 'object' ? source.type : undefined,
65
+ source && typeof source === 'object'
66
+ ? source.media_type
67
+ : undefined,
68
+ ];
69
+ if (tags.some((tag) => /^(?:image|audio|video|binary|application\/octet-stream)(?:\/|$)/iu.test(String(tag ?? '')))) {
70
+ return true;
71
+ }
72
+ return Object.values(record).some((item) => hasBinaryResultShape(item, depth + 1));
73
+ }
74
+ function textualCursorResult(value) {
75
+ if (typeof value === 'string')
76
+ return value;
77
+ if (!value || typeof value !== 'object' || Array.isArray(value))
78
+ return undefined;
79
+ if (hasBinaryResultShape(value))
80
+ return undefined;
81
+ const record = value;
82
+ for (const key of ['content', 'output', 'stdout', 'text', 'result']) {
83
+ if (typeof record[key] === 'string')
84
+ return record[key];
85
+ }
86
+ const file = record.file;
87
+ if (file && typeof file === 'object' && !Array.isArray(file)) {
88
+ const content = file.content;
89
+ if (typeof content === 'string')
90
+ return content;
91
+ }
92
+ if (Array.isArray(record.content)) {
93
+ const blocks = [];
94
+ for (const block of record.content) {
95
+ if (!block || typeof block !== 'object' || Array.isArray(block))
96
+ continue;
97
+ const typed = block;
98
+ if (typed.type === 'text' && typeof typed.text === 'string')
99
+ blocks.push(typed.text);
100
+ }
101
+ return blocks.length > 0 ? blocks.join('\n') : undefined;
102
+ }
103
+ return undefined;
104
+ }
105
+ export function isReadResultTool(toolName, toolInput) {
106
+ if (toolName === 'Read' || toolName === 'Grep')
107
+ return true;
108
+ if (toolName !== 'Shell')
109
+ return false;
110
+ const command = toolInput?.command;
111
+ return typeof command === 'string' && READ_COMMAND_PREFIX.test(command);
112
+ }
113
+ /** Extract direct textual content from Cursor's JSON-string PostToolUse result. */
114
+ export function extractCursorResultExcerpt(input) {
115
+ if (!isReadResultTool(input.toolName, input.toolInput) || typeof input.toolOutput !== 'string') {
116
+ return undefined;
117
+ }
118
+ let parsed;
119
+ try {
120
+ parsed = JSON.parse(input.toolOutput);
121
+ }
122
+ catch {
123
+ return undefined;
124
+ }
125
+ const raw = textualCursorResult(parsed);
126
+ if (raw === undefined)
127
+ return undefined;
128
+ const text = input.toolName === 'Read' ? raw.replace(/^[ \t]*\d+→ ?/gmu, '') : raw;
129
+ return safeCursorResultText(text);
130
+ }
131
+ /** A newline may delimit provider/native ids; every segment remains printable. */
132
+ export function validCursorToolUseId(value) {
133
+ if (typeof value !== 'string' || value.length === 0 || value.length > 255)
134
+ return undefined;
135
+ if (value.includes('\ufffd'))
136
+ return undefined;
137
+ const segments = value.split('\n');
138
+ if (segments.some((segment) => segment.length === 0 ||
139
+ // eslint-disable-next-line no-control-regex -- rejecting control bytes is the point
140
+ /[\u0000-\u0009\u000b-\u001f\u007f-\u009f]/u.test(segment))) {
141
+ return undefined;
142
+ }
143
+ return value;
144
+ }
145
+ function cuePath(sessionId, toolUseId) {
146
+ return sessionStateFilePath(sessionId, `result-cue-${hash(toolUseId)}.json`);
147
+ }
148
+ function ensurePrivateDirectory(filePath) {
149
+ const directory = dirname(filePath);
150
+ const root = dirname(directory);
151
+ try {
152
+ if (existsSync(root) && lstatSync(root).isSymbolicLink())
153
+ return false;
154
+ mkdirSync(root, { recursive: true, mode: 0o700 });
155
+ chmodSync(root, 0o700);
156
+ if (existsSync(directory) && lstatSync(directory).isSymbolicLink())
157
+ return false;
158
+ if (!existsSync(directory))
159
+ mkdirSync(directory, { mode: 0o700 });
160
+ chmodSync(directory, 0o700);
161
+ return lstatSync(directory).isDirectory() && !lstatSync(directory).isSymbolicLink();
162
+ }
163
+ catch {
164
+ return false;
165
+ }
166
+ }
167
+ function readCue(filePath) {
168
+ try {
169
+ const state = lstatSync(filePath);
170
+ if (!state.isFile() || state.isSymbolicLink() || state.size > MAX_RESULT_CUE_FILE_BYTES) {
171
+ return undefined;
172
+ }
173
+ const value = JSON.parse(readFileSync(filePath, 'utf8'));
174
+ if (value.version !== 1 ||
175
+ typeof value.targetKey !== 'string' ||
176
+ typeof value.createdAt !== 'number' ||
177
+ typeof value.expiresAt !== 'number' ||
178
+ !Number.isFinite(value.createdAt) ||
179
+ !Number.isFinite(value.expiresAt) ||
180
+ !/^[a-f0-9]{64}$/u.test(value.targetKey) ||
181
+ (value.excerpt !== undefined && typeof value.excerpt !== 'string')) {
182
+ return undefined;
183
+ }
184
+ if (value.excerpt !== undefined && safeCursorResultText(value.excerpt) !== value.excerpt) {
185
+ return undefined;
186
+ }
187
+ return value;
188
+ }
189
+ catch {
190
+ return undefined;
191
+ }
192
+ }
193
+ function writeCue(filePath, value) {
194
+ if (!ensurePrivateDirectory(filePath))
195
+ return false;
196
+ const directory = dirname(filePath);
197
+ const temporary = join(directory, `.result-cue-${process.pid}-${randomUUID()}.tmp`);
198
+ let descriptor;
199
+ try {
200
+ if (existsSync(filePath) && lstatSync(filePath).isSymbolicLink())
201
+ return false;
202
+ descriptor = openSync(temporary, 'wx', 0o600);
203
+ writeFileSync(descriptor, JSON.stringify(value), 'utf8');
204
+ closeSync(descriptor);
205
+ descriptor = undefined;
206
+ chmodSync(temporary, 0o600);
207
+ renameSync(temporary, filePath);
208
+ return true;
209
+ }
210
+ catch {
211
+ if (descriptor !== undefined) {
212
+ try {
213
+ closeSync(descriptor);
214
+ }
215
+ catch {
216
+ // Best effort only.
217
+ }
218
+ }
219
+ rmSync(temporary, { force: true });
220
+ return false;
221
+ }
222
+ }
223
+ function cueFiles(sessionId) {
224
+ const directory = dirname(sessionStateFilePath(sessionId, 'state.json'));
225
+ try {
226
+ if (!existsSync(directory))
227
+ return [];
228
+ const state = lstatSync(directory);
229
+ if (!state.isDirectory() || state.isSymbolicLink())
230
+ return [];
231
+ return readdirSync(directory)
232
+ .filter((name) => RESULT_CUE_FILE_RE.test(name))
233
+ .map((name) => join(directory, name));
234
+ }
235
+ catch {
236
+ return [];
237
+ }
238
+ }
239
+ function removeExpiredAndBound(sessionId, now) {
240
+ const retained = cueFiles(sessionId)
241
+ .map((filePath) => ({ filePath, cue: readCue(filePath) }))
242
+ .filter((entry) => {
243
+ if (!entry.cue || entry.cue.expiresAt <= now) {
244
+ rmSync(entry.filePath, { force: true });
245
+ return false;
246
+ }
247
+ return true;
248
+ })
249
+ .sort((left, right) => (left.cue?.createdAt ?? 0) - (right.cue?.createdAt ?? 0));
250
+ while (retained.length >= MAX_PENDING_RESULT_CUES) {
251
+ const oldest = retained.shift();
252
+ if (oldest)
253
+ rmSync(oldest.filePath, { force: true });
254
+ }
255
+ }
256
+ export function rememberPendingResultCue(input) {
257
+ const toolUseId = validCursorToolUseId(input.toolUseId);
258
+ if (!input.sessionId || !toolUseId || !input.target)
259
+ return false;
260
+ const now = input.now ?? Date.now();
261
+ const filePath = cuePath(input.sessionId, toolUseId);
262
+ removeExpiredAndBound(input.sessionId, now);
263
+ return writeCue(filePath, {
264
+ version: 1,
265
+ targetKey: hash(input.target),
266
+ createdAt: now,
267
+ expiresAt: now + RESULT_CUE_TTL_MS,
268
+ });
269
+ }
270
+ export function attachPendingResultCue(input) {
271
+ if (!input.sessionId || !input.target)
272
+ return false;
273
+ const excerpt = safeCursorResultText(input.excerpt);
274
+ if (!excerpt)
275
+ return false;
276
+ const now = input.now ?? Date.now();
277
+ const targetKey = hash(input.target);
278
+ const candidates = cueFiles(input.sessionId)
279
+ .map((filePath) => ({ filePath, cue: readCue(filePath) }))
280
+ .filter((entry) => !!entry.cue &&
281
+ entry.cue.expiresAt > now &&
282
+ entry.cue.targetKey === targetKey &&
283
+ entry.cue.excerpt === undefined)
284
+ .sort((left, right) => right.cue.createdAt - left.cue.createdAt);
285
+ const matched = candidates[0];
286
+ if (!matched) {
287
+ // Dedicated BeforeReadFile/AfterShellExecution payloads do not carry the
288
+ // generic tool_use_id. Keep one target-keyed fallback so the later generic
289
+ // PostToolUse can bind it to its validated native action id. The key and
290
+ // filename are still hashes; no path or command becomes a path segment.
291
+ removeExpiredAndBound(input.sessionId, now);
292
+ return writeCue(cuePath(input.sessionId, `target:${targetKey}`), {
293
+ version: 1,
294
+ targetKey,
295
+ excerpt,
296
+ createdAt: now,
297
+ expiresAt: now + RESULT_CUE_TTL_MS,
298
+ });
299
+ }
300
+ return writeCue(matched.filePath, { ...matched.cue, excerpt });
301
+ }
302
+ export function takePendingResultCue(input) {
303
+ const toolUseId = validCursorToolUseId(input.toolUseId);
304
+ if (!input.sessionId || !toolUseId || !input.target)
305
+ return undefined;
306
+ const filePath = cuePath(input.sessionId, toolUseId);
307
+ const cue = readCue(filePath);
308
+ const now = input.now ?? Date.now();
309
+ if (!cue) {
310
+ const targetKey = hash(input.target);
311
+ const fallback = cueFiles(input.sessionId)
312
+ .map((candidatePath) => ({ candidatePath, cue: readCue(candidatePath) }))
313
+ .filter((entry) => !!entry.cue &&
314
+ entry.cue.expiresAt > now &&
315
+ entry.cue.targetKey === targetKey &&
316
+ entry.cue.excerpt !== undefined)
317
+ .sort((left, right) => right.cue.createdAt - left.cue.createdAt)[0];
318
+ if (!fallback)
319
+ return undefined;
320
+ rmSync(fallback.candidatePath, { force: true });
321
+ if (fallback.cue.excerpt === undefined)
322
+ return undefined;
323
+ return { excerpt: fallback.cue.excerpt };
324
+ }
325
+ if (cue.expiresAt <= now) {
326
+ rmSync(filePath, { force: true });
327
+ return undefined;
328
+ }
329
+ if (cue.targetKey !== hash(input.target))
330
+ return undefined;
331
+ rmSync(filePath, { force: true });
332
+ return cue.excerpt === undefined ? undefined : { excerpt: cue.excerpt };
333
+ }
334
+ export function discardPendingResultCue(input) {
335
+ if (!input.sessionId)
336
+ return false;
337
+ const toolUseId = validCursorToolUseId(input.toolUseId);
338
+ let removed = false;
339
+ if (toolUseId) {
340
+ const filePath = cuePath(input.sessionId, toolUseId);
341
+ const existed = existsSync(filePath);
342
+ rmSync(filePath, { force: true });
343
+ removed = existed;
344
+ }
345
+ if (!input.target)
346
+ return removed;
347
+ const targetKey = hash(input.target);
348
+ for (const filePath of cueFiles(input.sessionId)) {
349
+ if (readCue(filePath)?.targetKey !== targetKey)
350
+ continue;
351
+ rmSync(filePath, { force: true });
352
+ removed = true;
353
+ }
354
+ return removed;
355
+ }
356
+ //# sourceMappingURL=resultCue.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resultCue.js","sourceRoot":"","sources":["../../src/lib/resultCue.ts"],"names":[],"mappings":"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"}
@@ -15,6 +15,12 @@
15
15
  export declare const STALE_SESSION_MS: number;
16
16
  /** A named file inside this session's private directory. */
17
17
  export declare function sessionStateFilePath(sessionId: string, fileName: string): string;
18
+ /**
19
+ * Remove exactly one hashed session directory without following links.
20
+ * SessionEnd cleanup is best effort: a hostile or malformed state path must
21
+ * never block the host from closing the session.
22
+ */
23
+ export declare function removeSessionState(sessionId: string): boolean;
18
24
  /**
19
25
  * Remove abandoned session directories. Runs once per SessionStart and checks
20
26
  * the directory plus each direct child before deletion. Never follows links.
@@ -12,7 +12,7 @@
12
12
  * short-TTL per-event dedup markers and is not per-session state.
13
13
  */
14
14
  import { createHash } from 'node:crypto';
15
- import { existsSync, lstatSync, readdirSync, rmSync } from 'node:fs';
15
+ import { existsSync, lstatSync, readdirSync, rmSync, unlinkSync } from 'node:fs';
16
16
  import { tmpdir } from 'node:os';
17
17
  import { join } from 'node:path';
18
18
  /** Sessions older than this are abandoned; Cursor fires no reliable end event. */
@@ -25,6 +25,34 @@ function privateRoot() {
25
25
  export function sessionStateFilePath(sessionId, fileName) {
26
26
  return join(privateRoot(), createHash('sha256').update(sessionId).digest('hex'), fileName);
27
27
  }
28
+ /**
29
+ * Remove exactly one hashed session directory without following links.
30
+ * SessionEnd cleanup is best effort: a hostile or malformed state path must
31
+ * never block the host from closing the session.
32
+ */
33
+ export function removeSessionState(sessionId) {
34
+ if (!sessionId)
35
+ return false;
36
+ const root = privateRoot();
37
+ const directory = join(root, createHash('sha256').update(sessionId).digest('hex'));
38
+ try {
39
+ if (!existsSync(root) || lstatSync(root).isSymbolicLink() || !existsSync(directory)) {
40
+ return false;
41
+ }
42
+ const state = lstatSync(directory);
43
+ if (state.isSymbolicLink()) {
44
+ unlinkSync(directory);
45
+ return true;
46
+ }
47
+ if (!state.isDirectory())
48
+ return false;
49
+ rmSync(directory, { recursive: true, force: true });
50
+ return true;
51
+ }
52
+ catch {
53
+ return false;
54
+ }
55
+ }
28
56
  /**
29
57
  * Remove abandoned session directories. Runs once per SessionStart and checks
30
58
  * the directory plus each direct child before deletion. Never follows links.
@@ -1 +1 @@
1
- {"version":3,"file":"sessionState.js","sourceRoot":"","sources":["../../src/lib/sessionState.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACrE,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,kFAAkF;AAClF,MAAM,CAAC,MAAM,gBAAgB,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;AAEpD,SAAS,WAAW;IAClB,MAAM,GAAG,GAAG,OAAO,OAAO,CAAC,MAAM,KAAK,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC;IAC7E,OAAO,IAAI,CAAC,MAAM,EAAE,EAAE,mBAAmB,GAAG,EAAE,CAAC,CAAC;AAClD,CAAC;AAED,4DAA4D;AAC5D,MAAM,UAAU,oBAAoB,CAAC,SAAiB,EAAE,QAAgB;IACtE,OAAO,IAAI,CAAC,WAAW,EAAE,EAAE,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,QAAQ,CAAC,CAAC;AAC7F,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,qBAAqB,CAAC,GAAG,GAAW,IAAI,CAAC,GAAG,EAAE;IAC5D,MAAM,IAAI,GAAG,WAAW,EAAE,CAAC;IAC3B,IAAI,OAAO,GAAG,CAAC,CAAC;IAChB,IAAI,CAAC;QACH,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,SAAS,CAAC,IAAI,CAAC,CAAC,cAAc,EAAE;YAAE,OAAO,CAAC,CAAC;QACpE,KAAK,MAAM,KAAK,IAAI,WAAW,CAAC,IAAI,CAAC,EAAE,CAAC;YACtC,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;YAC/B,IAAI,CAAC;gBACH,MAAM,EAAE,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;gBAC3B,IAAI,CAAC,EAAE,CAAC,WAAW,EAAE,IAAI,EAAE,CAAC,cAAc,EAAE;oBAAE,SAAS;gBACvD,IAAI,aAAa,GAAG,EAAE,CAAC,OAAO,CAAC;gBAC/B,KAAK,MAAM,KAAK,IAAI,WAAW,CAAC,IAAI,CAAC,EAAE,CAAC;oBACtC,aAAa,GAAG,IAAI,CAAC,GAAG,CAAC,aAAa,EAAE,SAAS,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;gBAChF,CAAC;gBACD,IAAI,GAAG,GAAG,aAAa,GAAG,gBAAgB;oBAAE,SAAS;gBACrD,MAAM,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;gBAC/C,OAAO,IAAI,CAAC,CAAC;YACf,CAAC;YAAC,MAAM,CAAC;gBACP,yBAAyB;YAC3B,CAAC;QACH,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,6DAA6D;IAC/D,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC"}
1
+ {"version":3,"file":"sessionState.js","sourceRoot":"","sources":["../../src/lib/sessionState.ts"],"names":[],"mappings":"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"}
@@ -1,8 +1,9 @@
1
- export type CursorHookEvent = 'sessionStart' | 'beforeSubmitPrompt' | 'preToolUse' | 'postToolUse' | 'beforeShellExecution' | 'beforeMCPExecution' | 'afterMCPExecution' | 'afterFileEdit' | 'stop' | 'subagentStart' | 'subagentStop' | 'preCompact';
1
+ export type CursorHookEvent = 'sessionStart' | 'sessionEnd' | 'beforeSubmitPrompt' | 'preToolUse' | 'postToolUse' | 'postToolUseFailure' | 'beforeShellExecution' | 'afterShellExecution' | 'beforeMCPExecution' | 'afterMCPExecution' | 'beforeReadFile' | 'afterFileEdit' | 'stop' | 'subagentStart' | 'subagentStop' | 'preCompact';
2
2
  /**
3
- * Stdin payload Cursor delivers to a hook. Per cursor.com/docs/hooks (verified
4
- * 2026-05-12). Common fields appear on every event; per-event fields are
5
- * union-typed and accessed by the dispatcher branch that recognises them.
3
+ * Stdin payload Cursor delivers to a hook. Captured from Cursor CLI
4
+ * 2026.09.02-c22c1a3 on 2026-09-03. Common fields appear on every event;
5
+ * per-event fields are union-typed and accessed by the dispatcher branch that
6
+ * recognises them.
6
7
  */
7
8
  export interface CursorHookInput {
8
9
  session_id?: string;
@@ -22,12 +23,20 @@ export interface CursorHookInput {
22
23
  messages_to_compact?: number;
23
24
  is_first_compaction?: boolean;
24
25
  tool_name?: string;
25
- tool_input?: Record<string, unknown>;
26
- tool_output?: unknown;
26
+ tool_use_id?: string;
27
+ tool_input?: string | Record<string, unknown>;
28
+ tool_output?: string;
27
29
  command?: string;
30
+ output?: string;
31
+ content?: string;
32
+ attachments?: unknown[];
28
33
  mcp_tool?: string;
29
34
  mcp_args?: Record<string, unknown>;
30
35
  mcp_result?: unknown;
36
+ result_json?: string;
37
+ mcp_server_name?: string;
38
+ mcp_server_url?: string;
39
+ url?: string;
31
40
  file_path?: string;
32
41
  followup_count?: number;
33
42
  /** Stop-loop counter Cursor actually sends (docs say followup_count; real payloads carry loop_count). */
@@ -42,6 +51,16 @@ export interface CursorHookInput {
42
51
  git_branch?: string;
43
52
  status?: string;
44
53
  duration_ms?: number;
54
+ duration?: number;
55
+ context_tokens?: number;
56
+ context_window_size?: number;
57
+ message_count?: number;
58
+ trigger?: string;
59
+ reason?: string;
60
+ final_status?: string;
61
+ error_message?: string;
62
+ failure_type?: string;
63
+ is_interrupt?: boolean;
45
64
  }
46
65
  /**
47
66
  * Cursor accepts a small family of output shapes depending on the event.
@@ -137,9 +156,8 @@ export interface McpPrecheckResponse {
137
156
  * docs/development/cursor-channel-probe.md): `agent_message` on the gate
138
157
  * hooks (`preToolUse`, `beforeShellExecution`, `beforeMCPExecution`);
139
158
  * `additional_context` on `beforeSubmitPrompt` (undocumented by Cursor, the
140
- * only per-prompt channel) and on `postToolUse` (fixed in 3.9.16 — an
141
- * earlier Cursor bug made this a no-op; `sessionStart`'s `additional_context`
142
- * remains bugged/not model-visible); `followup_message` on `stop`
159
+ * compatibility per-prompt channel) and on `sessionStart` / `postToolUse`
160
+ * (current CLI 2026.09.02); `followup_message` on `stop`
143
161
  * (auto-submitted as the next user message); and a synchronous `deny` on the
144
162
  * gate hooks. On deny/ask, Cursor forwards `user_message` to the model and
145
163
  * drops `agent_message` — the reverse of Cursor's own docs — so
@@ -157,9 +175,8 @@ export interface InjectionsResponse {
157
175
  confirmedJitDeliveryRefs?: string[];
158
176
  discardedJitDeliveryRefs?: string[];
159
177
  /**
160
- * Server-selected rendering channel (Cursor only). 'agent_message' —
161
- * render injections through the gate hooks' model-facing agent_message
162
- * field (verified channel; additional_context is bugged in Cursor).
178
+ * Server-selected rendering channel (Cursor only). 'agent_message' renders
179
+ * injections through gate-hook agent_message fields for compatibility modes.
163
180
  * Absent or 'legacy' — historical rendering. Controlled server-side by
164
181
  * MNEMONIK_CURSOR_INJECTION_CHANNEL so a rollback needs no republish.
165
182
  */
@@ -167,8 +184,8 @@ export interface InjectionsResponse {
167
184
  /**
168
185
  * Files the server asks the hook to write to the workspace, idempotently
169
186
  * (skip-if-unchanged). Currently only the Cursor memory-tools rule file on
170
- * SessionStart. Independent of `injections` — present even when `injections`
171
- * is empty, which is the common Cursor case.
187
+ * SessionStart and UserPromptSubmit. Independent of `injections` — present
188
+ * even when `injections` is empty.
172
189
  */
173
190
  filesToWrite?: Array<{
174
191
  path: string;
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/lib/types.ts"],"names":[],"mappings":"AAyOA,MAAM,CAAC,MAAM,cAAc,GAAG,0BAA0B,CAAC"}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/lib/types.ts"],"names":[],"mappings":"AA8PA,MAAM,CAAC,MAAM,cAAc,GAAG,0BAA0B,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mnemonik/cursor-hooks",
3
- "version": "0.7.65",
3
+ "version": "0.7.68",
4
4
  "description": "Cursor IDE hooks for Mnemonik memory awareness and host-side grounding gates. Hooks only: does not install skills, rules, AGENTS.md, .mnemonik.json, or MCP config.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -33,7 +33,7 @@
33
33
  ],
34
34
  "license": "SEE LICENSE IN LICENSE",
35
35
  "dependencies": {
36
- "@mnemonik/shared": "7.78.1"
36
+ "@mnemonik/shared": "7.80.0"
37
37
  },
38
38
  "devDependencies": {
39
39
  "@types/node": "^20.19.43",