@pushary/agent-hooks 0.85.1 → 0.86.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 (56) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/dist/bin/pushary-bell-hook.js +4 -4
  3. package/dist/bin/pushary-bell.js +5 -5
  4. package/dist/bin/pushary-claude.js +7 -7
  5. package/dist/bin/pushary-clean.js +9 -8
  6. package/dist/bin/pushary-codex-bridge.js +5 -5
  7. package/dist/bin/pushary-codex-hook.js +4 -4
  8. package/dist/bin/pushary-codex.js +3 -3
  9. package/dist/bin/pushary-connect.js +6 -6
  10. package/dist/bin/pushary-daemon.js +4 -4
  11. package/dist/bin/pushary-disconnect.js +7 -6
  12. package/dist/bin/pushary-doctor.js +11 -11
  13. package/dist/bin/pushary-gemini-bridge.js +5 -5
  14. package/dist/bin/pushary-gemini-hook.js +12 -7
  15. package/dist/bin/pushary-hook.js +6 -8
  16. package/dist/bin/pushary-login.js +4 -4
  17. package/dist/bin/pushary-logout.js +4 -4
  18. package/dist/bin/pushary-mode.js +3 -3
  19. package/dist/bin/pushary-notification-hook.js +3 -3
  20. package/dist/bin/pushary-permission-denied-hook.js +6 -8
  21. package/dist/bin/pushary-permission-hook.js +6 -8
  22. package/dist/bin/pushary-post-hook.js +3 -3
  23. package/dist/bin/pushary-prompt-hook.js +3 -3
  24. package/dist/bin/pushary-session-end-hook.js +3 -3
  25. package/dist/bin/pushary-session-start-hook.js +3 -3
  26. package/dist/bin/pushary-setup.js +20 -19
  27. package/dist/bin/pushary-stats.js +2 -2
  28. package/dist/bin/pushary-status.js +5 -5
  29. package/dist/bin/pushary-stop-hook.js +3 -3
  30. package/dist/bin/pushary-stopfailure-hook.js +3 -3
  31. package/dist/bin/pushary-upgrade.js +10 -9
  32. package/dist/bin/pushary-wait.js +3 -3
  33. package/dist/{chunk-EZPRY2ZY.js → chunk-2255DZ74.js} +1 -1
  34. package/dist/{chunk-OZKRF7MS.js → chunk-2C6B6V2U.js} +7 -3
  35. package/dist/{chunk-QDKJTPAT.js → chunk-2TZCZ7CL.js} +1 -1
  36. package/dist/{chunk-Y7BALRVB.js → chunk-33T5OOKP.js} +19 -34
  37. package/dist/{chunk-KTKIZEFT.js → chunk-5EUZYWID.js} +1 -1
  38. package/dist/{chunk-ES3FAX5X.js → chunk-7C5CJC3H.js} +1 -1
  39. package/dist/{chunk-CIVG746V.js → chunk-7G5VO76N.js} +1 -1
  40. package/dist/chunk-ATBKJNWS.js +19 -0
  41. package/dist/{chunk-G3LXA2ZP.js → chunk-CY4TZK5O.js} +59 -1
  42. package/dist/{chunk-XIR6N66V.js → chunk-FOY5FISQ.js} +1 -1
  43. package/dist/{chunk-GM54KC62.js → chunk-GK4CED6Z.js} +12 -18
  44. package/dist/{chunk-VGADRFG6.js → chunk-MNQI6ZZU.js} +1 -1
  45. package/dist/{chunk-XLM6COJA.js → chunk-MTKR4YLW.js} +1 -1
  46. package/dist/{chunk-DNSIVHIA.js → chunk-N52CIK47.js} +1 -1
  47. package/dist/{chunk-BZKTCOZQ.js → chunk-PZR4XMFU.js} +1 -1
  48. package/dist/{chunk-VKJUVPTJ.js → chunk-Q7N3XGWM.js} +2 -2
  49. package/dist/{chunk-IGV4KL7B.js → chunk-QW27ZFA4.js} +2 -2
  50. package/dist/{chunk-LB6RSCLL.js → chunk-S3UD7O35.js} +59 -45
  51. package/dist/{chunk-PHXEADSL.js → chunk-VPJAAGD4.js} +7 -3
  52. package/dist/{chunk-55656H4Q.js → chunk-VZNDR3Z7.js} +1 -1
  53. package/dist/{chunk-JDQY47OW.js → chunk-XPAGN3SC.js} +1 -1
  54. package/dist/src/index.d.ts +3 -10
  55. package/dist/src/index.js +6 -8
  56. package/package.json +1 -1
