@pushary/agent-hooks 0.87.3 → 0.88.0

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 (59) hide show
  1. package/data/cursor-plugin/scripts/pushary-gate.mjs +9 -0
  2. package/data/cursor-plugin/scripts/pushary-gate.test.mjs +39 -0
  3. package/dist/bin/pushary-bell-hook.js +4 -4
  4. package/dist/bin/pushary-bell.js +5 -5
  5. package/dist/bin/pushary-claude.js +8 -8
  6. package/dist/bin/pushary-clean.js +9 -8
  7. package/dist/bin/pushary-codex-bridge.js +5 -5
  8. package/dist/bin/pushary-codex-hook.js +15 -9
  9. package/dist/bin/pushary-codex.js +4 -4
  10. package/dist/bin/pushary-connect.js +6 -6
  11. package/dist/bin/pushary-daemon.js +4 -4
  12. package/dist/bin/pushary-disconnect.js +5 -5
  13. package/dist/bin/pushary-doctor.js +15 -14
  14. package/dist/bin/pushary-elicitation-hook.js +1 -1
  15. package/dist/bin/pushary-gemini-bridge.js +5 -5
  16. package/dist/bin/pushary-gemini-hook.js +15 -9
  17. package/dist/bin/pushary-hook.js +6 -6
  18. package/dist/bin/pushary-login.js +4 -4
  19. package/dist/bin/pushary-logout.js +4 -4
  20. package/dist/bin/pushary-mode.js +3 -3
  21. package/dist/bin/pushary-notification-hook.js +4 -4
  22. package/dist/bin/pushary-opencode-hook.js +5 -5
  23. package/dist/bin/pushary-permission-denied-hook.js +6 -6
  24. package/dist/bin/pushary-permission-hook.js +6 -6
  25. package/dist/bin/pushary-post-hook.js +4 -4
  26. package/dist/bin/pushary-prompt-hook.js +4 -4
  27. package/dist/bin/pushary-session-end-hook.js +4 -4
  28. package/dist/bin/pushary-session-start-hook.js +4 -4
  29. package/dist/bin/pushary-setup.js +16 -16
  30. package/dist/bin/pushary-stats.js +2 -2
  31. package/dist/bin/pushary-status.js +5 -5
  32. package/dist/bin/pushary-stop-hook.js +4 -4
  33. package/dist/bin/pushary-stopfailure-hook.js +4 -4
  34. package/dist/bin/pushary-upgrade.js +5 -5
  35. package/dist/bin/pushary-wait.js +3 -3
  36. package/dist/{chunk-LGVHYFIB.js → chunk-3B3PYTKH.js} +1 -1
  37. package/dist/{chunk-DMW54VWK.js → chunk-3JL64GVE.js} +93 -3
  38. package/dist/{chunk-TD3M232T.js → chunk-3PSEZOC4.js} +1 -1
  39. package/dist/{chunk-VIHRP25S.js → chunk-4VYELDAH.js} +1 -1
  40. package/dist/{chunk-YX6ZKCC5.js → chunk-67TNJVJW.js} +1 -1
  41. package/dist/{chunk-KFAH6KRE.js → chunk-6BOE5NJZ.js} +1 -1
  42. package/dist/{chunk-ZGC3W23N.js → chunk-DSTZ2RBU.js} +1 -1
  43. package/dist/{chunk-ICERLAE2.js → chunk-DXBUFXPL.js} +1 -1
  44. package/dist/{chunk-EJL3PJU7.js → chunk-ESARAYRI.js} +1 -1
  45. package/dist/{chunk-7J67NYK4.js → chunk-F5W3AY6L.js} +2 -1
  46. package/dist/{chunk-QLMX5LLV.js → chunk-LEYXXXRN.js} +1 -1
  47. package/dist/{chunk-VOE4SYPR.js → chunk-MNXG6IIM.js} +23 -7
  48. package/dist/{chunk-K42RXJPG.js → chunk-MY5A4EPQ.js} +1 -1
  49. package/dist/{chunk-KCAFCGGY.js → chunk-OULRSHFB.js} +1 -1
  50. package/dist/{chunk-TAYHEJFF.js → chunk-RSUBLTMP.js} +2 -2
  51. package/dist/{chunk-DPUOW6NS.js → chunk-T2XZH7D5.js} +3 -7
  52. package/dist/{chunk-2JYQAW4B.js → chunk-U6HRYNHC.js} +2 -2
  53. package/dist/{chunk-OMA2OZRE.js → chunk-UTQCV46U.js} +27 -62
  54. package/dist/{chunk-VL5ZNK6V.js → chunk-VHHVGVFQ.js} +12 -2
  55. package/dist/{chunk-KBS53WOE.js → chunk-VSCSZ2LI.js} +1 -1
  56. package/dist/{chunk-YYTLTEGU.js → chunk-WXCHB6BO.js} +1 -1
  57. package/dist/{chunk-TV3UFGT7.js → chunk-XFHMGVMX.js} +1 -1
  58. package/dist/src/index.js +6 -6
  59. package/package.json +1 -1