@@ -65,6 +65,7 @@ var hookWaitDeadline = (startMs, policyWaitSeconds, agent, nowMs) => {
65
65
  };
66
66
  var hookWaitClamped = (startMs, policyWaitSeconds, agent, nowMs) => startMs + hookMaxWaitSeconds(agent) * 1e3 < nowMs + Math.max(policyWaitSeconds, 0) * 1e3;
67
67
  var effectiveWaitSeconds = (timeoutAction, policySeconds, agent) => timeoutAction === "wait" ? hookMaxWaitSeconds(agent) : policySeconds;
68
+ var HOOK_LOCATOR_BINARY = "pushary-bridge";
68
69
  var HOOK_BATCH_MAX_BYTES = 4 * 1024 * 1024;
69
70
  var REPO_KEY_MAX_LENGTH = 200;
70
71
  var isRepoKeyMatch = (ruleRepoKey, currentRepoKey) => {
@@ -502,6 +503,8 @@ var selectGoverningRule = (policies, toolName, args, repoKey, sessionId) => {
502
503
  }
503
504
  return best;
504
505
  };
506
+ var ARG_PREFIX_TARGET_TOOLS = Object.keys(POLICY_COMMAND_KEYS);
507
+ var FILE_TARGET_TOOLS = /* @__PURE__ */ new Set(["Edit", "Write", "MultiEdit"]);
505
508
  var API_KEY_PATTERN = /^pk_[a-f0-9]+\.[a-f0-9]+$/;
506
509
  var isValidApiKey = (value) => API_KEY_PATTERN.test(value);
507
510
  var ACTION_BODY_MAX = 4e3;
@@ -1035,6 +1038,56 @@ var modeStateFromResponse = (data) => {
1035
1038
  degraded: false
1036
1039
  };
1037
1040
  };
1041
+ var claudePreToolUseOutput = (decision, reason, updatedInput) => ({
1042
+ hookSpecificOutput: {
1043
+ hookEventName: "PreToolUse",
1044
+ permissionDecision: decision,
1045
+ ...reason ? { permissionDecisionReason: reason } : {},
1046
+ ...updatedInput ? { updatedInput } : {}
1047
+ }
1048
+ });
1049
+ var claudePermissionRequestOutput = (decision, reason, updatedInput) => ({
1050
+ hookSpecificOutput: {
1051
+ hookEventName: "PermissionRequest",
1052
+ decision: decision === "allow" ? { behavior: "allow", ...updatedInput ? { updatedInput } : {} } : { behavior: "deny", ...reason ? { message: reason } : {} }
1053
+ }
1054
+ });
1055
+ var gateOutputFor = (source, event, decision, reason) => {
1056
+ let output;
1057
+ if (source === "claude") {
1058
+ if (event === "PreToolUse") {
1059
+ output = claudePreToolUseOutput(decision, reason);
1060
+ } else if (event === "PermissionRequest" && decision !== "ask") {
1061
+ output = claudePermissionRequestOutput(decision, reason);
1062
+ }
1063
+ } else if (source === "codex") {
1064
+ if (event === "PermissionRequest" && decision !== "ask") {
1065
+ output = {
1066
+ hookSpecificOutput: {
1067
+ hookEventName: "PermissionRequest",
1068
+ decision: decision === "allow" ? { behavior: "allow" } : { behavior: "deny", message: reason ?? "Denied from Pushary" }
1069
+ }
1070
+ };
1071
+ } else if (event === "PreToolUse" && decision === "deny") {
1072
+ output = {
1073
+ hookSpecificOutput: {
1074
+ hookEventName: "PreToolUse",
1075
+ permissionDecision: "deny",
1076
+ permissionDecisionReason: reason ?? "Denied from Pushary"
1077
+ }
1078
+ };
1079
+ }
1080
+ } else if (source === "gemini" && event === "BeforeTool" && decision !== "ask") {
1081
+ output = decision === "allow" ? { decision: "allow" } : { decision: "deny", reason: reason ?? "Denied from Pushary" };
1082
+ } else if (source === "cursor" && event === "beforeShellExecution") {
1083
+ output = decision === "allow" ? { permission: "allow" } : decision === "ask" ? { permission: "ask" } : {
1084
+ permission: "deny",
1085
+ user_message: "Command denied via Pushary.",
1086
+ agent_message: reason ?? "Denied from Pushary"
1087
+ };
1088
+ }
1089
+ return output;
1090
+ };
1038
1091
 
1039
1092
  export {
1040
1093
  parseAgentQuestionAnswers,
@@ -1043,10 +1096,12 @@ export {
1043
1096
  hookWaitDeadline,
1044
1097
  hookWaitClamped,
1045
1098
  effectiveWaitSeconds,
1099
+ HOOK_LOCATOR_BINARY,
1046
1100
  normalizeRepoRemote,
1047
1101
  localRepoKey,
1048
1102
  isSafeReadOnlyCommand,
1049
1103
  isAutoApprove,
1104
+ FILE_TARGET_TOOLS,
1050
1105
  isValidApiKey,
1051
1106
  ACTION_BODY_MAX,
1052
1107
  DECISION_LINE_MAX,
@@ -1057,5 +1112,8 @@ export {
1057
1112
  resolvePolicy,
1058
1113
  KILL_REASON,
1059
1114
  resolveGate,
1060
- modeStateFromResponse
1115
+ modeStateFromResponse,
1116
+ claudePreToolUseOutput,
1117
+ claudePermissionRequestOutput,
1118
+ gateOutputFor
1061
1119
  };
@@ -5,7 +5,7 @@ import {
5
5
  generateSessionKey,
6
6
  uploadTranscriptRecords,
7
7
  wrapSessionKey
8
- } from "./chunk-JDQY47OW.js";
8
+ } from "./chunk-XPAGN3SC.js";
9
9
 
10
10
  // src/live-session.ts
11
11
  var MAX_TRANSCRIPT_QUEUE = 100;
@@ -1,9 +1,14 @@
1
+ import {
2
+ PRETOOLUSE_GATED_TOOLS,
3
+ PRETOOLUSE_PHONE_ANSWERED_TOOLS
4
+ } from "./chunk-ATBKJNWS.js";
1
5
  import {
2
6
  guardBinary
3
7
  } from "./chunk-HSGEEBBL.js";
4
8
  import {
5
- HOOK_BUDGETS
6
- } from "./chunk-G3LXA2ZP.js";
9
+ HOOK_BUDGETS,
10
+ HOOK_LOCATOR_BINARY
11
+ } from "./chunk-CY4TZK5O.js";
7
12
 
8
13
  // src/claude-config.ts
9
14
  import { join } from "path";
@@ -21,21 +26,8 @@ var compareVersion = (a, b) => {
21
26
  };
22
27
  var atLeast = (version, floor) => compareVersion(version, floor) >= 0;
23
28
 
24
- // src/claude-tools.ts
25
- var PRETOOLUSE_GATED_TOOLS = [
26
- "Bash",
27
- "Write",
28
- "Edit",
29
- "MultiEdit",
30
- "NotebookEdit",
31
- "PowerShell",
32
- "Monitor"
33
- ];
34
- var POSTTOOLUSE_MIRRORED_TOOLS = [...PRETOOLUSE_GATED_TOOLS, "TodoWrite"];
35
- var PRETOOLUSE_HANDLED_TOOLS = /* @__PURE__ */ new Set([...PRETOOLUSE_GATED_TOOLS, "AskUserQuestion"]);
36
-
37
29
  // src/claude-events.ts