@@ -5,7 +5,7 @@ import {
5
5
  import {
6
6
  isGatingMoment,
7
7
  recordKeylessMoment
8
- } from "./chunk-VIHRP25S.js";
8
+ } from "./chunk-4VYELDAH.js";
9
9
  import {
10
10
  denyReasonFrom,
11
11
  isDeferAnswer
@@ -17,7 +17,7 @@ import {
17
17
  readLastUserPrompt,
18
18
  repoKeyFor,
19
19
  throttlePass
20
- } from "./chunk-VOE4SYPR.js";
20
+ } from "./chunk-MNXG6IIM.js";
21
21
  import {
22
22
  DEFAULT_SESSION,
23
23
  askUser,
@@ -33,7 +33,7 @@ import {
33
33
  deriveToolTarget,
34
34
  describeToolCall,
35
35
  scopePathFor
36
- } from "./chunk-7J67NYK4.js";
36
+ } from "./chunk-F5W3AY6L.js";
37
37
  import {
38
38
  getMachineId
39
39
  } from "./chunk-RN3NOEJF.js";
@@ -49,8 +49,10 @@ import {
49
49
  hookWaitClamped,
50
50
  hookWaitDeadline,
51
51
  parseAgentQuestionAnswers,
52
- resolveGate
53
- } from "./chunk-DMW54VWK.js";
52
+ parseAgentQuestions,
53
+ resolveGate,
54
+ shouldDeferToNativeMode
55
+ } from "./chunk-3JL64GVE.js";
54
56
 
55
57
  // src/decision-episode.ts
56
58
  var EPISODE_PATH = "/api/agent/decision-episode";
@@ -107,10 +109,10 @@ var handlePushOnly = async (apiKey, description, projectName, timeoutSeconds, ti
107
109
  return void 0;
108
110
  }
109
111
  }