38
- var GATED_TOOLS = [...PRETOOLUSE_GATED_TOOLS, "AskUserQuestion"].join("|");
30
+ var GATED_TOOLS = [...PRETOOLUSE_GATED_TOOLS, ...PRETOOLUSE_PHONE_ANSWERED_TOOLS].join("|");
39
31
  var MIRRORED_TOOLS = [...PRETOOLUSE_GATED_TOOLS, "TodoWrite"].join("|");
40
32
  var ESTABLISHED = [1, 0, 0];
41
33
  var OBSERVED = [2, 0, 0];
@@ -72,7 +64,10 @@ var ensureRecord = (target, key) => {
72
64
  return created;
73
65
  };
74
66
  var isPusharyPermission = (rule) => typeof rule === "string" && (rule.includes("pushary") || rule.includes("MCP(pushary"));
75
- var HOOK_BINARIES = new Set(CLAUDE_HOOK_EVENTS.map((hook) => hook.binary));
67
+ var HOOK_BINARIES = /* @__PURE__ */ new Set([
68
+ ...CLAUDE_HOOK_EVENTS.map((hook) => hook.binary),
69
+ HOOK_LOCATOR_BINARY
70
+ ]);
76
71
  var namesPusharyBinary = (command) => command.split(/[\s/\\"']+/).some((segment) => HOOK_BINARIES.has(segment.replace(/\.(cmd|exe)$/i, "")));
77
72
  var isPusharyHook = (entry2) => {
78
73
  const hooks = asRecord(entry2)?.hooks;
@@ -178,7 +173,6 @@ var removePusharySettings = (settings) => {
178
173
  export {
179
174
  parseVersion,
180
175
  compareVersion,
181
- PRETOOLUSE_HANDLED_TOOLS,
182
176
  addClaudeMcpServer,
183
177
  removeClaudeMcpServers,
184
178
  addPusharyToolPermissions,
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  isSafeReadOnlyCommand
3
- } from "./chunk-G3LXA2ZP.js";
3
+ } from "./chunk-CY4TZK5O.js";
4
4
 
5
5
  // src/ledger.ts
6
6
  import { appendFileSync, existsSync, mkdirSync, readFileSync, renameSync, rmSync, statSync } from "fs";
@@ -7,7 +7,7 @@ import {
7
7
  } from "./chunk-VT4IVERX.js";
8
8
  import {
9
9
  pusharyDir
10
- } from "./chunk-PHXEADSL.js";
10
+ } from "./chunk-VPJAAGD4.js";
11
11
  import {
12
12
  getMachineId
13
13
  } from "./chunk-RN3NOEJF.js";
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  describeKeyCheck,
3
3
  keyCheckFromResponse
4
- } from "./chunk-QDKJTPAT.js";
4
+ } from "./chunk-2TZCZ7CL.js";
5
5
  import {
6
6
  createIo
7
7
  } from "./chunk-5RUIFDTP.js";
@@ -11,7 +11,7 @@ import {
11
11
  } from "./chunk-BSZYIAZL.js";
12
12
  import {
13
13
  reportedInstallSource
14
- } from "./chunk-ES3FAX5X.js";
14
+ } from "./chunk-7C5CJC3H.js";
15
15
 
16
16
  // src/reach.ts
17
17
  var reachVerdict = (channels) => {
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  hasGeminiHooks
3
- } from "./chunk-OZKRF7MS.js";
3
+ } from "./chunk-2C6B6V2U.js";
4
4
  import {
5
5
  hasCodexHooks
6
- } from "./chunk-PHXEADSL.js";
6
+ } from "./chunk-VPJAAGD4.js";
7
7
 
8
8
  // src/diagnostics/wiring.ts
9
9
  var record = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : null;
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  codexHomeFrom
3
- } from "./chunk-PHXEADSL.js";
3
+ } from "./chunk-VPJAAGD4.js";
4
4
 
5
5
  // src/vscode-config.ts
6
6
  import { homedir } from "os";
@@ -253,7 +253,7 @@ You have Pushary MCP tools that reach the user on their phone. Use them proactiv
253
253
 
254
254
  - When you need the user to make a decision or answer a clarifying question, call \`ask_user\` instead of guessing or stalling. Use type "confirm" for yes/no, "select" for a fixed set of options, and "input" for free text. \`ask_user\` blocks until the user replies or it times out, so you do not need a separate wait step.
255
255
  - Before any risky or irreversible step (deleting files, force pushing, spending money, sending external messages), call \`ask_user\` with type "confirm" and wait for approval.
256
- - Before starting a multi-step run that will change several files, call \`propose_scope\` once with the paths you expect to change, anything you promise not to touch, and what done means. The user agrees to the boundary in one tap, and after that only work outside it needs approving. Skip it for a single quick edit, and do not propose a new scope mid-run just to widen one: if you need to go outside the agreed scope, do the work and let the approval that follows widen it.
256
+ - Before starting a multi-step run that will change several files, call \`propose_scope\` once with the paths you expect to change, anything you promise not to touch, and what done means. The user agrees to the boundary in one tap. A ratified boundary NARROWS what may proceed without asking, it does not pre-approve work inside it, so an action that would normally need approval still needs it. Skip it for a single quick edit, and do not propose a new scope mid-run just to widen one: if you need to go outside the agreed scope, do the work and let the approval that follows widen it.
257
257
  - When you are blocked, stuck, or hit an error you cannot resolve on your own, call \`send_notification\` so the user knows, and call \`ask_user\` if you need a decision to continue.
258
258
  - When a task that took several steps finishes, call \`send_notification\` so the user knows it is done. Always pass \`context\` with a \`type\` of "task_complete", "error" or "info": it is what tells Pushary this is a task update, so the user's own setting for where updates land can route it.
259
259
  - Use \`cancel_question\` to retract a pending question once it is no longer needed.
@@ -1,10 +1,11 @@
1
1
  import {
2
- PRETOOLUSE_HANDLED_TOOLS
3
- } from "./chunk-GM54KC62.js";
2
+ PRETOOLUSE_HANDLED_TOOLS,
3
+ PRETOOLUSE_PHONE_ANSWERED_TOOLS
4
+ } from "./chunk-ATBKJNWS.js";
4
5
  import {
5
6
  isGatingMoment,
6
7
  recordKeylessMoment
7
- } from "./chunk-VGADRFG6.js";
8
+ } from "./chunk-MNQI6ZZU.js";
8
9
  import {
9
10
  denyReasonFrom,
10
11
  isDeferAnswer
@@ -28,7 +29,7 @@ import {
28
29
  sendNotification,
29
30
  throttlePass,
30
31
  waitForAnswer
31
- } from "./chunk-Y7BALRVB.js";
32
+ } from "./chunk-33T5OOKP.js";
32
33
  import {
33
34
  getMachineId
34
35
  } from "./chunk-RN3NOEJF.js";
@@ -38,12 +39,14 @@ import {
38
39
  } from "./chunk-2UMNXADU.js";
39
40
  import {
40
41
  buildDecisionEpisodeFeatures,
42
+ claudePermissionRequestOutput,
43
+ claudePreToolUseOutput,
41
44
  effectiveWaitSeconds,
42
45
  hookWaitClamped,
43
46
  hookWaitDeadline,
44
47
  parseAgentQuestionAnswers,
45
48
  resolveGate
46
- } from "./chunk-G3LXA2ZP.js";
49
+ } from "./chunk-CY4TZK5O.js";
47
50
 
48
51
  // src/decision-episode.ts
49
52
  var EPISODE_PATH = "/api/agent/decision-episode";
@@ -73,33 +76,10 @@ import { tmpdir } from "os";
73
76
  import { existsSync, writeFileSync } from "fs";
74
77
  var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
75
78
  var START_MS = Date.now();
76
- var allow = () => ({
77
- hookSpecificOutput: {
78
- hookEventName: "PreToolUse",
79
- permissionDecision: "allow"
80
- }
81
- });
82
- var deny = (reason) => ({
83
- hookSpecificOutput: {
84
- hookEventName: "PreToolUse",
85
- permissionDecision: "deny",
86
- permissionDecisionReason: reason
87
- }
88
- });
89
- var allowWithInput = (updatedInput) => ({
90
- hookSpecificOutput: {
91
- hookEventName: "PreToolUse",
92
- permissionDecision: "allow",
93
- updatedInput
94
- }
95
- });
96
- var ask = (reason) => ({
97
- hookSpecificOutput: {
98
- hookEventName: "PreToolUse",
99
- permissionDecision: "ask",
100
- ...reason ? { permissionDecisionReason: reason } : {}
101
- }
102
- });
79
+ var allow = () => claudePreToolUseOutput("allow");
80
+ var deny = (reason) => claudePreToolUseOutput("deny", reason);
81
+ var allowWithInput = (updatedInput) => claudePreToolUseOutput("allow", void 0, updatedInput);
82
+ var ask = (reason) => claudePreToolUseOutput("ask", reason);
103
83
  var pollForAnswer = async (apiKey, correlationId, deadlineMs, pollInterval = 2e3) => {
104
84
  while (Date.now() < deadlineMs) {
105
85
  const remaining = Math.min(Math.max(deadlineMs - Date.now(), 1e3), 3e4);
@@ -469,6 +449,47 @@ var handleAskUserQuestion = async (apiKey, input) => {
469
449
  if (!answers) return void 0;
470
450
  return allowWithInput({ ...input.tool_input ?? {}, answers });
471
451
  };
452
+ var PLAN_APPROVE = "Approve, start coding";
453
+ var PLAN_KEEP_PLANNING = "Keep planning";
454
+ var handleExitPlanMode = async (apiKey, input) => {
455
+ const toolInput = input.tool_input ?? {};
456
+ const projectName = basename(input.cwd ?? process.cwd());
457
+ let result;
458
+ try {
459
+ result = await askUser(apiKey, {
460
+ question: `Approve the plan and start coding in ${projectName}?`,
461
+ type: "select",
462
+ options: [PLAN_APPROVE, PLAN_KEEP_PLANNING],
463
+ context: `Your agent finished planning in ${projectName} and is waiting to start.`,
464
+ agentName: `Claude Code - ${projectName}`,
465
+ sessionId: input.session_id,
466
+ machineId: getMachineId(),
467
+ toolName: "ExitPlanMode",
468
+ action: deriveAction("ExitPlanMode", toolInput),
469
+ // The plan markdown itself, through the same redaction and cap as any diff,
470
+ // so approving from a lock screen is a real decision and not a blind yes.
471
+ actionBody: deriveActionBody("ExitPlanMode", toolInput),
472
+ intent: readLastPrompt(input.session_id ?? DEFAULT_SESSION) ?? (input.transcript_path ? readLastUserPrompt(input.transcript_path) : void 0),
473
+ blocker: "Waiting for you to approve the plan"
474
+ });
475
+ } catch {
476
+ return void 0;
477
+ }
478
+ if (result.suppressed || result.noDevices) {
479
+ await cancelQuestion(apiKey, result.correlationId).catch(() => {
480
+ });
481
+ return void 0;
482
+ }
483
+ const deadline = hookWaitDeadline(START_MS, effectiveWaitSeconds("wait", 0, "claude"), "claude", Date.now());
484
+ const answer = await pollForAnswer(apiKey, result.correlationId, deadline);
485
+ const value = answer.answered ? (answer.value ?? "").trim() : "";
486
+ if (!value || isDeferAnswer(answer.value)) {
487
+ savePendingQuestion(input.session_id || DEFAULT_SESSION, result.correlationId);
488
+ return void 0;
489
+ }
490
+ if (value === PLAN_APPROVE) return allow();
491
+ return deny("Plan declined from the phone. Ask what to change before coding.");
492
+ };
472
493
  var handleKeyless = (input) => {
473
494
  try {
474
495
  if (isGatingMoment(input.tool_name, input.tool_input ?? {})) {
@@ -514,6 +535,9 @@ var handlePreToolUse = async (input) => {
514
535
  return handleAskUserQuestion(apiKey, input);
515
536
  }
516
537
  if (verdict.kind === "deny") return deny(verdict.reason);
538
+ if (input.tool_name === "ExitPlanMode") {
539
+ return handleExitPlanMode(apiKey, input);
540
+ }
517
541
  if (shouldDeferToNativeMode(input.permission_mode, input.tool_name)) {
518
542
  return void 0;
519
543
  }
@@ -534,18 +558,8 @@ var handlePreToolUse = async (input) => {
534
558
  return void 0;
535
559
  }
536
560
  };
537
- var permReqAllow = (updatedInput) => ({
538
- hookSpecificOutput: {
539
- hookEventName: "PermissionRequest",
540
- decision: { behavior: "allow", ...updatedInput ? { updatedInput } : {} }
541
- }
542
- });
543
- var permReqDeny = (message) => ({
544
- hookSpecificOutput: {
545
- hookEventName: "PermissionRequest",
546
- decision: { behavior: "deny", ...message ? { message } : {} }
547
- }
548
- });
561
+ var permReqAllow = (updatedInput) => claudePermissionRequestOutput("allow", void 0, updatedInput);
562
+ var permReqDeny = (message) => claudePermissionRequestOutput("deny", message);
549
563
  var toPermissionRequestOutput = (hookOutput) => {
550
564
  if (!hookOutput) return void 0;
551
565
  const d = hookOutput.hookSpecificOutput;
@@ -555,7 +569,7 @@ var toPermissionRequestOutput = (hookOutput) => {
555
569
  };
556
570
  var handlePermissionRequest = async (input) => {
557
571
  if (PRETOOLUSE_HANDLED_TOOLS.has(input.tool_name)) {
558
- const reclaimDeferredExec = input.tool_name !== "AskUserQuestion" && shouldDeferToNativeMode(input.permission_mode, input.tool_name);
572
+ const reclaimDeferredExec = !PRETOOLUSE_PHONE_ANSWERED_TOOLS.includes(input.tool_name) && shouldDeferToNativeMode(input.permission_mode, input.tool_name);
559
573
  if (!reclaimDeferredExec) return void 0;
560
574
  }
561
575
  if (input.tool_name.startsWith("mcp__pushary__")) return void 0;
@@ -1,6 +1,7 @@
1
1
  import {
2
- HOOK_BUDGETS
3
- } from "./chunk-G3LXA2ZP.js";
2
+ HOOK_BUDGETS,
3
+ HOOK_LOCATOR_BINARY
4
+ } from "./chunk-CY4TZK5O.js";
4
5
 
5
6
  // src/codex-config.ts
6
7
  import { createHash } from "crypto";
@@ -63,7 +64,10 @@ var readCodexMcpAuth = (config) => {
63
64
  var isPusharyCodexHook = (entry) => {
64
65
  const hooks = asRecord(entry)?.hooks;
65
66
  if (!Array.isArray(hooks)) return false;
66
- return hooks.some((hook) => String(asRecord(hook)?.command ?? "").includes(CODEX_HOOK_BINARY));
67
+ return hooks.some((hook) => {
68
+ const command = String(asRecord(hook)?.command ?? "");
69
+ return command.includes(CODEX_HOOK_BINARY) || command.includes(HOOK_LOCATOR_BINARY);
70
+ });
67
71
  };
68
72
  var addCodexHooks = (config, command) => {
69
73
  const hooks = ensureRecord(config, "hooks");
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  pusharyDir
3
- } from "./chunk-PHXEADSL.js";
3
+ } from "./chunk-VPJAAGD4.js";
4
4
  import {
5
5
  readJsonSafe
6
6
  } from "./chunk-6MTNS63X.js";
@@ -3,7 +3,7 @@ import {
3
3
  } from "./chunk-2UMNXADU.js";
4
4
  import {
5
5
  redactSecrets
6
- } from "./chunk-G3LXA2ZP.js";
6
+ } from "./chunk-CY4TZK5O.js";
7
7
 
8
8
  // src/crypto.ts
9
9
  import nacl from "tweetnacl";
@@ -1,6 +1,6 @@
1
- import { ReceiptMeta } from '@pushary/hook-mapping';
2
- import { PolicyConfig, ApprovalMode, ModeState, AutoResolveOrigin, AgentQuestion } from '@pushary/contracts';
1
+ import { ClaudePreToolUseOutput, PolicyConfig, ApprovalMode, ModeState, AutoResolveOrigin, AgentQuestion } from '@pushary/contracts';
3
2
  export { ApprovalMode, ModeState, PolicyConfig, ToolPolicy, resolvePolicy } from '@pushary/contracts';
3
+ import { ReceiptMeta } from '@pushary/hook-mapping';
4
4
 
5
5
  interface ToolInput {
6
6
  tool_name: string;
@@ -11,14 +11,7 @@ interface ToolInput {
11
11
  tool_use_id?: string;
12
12
  permission_mode?: string;
13
13
  }
14
- interface HookOutput {
15
- hookSpecificOutput: {
16
- hookEventName: 'PreToolUse';
17
- permissionDecision: 'allow' | 'deny' | 'ask';
18
- permissionDecisionReason?: string;
19
- updatedInput?: Record<string, unknown>;
20
- };
21
- }
14
+ type HookOutput = ClaudePreToolUseOutput;
22
15
  declare const handlePreToolUse: (input: ToolInput) => Promise<HookOutput | undefined>;
23
16
 
24
17
  declare const getPolicy: (apiKey: string, expectedVersion?: string | null, agent?: string, repoAware?: boolean, sessionId?: string) => Promise<PolicyConfig>;
package/dist/src/index.js CHANGED
@@ -1,10 +1,8 @@
1
1
  import {
2
2
  handlePreToolUse
3
- } from "../chunk-LB6RSCLL.js";
4
- import "../chunk-GM54KC62.js";
5
- import "../chunk-HSGEEBBL.js";
6
- import "../chunk-VT4IVERX.js";
7
- import "../chunk-VGADRFG6.js";
3
+ } from "../chunk-S3UD7O35.js";
4
+ import "../chunk-ATBKJNWS.js";
5
+ import "../chunk-MNQI6ZZU.js";
8
6
  import "../chunk-YHG74UFF.js";
9
7
  import {
10
8
  askUser,
@@ -17,11 +15,11 @@ import {
17
15
  handleStop,
18
16
  reportEvent,
19
17
  waitForAnswer
20
- } from "../chunk-Y7BALRVB.js";
18
+ } from "../chunk-33T5OOKP.js";
21
19
  import "../chunk-RN3NOEJF.js";
22
20
  import "../chunk-BSZYIAZL.js";
23
21
  import "../chunk-SAF6HGAA.js";
24
- import "../chunk-ES3FAX5X.js";
22
+ import "../chunk-7C5CJC3H.js";
25
23
  import "../chunk-7QLSKOSU.js";
26
24
  import "../chunk-KZERVKTD.js";
27
25
  import {
@@ -30,7 +28,7 @@ import {
30
28
  } from "../chunk-2UMNXADU.js";
31
29
  import {
32
30
  resolvePolicy
33
- } from "../chunk-G3LXA2ZP.js";
31
+ } from "../chunk-CY4TZK5O.js";
34
32
  export {
35
33
  askUser,
36
34
  cancelQuestion,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pushary/agent-hooks",
3
- "version": "0.85.1",
3
+ "version": "0.86.0",
4
4
  "description": "Permission hooks for AI coding agents: route tool approvals through Pushary push notifications",
5
5
  "keywords": [
6
6
  "pushary",