110
- if (result.suppressed) {
112
+ if (result.suppressed || result.status === "terminal") {
111
113
  await cancelQuestion(apiKey, result.correlationId).catch(() => {
112
114
  });
113
- return ask("You are at the keyboard, approve here.");
115
+ return ask(result.suppressed ? "You are at the keyboard, approve here." : "Delivery mode is Terminal, approve here.");
114
116
  }
115
117
  if (result.noDevices) {
116
118
  switch (timeoutAction) {
@@ -300,58 +302,21 @@ var keylessNoticeOnce = (sessionId) => {
300
302
  } catch {
301
303
  }
302
304
  };
303
- var FULL_DEFER_MODES = /* @__PURE__ */ new Set(["plan", "auto", "dontAsk"]);
304
- var ACCEPT_EDITS_DEFER_TOOLS = /* @__PURE__ */ new Set(["Write", "Edit", "MultiEdit", "NotebookEdit"]);
305
- var nativeModeGatingDisabled = () => {
306
- const flag = process.env.PUSHARY_RESPECT_PERMISSION_MODE;
307
- return flag === "0" || flag === "false";
308
- };
309
- var shouldDeferToNativeMode = (permissionMode, toolName) => {
310
- if (nativeModeGatingDisabled()) return false;
311
- if (typeof permissionMode !== "string") return false;
312
- if (FULL_DEFER_MODES.has(permissionMode)) return true;
313
- if (permissionMode === "acceptEdits" && ACCEPT_EDITS_DEFER_TOOLS.has(toolName)) return true;
314
- if (permissionMode === "bypassPermissions") {
315
- const flag = process.env.PUSHARY_DEFER_BYPASS;
316
- return flag === "1" || flag === "true";
317
- }
318
- return false;
319
- };
320
- var MAX_PHONE_QUESTIONS = 4;
321
- var parseQuestion = (raw) => {
322
- const q = raw;
323
- if (!q || typeof q.question !== "string") return void 0;
324
- const options = Array.isArray(q.options) ? q.options.flatMap((option) => {
325
- if (!option || typeof option !== "object") return [];
326
- const value = option;
327
- if (typeof value.label !== "string" || value.label.length === 0) return [];
328
- return [{
329
- label: value.label,
330
- ...typeof value.description === "string" ? { description: value.description } : {}
331
- }];
332
- }) : [];
333
- const labels = options.map((option) => option.label);
334
- if (labels.length < 2) return void 0;
305
+ var nativeDeferOptions = () => {
306
+ const respect = process.env.PUSHARY_RESPECT_PERMISSION_MODE;
307
+ const bypass = process.env.PUSHARY_DEFER_BYPASS;
335
308
  return {
336
- question: q.question,
337
- header: typeof q.header === "string" ? q.header : void 0,
338
- multiSelect: q.multiSelect === true,
339
- options,
340
- labels
309
+ respectPermissionMode: !(respect === "0" || respect === "false"),
310
+ deferBypass: bypass === "1" || bypass === "true"
341
311
  };
342
312
  };
343
- var parseQuestions = (toolInput) => {
344
- const questions = toolInput.questions;
345
- if (!Array.isArray(questions) || questions.length === 0) return void 0;
346
- if (questions.length > MAX_PHONE_QUESTIONS) return void 0;
347
- const parsed = [];
348
- for (const raw of questions) {
349
- const one = parseQuestion(raw);
350
- if (!one || parsed.some((question) => question.question === one.question)) return void 0;
351
- parsed.push(one);
352
- }
353
- return parsed;
354
- };
313
+ var shouldDeferToNativeMode2 = (permissionMode, toolName) => shouldDeferToNativeMode(permissionMode, toolName, nativeDeferOptions());
314
+ var handedToTerminal = (result) => result.suppressed === true || result.noDevices === true || result.status === "terminal";
315
+ var withLabels = (question) => ({
316
+ ...question,
317
+ labels: question.options.map((option) => option.label)
318
+ });
319
+ var parseQuestions = (toolInput) => parseAgentQuestions(toolInput)?.map(withLabels);
355
320
  var questionContext = (question, index, total, project) => {
356
321
  const subject = question.header ? `${question.header}: your agent is asking in ${project}` : `Your agent is asking in ${project}`;
357
322
  return total > 1 ? `${subject} (${index + 1} of ${total})` : subject;
@@ -376,7 +341,7 @@ var askAsOneCard = async (apiKey, input, parsed, projectName, deadline) => {
376
341
  } catch {
377
342
  return void 0;
378
343
  }
379
- if (result.suppressed || result.noDevices) {
344
+ if (handedToTerminal(result)) {
380
345
  await cancelQuestion(apiKey, result.correlationId).catch(() => {
381
346
  });
382
347
  return void 0;
@@ -411,7 +376,7 @@ var askQuestionByQuestion = async (apiKey, input, parsed, projectName, deadline)
411
376
  } catch {
412
377
  return void 0;
413
378
  }
414
- if (result.suppressed || result.noDevices) {
379
+ if (handedToTerminal(result)) {
415
380
  await cancelQuestion(apiKey, result.correlationId).catch(() => {
416
381
  });
417
382
  return void 0;
@@ -461,7 +426,7 @@ var handleExitPlanMode = async (apiKey, input) => {
461
426
  } catch {
462
427
  return void 0;
463
428
  }
464
- if (result.suppressed || result.noDevices) {
429
+ if (handedToTerminal(result)) {
465
430
  await cancelQuestion(apiKey, result.correlationId).catch(() => {
466
431
  });
467
432
  return void 0;
@@ -524,7 +489,7 @@ var handlePreToolUse = async (input) => {
524
489
  if (input.tool_name === "ExitPlanMode") {
525
490
  return handleExitPlanMode(apiKey, input);
526
491
  }
527
- if (shouldDeferToNativeMode(input.permission_mode, input.tool_name)) {
492
+ if (shouldDeferToNativeMode2(input.permission_mode, input.tool_name)) {
528
493
  return void 0;
529
494
  }
530
495
  switch (verdict.kind) {
@@ -555,7 +520,7 @@ var toPermissionRequestOutput = (hookOutput) => {
555
520
  };
556
521
  var handlePermissionRequest = async (input) => {
557
522
  if (PRETOOLUSE_HANDLED_TOOLS.has(input.tool_name)) {
558
- const reclaimDeferredExec = !PRETOOLUSE_PHONE_ANSWERED_TOOLS.includes(input.tool_name) && shouldDeferToNativeMode(input.permission_mode, input.tool_name);
523
+ const reclaimDeferredExec = !PRETOOLUSE_PHONE_ANSWERED_TOOLS.includes(input.tool_name) && shouldDeferToNativeMode2(input.permission_mode, input.tool_name);
559
524
  if (!reclaimDeferredExec) return void 0;
560
525
  }
561
526
  if (input.tool_name.startsWith("mcp__pushary__")) return void 0;
@@ -636,7 +601,7 @@ var handlePermissionDenied = async (input) => {
636
601
  } catch {
637
602
  return void 0;
638
603
  }
639
- if (result.suppressed || result.noDevices) {
604
+ if (handedToTerminal(result)) {
640
605
  await cancelQuestion(apiKey, result.correlationId).catch(() => {
641
606
  });
642
607
  return void 0;
@@ -4,7 +4,7 @@ import {
4
4
  import {
5
5
  HOOK_BUDGETS,
6
6
  HOOK_LOCATOR_BINARY
7
- } from "./chunk-DMW54VWK.js";
7
+ } from "./chunk-3JL64GVE.js";
8
8
 
9
9
  // src/codex-config.ts
10
10
  import { createHash } from "crypto";
@@ -228,10 +228,20 @@ var addCodexHookTrust = (config, hooksJsonPath, command, positions) => {
228
228
  const position = positions[definition.event];
229
229
  if (!position) continue;
230
230
  const key = codexHookStateKey(hooksJsonPath, definition.event, position);
231
+ const ours = codexHookTrustHash(definition, command);
231
232
  const existing = asRecord(state[key]) ?? {};
232
- state[key] = { ...existing, trusted_hash: codexHookTrustHash(definition, command) };
233
+ state[key] = { ...existing, trusted_hash: ours };
234
+ for (const stale of staleTrustKeys(state, hooksJsonPath, definition.event, key, ours)) {
235
+ delete state[stale];
236
+ }
233
237
  }
234
238
  };
239
+ var staleTrustKeys = (state, hooksJsonPath, event, currentKey, ourHash) => {
240
+ const prefix = `${hooksJsonPath}:${CODEX_EVENT_KEY[event]}:`;
241
+ return Object.keys(state).filter(
242
+ (key) => key !== currentKey && key.startsWith(prefix) && asRecord(state[key])?.trusted_hash === ourHash
243
+ );
244
+ };
235
245
 
236
246
  // src/setup/paths.ts
237
247
  import { homedir as homedir2 } from "os";
@@ -3,7 +3,7 @@ import {
3
3
  } from "./chunk-2UMNXADU.js";
4
4
  import {
5
5
  normalizeInstallSource
6
- } from "./chunk-DMW54VWK.js";
6
+ } from "./chunk-3JL64GVE.js";
7
7
 
8
8
  // src/install-source.ts
9
9
  import { readFileSync } from "fs";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  readConfigFileKey
3
- } from "./chunk-YX6ZKCC5.js";
3
+ } from "./chunk-67TNJVJW.js";
4
4
 
5
5
  // src/bell/fleet.ts
6
6
  import { closeSync, constants, futimesSync, lstatSync, mkdirSync, openSync, readdirSync, unlinkSync } from "fs";
@@ -5,7 +5,7 @@ import {
5
5
  generateSessionKey,
6
6
  uploadTranscriptRecords,
7
7
  wrapSessionKey
8
- } from "./chunk-QLMX5LLV.js";
8
+ } from "./chunk-LEYXXXRN.js";
9
9
 
10
10
  // src/live-session.ts
11
11
  var MAX_TRANSCRIPT_QUEUE = 100;
package/dist/src/index.js CHANGED
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  handlePreToolUse
3
- } from "../chunk-OMA2OZRE.js";
3
+ } from "../chunk-UTQCV46U.js";
4
4
  import "../chunk-ATBKJNWS.js";
5
- import "../chunk-VIHRP25S.js";
5
+ import "../chunk-4VYELDAH.js";
6
6
  import "../chunk-YHG74UFF.js";
7
7
  import {
8
8
  fetchModeOverride,
@@ -12,17 +12,17 @@ import {
12
12
  handlePostToolUse,
13
13
  handleStop,
14
14
  reportEvent
15
- } from "../chunk-VOE4SYPR.js";
15
+ } from "../chunk-MNXG6IIM.js";
16
16
  import {
17
17
  askUser,
18
18
  cancelQuestion,
19
19
  waitForAnswer
20
20
  } from "../chunk-J3YDT4HM.js";
21
- import "../chunk-7J67NYK4.js";
21
+ import "../chunk-F5W3AY6L.js";
22
22
  import "../chunk-RN3NOEJF.js";
23
23
  import "../chunk-BSZYIAZL.js";
24
24
  import "../chunk-SAF6HGAA.js";
25
- import "../chunk-KBS53WOE.js";
25
+ import "../chunk-VSCSZ2LI.js";
26
26
  import "../chunk-7QLSKOSU.js";
27
27
  import "../chunk-KZERVKTD.js";
28
28
  import {
@@ -31,7 +31,7 @@ import {
31
31
  } from "../chunk-2UMNXADU.js";
32
32
  import {
33
33
  resolvePolicy
34
- } from "../chunk-DMW54VWK.js";
34
+ } from "../chunk-3JL64GVE.js";
35
35
  export {
36
36
  askUser,
37
37
  cancelQuestion,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pushary/agent-hooks",
3
- "version": "0.87.3",
3
+ "version": "0.88.0",
4
4
  "description": "Permission hooks for AI coding agents: route tool approvals through Pushary push notifications",
5
5
  "keywords": [
6
6
  "pushary",