@pushary/agent-hooks 1.7.0 → 1.7.2

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 (102) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/dist/bin/pushary-bell-hook.js +23 -23
  3. package/dist/bin/pushary-bell.js +10 -10
  4. package/dist/bin/pushary-claude.js +46 -35
  5. package/dist/bin/pushary-clean.js +194 -102
  6. package/dist/bin/pushary-codex-bridge.js +18 -17
  7. package/dist/bin/pushary-codex-hook.js +53 -39
  8. package/dist/bin/pushary-codex.js +14 -12
  9. package/dist/bin/pushary-connect.js +16 -16
  10. package/dist/bin/pushary-cowork.js +5 -5
  11. package/dist/bin/pushary-daemon.js +27 -27
  12. package/dist/bin/pushary-disconnect.js +19 -17
  13. package/dist/bin/pushary-doctor.js +99 -43
  14. package/dist/bin/pushary-elicitation-hook.js +35 -26
  15. package/dist/bin/pushary-gemini-bridge.js +12 -12
  16. package/dist/bin/pushary-gemini-hook.js +47 -32
  17. package/dist/bin/pushary-hook.js +34 -32
  18. package/dist/bin/pushary-login.js +16 -16
  19. package/dist/bin/pushary-logout.js +8 -8
  20. package/dist/bin/pushary-mcp.js +9 -8
  21. package/dist/bin/pushary-mode.js +10 -10
  22. package/dist/bin/pushary-notification-hook.js +29 -27
  23. package/dist/bin/pushary-opencode-hook.js +46 -32
  24. package/dist/bin/pushary-permission-denied-hook.js +34 -32
  25. package/dist/bin/pushary-permission-hook.js +33 -31
  26. package/dist/bin/pushary-post-hook.js +29 -27
  27. package/dist/bin/pushary-prompt-hook.js +29 -27
  28. package/dist/bin/pushary-session-end-hook.js +29 -27
  29. package/dist/bin/pushary-session-start-hook.js +33 -31
  30. package/dist/bin/pushary-setup.js +143 -105
  31. package/dist/bin/pushary-stats.js +5 -5
  32. package/dist/bin/pushary-status.js +19 -19
  33. package/dist/bin/pushary-stop-hook.js +29 -27
  34. package/dist/bin/pushary-stopfailure-hook.js +29 -27
  35. package/dist/bin/pushary-suggestions.js +5 -5
  36. package/dist/bin/pushary-transcript-register.js +19 -19
  37. package/dist/bin/pushary-transcripts.js +7 -7
  38. package/dist/bin/pushary-upgrade.js +20 -20
  39. package/dist/bin/pushary-wait.js +10 -10
  40. package/dist/bin/pushary.js +6 -6
  41. package/dist/{chunk-IJ6ECPA6.js → chunk-2B4XCMH6.js} +75 -232
  42. package/dist/{chunk-EGKXSQCY.js → chunk-2XOHXZRC.js} +12 -4
  43. package/dist/{chunk-D6BULEJW.js → chunk-3NSRL5E7.js} +2 -2
  44. package/dist/{chunk-UC65FJJT.js → chunk-3RO2UWJQ.js} +2 -2
  45. package/dist/{chunk-73T6MSU5.js → chunk-4S7CCOXB.js} +5 -5
  46. package/dist/{chunk-7OS4IHGI.js → chunk-5GIBNUBD.js} +2 -2
  47. package/dist/chunk-5OP5MQI7.js +45 -0
  48. package/dist/{chunk-B3UXMSMW.js → chunk-65RN6ZCO.js} +1 -1
  49. package/dist/{chunk-2PS6QWDW.js → chunk-6NHXKTQU.js} +2 -2
  50. package/dist/{chunk-V6QRSU2G.js → chunk-6QZK4O36.js} +4 -4
  51. package/dist/{chunk-ZQ7IDOKY.js → chunk-A6WQF7FQ.js} +3 -3
  52. package/dist/{chunk-QXAK5RFU.js → chunk-AWZNDM3B.js} +1 -1
  53. package/dist/{chunk-JNXVOU6Y.js → chunk-D4OGEWLH.js} +19 -2
  54. package/dist/{chunk-R2XCLT6I.js → chunk-E3PLF7YB.js} +4 -4
  55. package/dist/{chunk-2C7CCWVX.js → chunk-F6BMJOSW.js} +1 -1
  56. package/dist/{chunk-XNCMT7YY.js → chunk-HOIGXMI7.js} +1 -1
  57. package/dist/{chunk-CVALVGA7.js → chunk-I7B2DNVU.js} +1 -1
  58. package/dist/{chunk-S3UD4V6W.js → chunk-INR3RVQ3.js} +13 -3
  59. package/dist/{chunk-52NVBNJT.js → chunk-IZVAVXHQ.js} +6 -4
  60. package/dist/{chunk-5HLTWFJ6.js → chunk-JMSB6CJT.js} +1 -1
  61. package/dist/{chunk-KYQXUGH7.js → chunk-KAJT6HPI.js} +3 -3
  62. package/dist/{chunk-VKVIAZQO.js → chunk-KLXYMPTM.js} +1 -1
  63. package/dist/{chunk-PQ4F64M2.js → chunk-L6IF4JOF.js} +3 -3
  64. package/dist/{chunk-ROQK7IN3.js → chunk-LI3C2IQY.js} +2 -2
  65. package/dist/{chunk-B234346F.js → chunk-M2JGLFFS.js} +1 -1
  66. package/dist/chunk-MY3UZO5U.js +99 -0
  67. package/dist/chunk-N6IOSZOE.js +185 -0
  68. package/dist/{chunk-DNJUSFBL.js → chunk-NYBZJ44G.js} +40 -5
  69. package/dist/{chunk-BBKNJJVX.js → chunk-OJRB5QKN.js} +6 -6
  70. package/dist/{chunk-PUDBG3GM.js → chunk-OXEF6JHD.js} +4 -4
  71. package/dist/{chunk-LEZFOTMI.js → chunk-PWNQGITG.js} +1 -1
  72. package/dist/{chunk-B76WVEDX.js → chunk-Q7CDBJXU.js} +1 -1
  73. package/dist/{chunk-KXMZMZD3.js → chunk-QSZ37ROP.js} +1 -1
  74. package/dist/{chunk-BH2SKSSK.js → chunk-R7ZETVNR.js} +1 -1
  75. package/dist/{chunk-DZURML2D.js → chunk-RLRDPZG4.js} +1 -1
  76. package/dist/{chunk-J2355HR7.js → chunk-SEIZQZEE.js} +1 -1
  77. package/dist/{chunk-V4AQBAVG.js → chunk-SJN64NJX.js} +1 -1
  78. package/dist/{chunk-B3YQANFN.js → chunk-SKEW3TQX.js} +1 -1
  79. package/dist/{chunk-L4H2Q2S4.js → chunk-SKMWPXGV.js} +500 -46
  80. package/dist/{chunk-WUNZAE7I.js → chunk-T3PRPFDY.js} +5 -5
  81. package/dist/{chunk-YYINL576.js → chunk-TVJYGZXB.js} +2 -2
  82. package/dist/{chunk-O2LEPIU6.js → chunk-U3MS6D5X.js} +1 -1
  83. package/dist/{chunk-5RUIFDTP.js → chunk-U4TMAANR.js} +33 -2
  84. package/dist/{chunk-KW2RRR7D.js → chunk-UFNBKYWT.js} +3 -3
  85. package/dist/{chunk-DVQDPI3T.js → chunk-UUTWHVSV.js} +35 -35
  86. package/dist/{chunk-X6KDKOV5.js → chunk-VQDJ6KB3.js} +3 -3
  87. package/dist/{chunk-UOAQ6QMP.js → chunk-VSGKQ3HI.js} +2 -2
  88. package/dist/{chunk-YROGUPOZ.js → chunk-WWTLAZQA.js} +25 -8
  89. package/dist/{chunk-SQRWHALT.js → chunk-WXIVUIXA.js} +2 -2
  90. package/dist/{chunk-HDRNWNVP.js → chunk-YLCSKVQQ.js} +9 -28
  91. package/dist/{chunk-JXUAXO74.js → chunk-YNJ2H2FD.js} +2 -2
  92. package/dist/{chunk-BWYJ6AAF.js → chunk-YU4222G2.js} +2 -2
  93. package/dist/{chunk-4F46VIEE.js → chunk-YU54LNC6.js} +6 -6
  94. package/dist/{chunk-EAQMWJPG.js → chunk-ZMWX5SDA.js} +8 -8
  95. package/dist/{chunk-6ADZQFVW.js → chunk-ZUKZJQGR.js} +1 -1
  96. package/dist/{reapply-6PH3FMPX.js → reapply-D6RSDYCK.js} +9 -9
  97. package/dist/src/index.d.ts +1 -0
  98. package/dist/src/index.js +22 -20
  99. package/package.json +2 -1
  100. package/dist/chunk-DANOZVZK.js +0 -307
  101. package/dist/chunk-P32LPT2L.js +0 -6
  102. package/dist/chunk-YHG74UFF.js +0 -12
@@ -1,30 +1,42 @@
1
+ import {
2
+ DEFAULT_SESSION,
3
+ isDefaultSession,
4
+ listPendingQuestions,
5
+ listPendingQuestionsForToolUse,
6
+ markPendingCommandDelivered,
7
+ readPendingCommand,
8
+ removePendingCommand,
9
+ removePendingQuestion,
10
+ removePendingSession,
11
+ savePendingCommand
12
+ } from "./chunk-N6IOSZOE.js";
1
13
  import {
2
14
  NOTIFICATION_PUSH_TYPES,
3
15
  POSTTOOLUSE_MIRROR_ONLY_TOOLS
4
- } from "./chunk-DZURML2D.js";
16
+ } from "./chunk-RLRDPZG4.js";
5
17
  import {
6
18
  cancelQuestion,
7
19
  isPolicyConfig,
8
20
  sendNotification,
9
21
  waitForAnswer
10
- } from "./chunk-EGKXSQCY.js";
22
+ } from "./chunk-2XOHXZRC.js";
11
23
  import {
12
24
  clientErrorKindOf,
13
25
  reportClientError
14
- } from "./chunk-V6QRSU2G.js";
26
+ } from "./chunk-6QZK4O36.js";
15
27
  import {
16
28
  withRetry
17
- } from "./chunk-JNXVOU6Y.js";
29
+ } from "./chunk-D4OGEWLH.js";
18
30
  import {
19
31
  getMachineId
20
- } from "./chunk-XNCMT7YY.js";
32
+ } from "./chunk-HOIGXMI7.js";
21
33
  import {
22
34
  reportedInstallSource
23
- } from "./chunk-ROQK7IN3.js";
35
+ } from "./chunk-LI3C2IQY.js";
24
36
  import {
25
37
  getApiKey,
26
38
  getBaseUrl
27
- } from "./chunk-V4AQBAVG.js";
39
+ } from "./chunk-SJN64NJX.js";
28
40
  import {
29
41
  ACTION_BODY_MAX,
30
42
  AGENT_LABELS_BY_TYPE,
@@ -48,7 +60,7 @@ import {
48
60
  resolveAutoResolveOrigin,
49
61
  resolvePolicy,
50
62
  toToolLookup
51
- } from "./chunk-YROGUPOZ.js";
63
+ } from "./chunk-WWTLAZQA.js";
52
64
 
53
65
  // src/policy.ts
54
66
  import { createHash } from "crypto";
@@ -865,190 +877,19 @@ var codexReviewerDenialsSince = (reviews, since) => {
865
877
  });
866
878
  };
867
879
 
868
- // src/pending.ts
869
- import { join as join2 } from "path";
870
- import { homedir, tmpdir as tmpdir2 } from "os";
871
- import { createHash as createHash2 } from "crypto";
872
- import { existsSync as existsSync2, mkdirSync, writeFileSync as writeFileSync2, readFileSync as readFileSync2, readdirSync, renameSync, unlinkSync, rmSync, statSync } from "fs";
873
- var PENDING_DIR = join2(tmpdir2(), "pushary-pending");
874
- var commandRoot = () => process.env.PUSHARY_COMMAND_DIR?.trim() || join2(homedir(), ".pushary", "run", "commands");
875
- var DEFAULT_SESSION = "_no_session";
876
- var GRACE_MS = 10 * 60 * 1e3;
877
- var sanitize = (sessionId) => sessionId.replace(/[^A-Za-z0-9_-]/g, "_").slice(0, 128) || DEFAULT_SESSION;
878
- var dirFor = (sessionId) => join2(PENDING_DIR, sanitize(sessionId));
879
- var commandDirFor = (sessionId, namespace = "") => join2(
880
- commandRoot(),
881
- createHash2("sha256").update(namespace).digest("hex").slice(0, 16),
882
- sanitize(sessionId)
883
- );
884
- var legacyCommandFile = (sessionId, namespace = "") => join2(commandDirFor(sessionId, namespace), "command.json");
885
- var commandFile = (sessionId, id, namespace = "") => join2(commandDirFor(sessionId, namespace), `${createHash2("sha256").update(id).digest("hex")}.json`);
886
- var readCommandFile = (file) => {
887
- try {
888
- const value = JSON.parse(readFileSync2(file, "utf-8"));
889
- if (typeof value.id !== "string" || typeof value.text !== "string" || typeof value.expiresAtMs !== "number") return null;
890
- if (value.expiresAtMs <= Date.now()) {
891
- unlinkSync(file);
892
- return null;
893
- }
894
- return {
895
- ...value,
896
- delivered: value.delivered === true,
897
- ...value.failed === true ? { failed: true } : {}
898
- };
899
- } catch {
900
- return null;
901
- }
902
- };
903
- var readPendingCommandById = (sessionId, id, namespace = "") => {
904
- const current = readCommandFile(commandFile(sessionId, id, namespace));
905
- if (current?.id === id) return current;
906
- const legacy = readCommandFile(legacyCommandFile(sessionId, namespace));
907
- return legacy?.id === id ? legacy : null;
908
- };
909
- var pruneExpiredCommands = (dir) => {
910
- let files;
911
- try {
912
- files = readdirSync(dir).filter((file) => file.endsWith(".json"));
913
- } catch {
914
- return [];
915
- }
916
- return files.flatMap((file) => {
917
- const command = readCommandFile(join2(dir, file));
918
- return command ? [command] : [];
919
- });
920
- };
921
- var pruneExpiredCommandStore = () => {
922
- let namespaces;
923
- try {
924
- namespaces = readdirSync(commandRoot(), { withFileTypes: true });
925
- } catch {
926
- return;
927
- }
928
- for (const namespace of namespaces.filter((entry) => entry.isDirectory())) {
929
- const namespaceDir = join2(commandRoot(), namespace.name);
930
- let sessions;
931
- try {
932
- sessions = readdirSync(namespaceDir, { withFileTypes: true });
933
- } catch {
934
- continue;
935
- }
936
- for (const session of sessions.filter((entry) => entry.isDirectory())) {
937
- pruneExpiredCommands(join2(namespaceDir, session.name));
938
- }
939
- }
940
- };
941
- var readPendingCommand = (sessionId, namespace = "") => {
942
- const commands = pruneExpiredCommands(commandDirFor(sessionId, namespace));
943
- return commands.find((command) => !command.delivered && !command.failed) ?? commands[0] ?? null;
944
- };
945
- var savePendingCommand = (sessionId, command, namespace = "") => {
946
- const dir = commandDirFor(sessionId, namespace);
947
- try {
948
- mkdirSync(dir, { recursive: true, mode: 448 });
949
- pruneExpiredCommandStore();
950
- if (readPendingCommandById(sessionId, command.id, namespace)) return false;
951
- writeFileSync2(commandFile(sessionId, command.id, namespace), JSON.stringify(command), { encoding: "utf-8", mode: 384, flag: "wx" });
952
- return true;
953
- } catch {
954
- return false;
955
- }
956
- };
957
- var updatePendingCommand = (sessionId, id, update, namespace = "") => {
958
- const command = readPendingCommandById(sessionId, id, namespace);
959
- if (!command) return false;
960
- const currentFile = commandFile(sessionId, id, namespace);
961
- const file = existsSync2(currentFile) ? currentFile : legacyCommandFile(sessionId, namespace);
962
- const temporary = `${file}.${process.pid}.tmp`;
963
- try {
964
- writeFileSync2(temporary, JSON.stringify({ ...command, ...update, text: "" }), { encoding: "utf-8", mode: 384 });
965
- renameSync(temporary, file);
966
- return true;
967
- } catch {
968
- try {
969
- unlinkSync(temporary);
970
- } catch {
971
- }
972
- return false;
973
- }
974
- };
975
- var markPendingCommandDelivered = (sessionId, id, namespace = "") => updatePendingCommand(sessionId, id, { delivered: true, failed: void 0 }, namespace);
976
- var markPendingCommandFailed = (sessionId, id, namespace = "") => updatePendingCommand(sessionId, id, { delivered: false, failed: true }, namespace);
977
- var removePendingCommand = (sessionId, id, namespace = "") => {
978
- if (!readPendingCommandById(sessionId, id, namespace)) return;
979
- try {
980
- unlinkSync(commandFile(sessionId, id, namespace));
981
- } catch {
982
- }
983
- try {
984
- if (readCommandFile(legacyCommandFile(sessionId, namespace))?.id === id) {
985
- unlinkSync(legacyCommandFile(sessionId, namespace));
986
- }
987
- } catch {
988
- }
989
- };
990
- var isDefaultSession = (sessionId) => sanitize(sessionId) === DEFAULT_SESSION;
991
- var listPendingQuestionsForToolUse = (sessionId, toolUseId) => {
992
- if (!toolUseId) return [];
993
- const dir = dirFor(sessionId);
994
- let files;
995
- try {
996
- files = readdirSync(dir);
997
- } catch {
998
- return [];
999
- }
1000
- return files.filter((name) => {
1001
- try {
1002
- return readFileSync2(join2(dir, name), "utf-8") === toolUseId;
1003
- } catch {
1004
- return false;
1005
- }
1006
- });
1007
- };
1008
- var listPendingQuestions = (sessionId) => {
1009
- const dir = dirFor(sessionId);
1010
- let files;
1011
- try {
1012
- files = readdirSync(dir);
1013
- } catch {
1014
- return [];
1015
- }
1016
- if (!isDefaultSession(sessionId)) return files;
1017
- const cutoff = Date.now() - GRACE_MS;
1018
- return files.filter((name) => {
1019
- try {
1020
- return statSync(join2(dir, name)).mtimeMs < cutoff;
1021
- } catch {
1022
- return false;
1023
- }
1024
- });
1025
- };
1026
- var removePendingQuestion = (sessionId, correlationId) => {
1027
- try {
1028
- unlinkSync(join2(dirFor(sessionId), correlationId));
1029
- } catch {
1030
- }
1031
- };
1032
- var removePendingSession = (sessionId) => {
1033
- try {
1034
- rmSync(dirFor(sessionId), { recursive: true, force: true });
1035
- } catch {
1036
- }
1037
- };
1038
-
1039
880
  // src/repo.ts
1040
- import { existsSync as existsSync3, readFileSync as readFileSync3, statSync as statSync2 } from "fs";
1041
- import { basename as basename2, dirname, isAbsolute as isAbsolute2, join as join3, resolve as resolve2 } from "path";
881
+ import { existsSync as existsSync2, readFileSync as readFileSync2, statSync } from "fs";
882
+ import { basename as basename2, dirname, isAbsolute as isAbsolute2, join as join2, resolve as resolve2 } from "path";
1042
883
  var MAX_PARENT_WALK = 64;
1043
884
  var GIT_FILE_PREFIX = "gitdir:";
1044
885
  var findGitDir = (startDir) => {
1045
886
  let current = resolve2(startDir);
1046
887
  for (let depth = 0; depth < MAX_PARENT_WALK; depth += 1) {
1047
- const candidate = join3(current, ".git");
888
+ const candidate = join2(current, ".git");
1048
889
  try {
1049
- if (existsSync3(candidate)) {
1050
- if (statSync2(candidate).isDirectory()) return candidate;
1051
- const pointer = readFileSync3(candidate, "utf-8").trim();
890
+ if (existsSync2(candidate)) {
891
+ if (statSync(candidate).isDirectory()) return candidate;
892
+ const pointer = readFileSync2(candidate, "utf-8").trim();
1052
893
  if (pointer.startsWith(GIT_FILE_PREFIX)) {
1053
894
  const target = pointer.slice(GIT_FILE_PREFIX.length).trim();
1054
895
  return isAbsolute2(target) ? target : resolve2(current, target);
@@ -1064,7 +905,7 @@ var findGitDir = (startDir) => {
1064
905
  };
1065
906
  var configPathFor = (gitDir) => {
1066
907
  const index = gitDir.replace(/\\/g, "/").indexOf("/worktrees/");
1067
- return index === -1 ? join3(gitDir, "config") : join3(gitDir.slice(0, index), "config");
908
+ return index === -1 ? join2(gitDir, "config") : join2(gitDir.slice(0, index), "config");
1068
909
  };
1069
910
  var ORIGIN_SECTION = /^\[remote "origin"\]/;
1070
911
  var SECTION_HEADER = /^\[/;
@@ -1072,9 +913,9 @@ var URL_LINE = /^url\s*=\s*(.+)$/;
1072
913
  var readOriginRemote = (gitDir) => {
1073
914
  try {
1074
915
  const configPath = configPathFor(gitDir);
1075
- if (!existsSync3(configPath)) return void 0;
916
+ if (!existsSync2(configPath)) return void 0;
1076
917
  let inOrigin = false;
1077
- for (const rawLine of readFileSync3(configPath, "utf-8").split("\n")) {
918
+ for (const rawLine of readFileSync2(configPath, "utf-8").split("\n")) {
1078
919
  const line = rawLine.trim();
1079
920
  if (SECTION_HEADER.test(line)) {
1080
921
  inOrigin = ORIGIN_SECTION.test(line);
@@ -1121,10 +962,10 @@ var repoKeyFor = (cwd) => {
1121
962
  };
1122
963
 
1123
964
  // src/usage.ts
1124
- import { closeSync, mkdirSync as mkdirSync2, openSync, readFileSync as readFileSync4, readSync, statSync as statSync3, writeFileSync as writeFileSync3 } from "fs";
1125
- import { createHash as createHash3 } from "crypto";
1126
- import { join as join4 } from "path";
1127
- import { tmpdir as tmpdir3 } from "os";
965
+ import { closeSync, mkdirSync, openSync, readFileSync as readFileSync3, readSync, statSync as statSync2, writeFileSync as writeFileSync2 } from "fs";
966
+ import { createHash as createHash2 } from "crypto";
967
+ import { join as join3 } from "path";
968
+ import { tmpdir as tmpdir2 } from "os";
1128
969
  var DEFAULT_PRICES = [
1129
970
  { match: "opus-4-1", in: 15, out: 75 },
1130
971
  { match: "opus-4-0", in: 15, out: 75 },
@@ -1159,12 +1000,12 @@ var estimateCostUsd = (usage, model) => {
1159
1000
  const perTokenOut = price.out / 1e6;
1160
1001
  return usage.inputTokens * perTokenIn + usage.outputTokens * perTokenOut + usage.cacheCreationTokens * perTokenIn * CACHE_WRITE_MULTIPLIER + usage.cacheReadTokens * perTokenIn * CACHE_READ_MULTIPLIER;
1161
1002
  };
1162
- var stateDir = () => process.env.PUSHARY_USAGE_DIR?.trim() || join4(tmpdir3(), "pushary-usage");
1163
- var stateFile = (sessionId) => join4(stateDir(), sessionId.replace(/[^a-zA-Z0-9_-]/g, "_"));
1003
+ var stateDir = () => process.env.PUSHARY_USAGE_DIR?.trim() || join3(tmpdir2(), "pushary-usage");
1004
+ var stateFile = (sessionId) => join3(stateDir(), sessionId.replace(/[^a-zA-Z0-9_-]/g, "_"));
1164
1005
  var emptyState = () => ({ offset: 0, tokensIn: 0, tokensOut: 0, costUsd: 0, recentIds: [] });
1165
1006
  var readState = (path) => {
1166
1007
  try {
1167
- const parsed = JSON.parse(readFileSync4(path, "utf-8"));
1008
+ const parsed = JSON.parse(readFileSync3(path, "utf-8"));
1168
1009
  if (typeof parsed.offset === "number" && parsed.offset >= 0 && typeof parsed.tokensIn === "number" && typeof parsed.tokensOut === "number" && typeof parsed.costUsd === "number" && Array.isArray(parsed.recentIds)) {
1169
1010
  return {
1170
1011
  offset: parsed.offset,
@@ -1239,7 +1080,7 @@ var extractUserText = (content) => {
1239
1080
  };
1240
1081
  var readLastUserPrompt = (transcriptPath) => {
1241
1082
  try {
1242
- const size = statSync3(transcriptPath).size;
1083
+ const size = statSync2(transcriptPath).size;
1243
1084
  const start = Math.max(0, size - USER_PROMPT_TAIL_BYTES);
1244
1085
  const lines = readRange(transcriptPath, start, size).toString("utf-8").split("\n");
1245
1086
  for (let i = lines.length - 1; i >= 0; i -= 1) {
@@ -1262,7 +1103,7 @@ var readLastUserPrompt = (transcriptPath) => {
1262
1103
  };
1263
1104
  var readNewUsage = (transcriptPath, sessionId) => {
1264
1105
  try {
1265
- const file = statSync3(transcriptPath);
1106
+ const file = statSync2(transcriptPath);
1266
1107
  const size = file.size;
1267
1108
  const path = stateFile(sessionId);
1268
1109
  let state = readState(path);
@@ -1280,14 +1121,14 @@ var readNewUsage = (transcriptPath, sessionId) => {
1280
1121
  state.offset += lastNewline + 1;
1281
1122
  }
1282
1123
  }
1283
- mkdirSync2(stateDir(), { recursive: true });
1284
- writeFileSync3(path, JSON.stringify(state), "utf-8");
1124
+ mkdirSync(stateDir(), { recursive: true });
1125
+ writeFileSync2(path, JSON.stringify(state), "utf-8");
1285
1126
  if (state.tokensIn === 0 && state.tokensOut === 0) return null;
1286
1127
  return {
1287
1128
  // Identical cursor-range reports share an identity across transport retries.
1288
1129
  // Native message identity distinguishes an in-place rotation of the range.
1289
1130
  // This does not serialize readers of different, potentially overlapping ranges.
1290
- eventId: createHash3("sha256").update(JSON.stringify([
1131
+ eventId: createHash2("sha256").update(JSON.stringify([
1291
1132
  sessionId,
1292
1133
  file.dev,
1293
1134
  file.ino,
@@ -1372,57 +1213,58 @@ var phoneDrivenEnv = (base) => ({
1372
1213
  });
1373
1214
 
1374
1215
  // src/throttle.ts
1375
- import { join as join5 } from "path";
1376
- import { tmpdir as tmpdir4 } from "os";
1377
- import { createHash as createHash4 } from "crypto";
1378
- import { existsSync as existsSync4, mkdirSync as mkdirSync3, statSync as statSync4, writeFileSync as writeFileSync4, rmSync as rmSync2 } from "fs";
1379
- var THROTTLE_DIR = join5(tmpdir4(), "pushary-throttle");
1380
- var markerPath = (key) => join5(THROTTLE_DIR, createHash4("sha256").update(key).digest("hex").slice(0, 16));
1216
+ import { join as join4 } from "path";
1217
+ import { tmpdir as tmpdir3 } from "os";
1218
+ import { createHash as createHash3 } from "crypto";
1219
+ import { existsSync as existsSync3, mkdirSync as mkdirSync2, statSync as statSync3, writeFileSync as writeFileSync3, rmSync } from "fs";
1220
+ var THROTTLE_DIR = join4(tmpdir3(), "pushary-throttle");
1221
+ var markerPath = (key) => join4(THROTTLE_DIR, createHash3("sha256").update(key).digest("hex").slice(0, 16));
1381
1222
  var claimThrottle = (key, windowMs) => {
1382
1223
  try {
1383
1224
  const path = markerPath(key);
1384
- if (existsSync4(path) && Date.now() - statSync4(path).mtimeMs < windowMs) return "throttled";
1385
- mkdirSync3(THROTTLE_DIR, { recursive: true });
1386
- writeFileSync4(path, "", "utf-8");
1225
+ if (existsSync3(path) && Date.now() - statSync3(path).mtimeMs < windowMs) return "throttled";
1226
+ mkdirSync2(THROTTLE_DIR, { recursive: true });
1227
+ writeFileSync3(path, "", "utf-8");
1387
1228
  return "passed";
1388
1229
  } catch {
1389
1230
  return "unavailable";
1390
1231
  }
1391
1232
  };
1392
1233
  var releaseThrottle = (key) => {
1393
- rmSync2(markerPath(key), { force: true });
1234
+ rmSync(markerPath(key), { force: true });
1394
1235
  };
1395
1236
 
1396
1237
  // src/events.ts
1397
- import { basename as basename3, join as join6 } from "path";
1398
- import { existsSync as existsSync5, mkdirSync as mkdirSync4, readFileSync as readFileSync5, statSync as statSync5, writeFileSync as writeFileSync5 } from "fs";
1399
- import { tmpdir as tmpdir5 } from "os";
1400
- var INTENT_DIR = join6(tmpdir5(), "pushary-intent");
1238
+ import { basename as basename3, join as join5 } from "path";
1239
+ import { existsSync as existsSync4, mkdirSync as mkdirSync3, readFileSync as readFileSync4, statSync as statSync4, writeFileSync as writeFileSync4 } from "fs";
1240
+ import { tmpdir as tmpdir4 } from "os";
1241
+ var INTENT_DIR = join5(tmpdir4(), "pushary-intent");
1401
1242
  var INTENT_MAX_BYTES = 2048;
1402
1243
  var INTENT_GRACE_MS = 10 * 60 * 1e3;
1403
1244
  var sanitizeSession = (sessionId) => sessionId.replace(/[^A-Za-z0-9_-]/g, "_").slice(0, 128) || DEFAULT_SESSION;
1404
- var intentFile = (sessionId) => join6(INTENT_DIR, sanitizeSession(sessionId));
1245
+ var intentFile = (sessionId) => join5(INTENT_DIR, sanitizeSession(sessionId));
1405
1246
  var saveLastPrompt = (sessionId, prompt) => {
1406
1247
  try {
1407
1248
  const trimmed = prompt.replace(/\s+/g, " ").trim().slice(0, INTENT_MAX_BYTES);
1408
1249
  if (!trimmed) return;
1409
- mkdirSync4(INTENT_DIR, { recursive: true });
1410
- writeFileSync5(intentFile(sessionId), trimmed, "utf-8");
1250
+ mkdirSync3(INTENT_DIR, { recursive: true });
1251
+ writeFileSync4(intentFile(sessionId), trimmed, "utf-8");
1411
1252
  } catch {
1412
1253
  }
1413
1254
  };
1414
1255
  var readLastPrompt = (sessionId) => {
1415
1256
  try {
1416
1257
  const path = intentFile(sessionId);
1417
- if (isDefaultSession(sessionId) && statSync5(path).mtimeMs < Date.now() - INTENT_GRACE_MS) return void 0;
1418
- const value = readFileSync5(path, "utf-8").trim();
1258
+ if (isDefaultSession(sessionId) && statSync4(path).mtimeMs < Date.now() - INTENT_GRACE_MS) return void 0;
1259
+ const value = readFileSync4(path, "utf-8").trim();
1419
1260
  return value.length > 0 ? value : void 0;
1420
1261
  } catch {
1421
1262
  return void 0;
1422
1263
  }
1423
1264
  };
1424
- var pendingQuestionsToSettle = (sessionId, toolUseId) => {
1265
+ var pendingQuestionsToSettle = (sessionId, toolUseId, insideSubagent = false) => {
1425
1266
  const settledElsewhere = toolUseId ? listPendingQuestionsForToolUse(sessionId, toolUseId) : [];
1267
+ if (insideSubagent) return settledElsewhere;
1426
1268
  return [.../* @__PURE__ */ new Set([...settledElsewhere, ...listPendingQuestions(sessionId)])];
1427
1269
  };
1428
1270
  var reconcileQuestions = async (sessionId, files, signal) => {
@@ -1478,8 +1320,8 @@ var POLICY_CACHE_TTL_MS = 5 * 60 * 1e3;
1478
1320
  var readFreshCachedPolicy = (apiKey, agentType, sessionId) => {
1479
1321
  const request = policyRequestFor(agentType);
1480
1322
  const path = policyCacheFile(apiKey, request.agent, request.repoAware);
1481
- if (!existsSync5(path)) return null;
1482
- const cached = JSON.parse(readFileSync5(path, "utf-8"));
1323
+ if (!existsSync4(path)) return null;
1324
+ const cached = JSON.parse(readFileSync4(path, "utf-8"));
1483
1325
  if (!isPolicyConfig(cached)) return null;
1484
1326
  if ((cached._sessionId ?? null) !== (sessionId ?? null)) return null;
1485
1327
  if (!cached._cachedAt || Date.now() - cached._cachedAt >= POLICY_CACHE_TTL_MS) return null;
@@ -1575,7 +1417,7 @@ var reportEvent = async (event, options = {}) => {
1575
1417
  const machineId = event.machineId ?? getMachineId();
1576
1418
  const sessionKey = event.sessionId || DEFAULT_SESSION;
1577
1419
  const receiptNamespace = `${apiKey}:${machineId}:${event.agentType}`;
1578
- const clientCanDrain = consumesActivityContext(event.agentType) && options.timeoutMs === void 0;
1420
+ const clientCanDrain = consumesActivityContext(event.agentType) && options.timeoutMs === void 0 && options.skipDrain !== true;
1579
1421
  const deadlineMs = options.budgetMs === void 0 ? void 0 : Date.now() + options.budgetMs;
1580
1422
  const ackReserveMs = deadlineMs === void 0 ? 0 : Math.min(ACK_RESERVE_MS, Math.floor((options.budgetMs ?? 0) * 0.4));
1581
1423
  const postTimeoutMs = (fallback) => deadlineMs === void 0 ? fallback : Math.max(0, Math.min(fallback, deadlineMs - ackReserveMs - Date.now()));
@@ -1675,6 +1517,7 @@ var settleQuestionsForToolUse = async (sessionId, toolUseId) => {
1675
1517
  const late = await reconcileQuestions(sessionId, listPendingQuestionsForToolUse(sessionId, toolUseId));
1676
1518
  for (const answer of late) removePendingQuestion(sessionId, answer.correlationId);
1677
1519
  };
1520
+ var ranInsideSubagent = (input) => sessionIdentityOf(input).parentSessionId !== void 0;
1678
1521
  var handlePostToolUse = async (input, agent = CLAUDE_CODE_AGENT) => {
1679
1522
  const occurredAt = (/* @__PURE__ */ new Date()).toISOString();
1680
1523
  const deadline = Date.now() + HOOK_BUDGET_MS;
@@ -1687,9 +1530,11 @@ var handlePostToolUse = async (input, agent = CLAUDE_CODE_AGENT) => {
1687
1530
  const isError = isToolResultError(toolResult);
1688
1531
  const receiptsEnabled = process.env.PUSHARY_RECEIPTS !== "off";
1689
1532
  const sessionKey = input.session_id || DEFAULT_SESSION;
1533
+ const insideSubagent = ranInsideSubagent(input);
1534
+ const questionsToSettle = pendingQuestionsToSettle(sessionKey, input.tool_use_id, insideSubagent);
1690
1535
  const [liveMode, late] = await Promise.all([
1691
1536
  fetchModeState(getApiKey(), input.session_id, MODE_REUSE_MS, signal),
1692
- reconcilePendingQuestions(sessionKey, input.tool_use_id, signal)
1537
+ reconcileQuestions(sessionKey, questionsToSettle, signal)
1693
1538
  ]);
1694
1539
  let additionalContext;
1695
1540
  if (late.length > 0 && agent.type === CLAUDE_CODE_AGENT.type) {
@@ -1712,7 +1557,12 @@ var handlePostToolUse = async (input, agent = CLAUDE_CODE_AGENT) => {
1712
1557
  ...beacon ? { decisionOrigin: beacon.decisionOrigin, toolName: beacon.toolName, toolTarget: beacon.toolTarget } : {},
1713
1558
  meta: receiptsEnabled ? deriveReceiptMeta(lookup.tool, lookup.input, toolResult, input.cwd ?? process.cwd()) : void 0,
1714
1559
  usage: deriveUsage(input.transcript_path, input.session_id)
1715
- }, { maxAttempts: 1, budgetMs: Math.min(DRAINING_REPORT_BUDGET_MS, Math.max(0, deadline - Date.now())), signal });
1560
+ }, {
1561
+ maxAttempts: 1,
1562
+ budgetMs: Math.min(DRAINING_REPORT_BUDGET_MS, Math.max(0, deadline - Date.now())),
1563
+ signal,
1564
+ skipDrain: insideSubagent
1565
+ });
1716
1566
  const extraReports = [];
1717
1567
  if (claimThrottle(`hookactive:${sessionKey}`, HOOK_ACTIVE_WINDOW_MS) === "passed") {
1718
1568
  extraReports.push(
@@ -1992,13 +1842,6 @@ export {
1992
1842
  isCodexGuardianRolloutHead,
1993
1843
  scanCodexGuardianReviews,
1994
1844
  codexReviewerDenialsSince,
1995
- DEFAULT_SESSION,
1996
- readPendingCommandById,
1997
- readPendingCommand,
1998
- savePendingCommand,
1999
- markPendingCommandDelivered,
2000
- markPendingCommandFailed,
2001
- removePendingCommand,
2002
1845
  repoKeyFor,
2003
1846
  claimThrottle,
2004
1847
  releaseThrottle,
@@ -1,16 +1,17 @@
1
1
  import {
2
2
  clientErrorKindOf,
3
3
  reportClientError
4
- } from "./chunk-V6QRSU2G.js";
4
+ } from "./chunk-6QZK4O36.js";
5
5
  import {
6
+ SubscriptionRequiredError,
6
7
  callMcpTool
7
- } from "./chunk-JNXVOU6Y.js";
8
+ } from "./chunk-D4OGEWLH.js";
8
9
  import {
9
10
  installNetworkDefaults
10
- } from "./chunk-KW2RRR7D.js";
11
+ } from "./chunk-UFNBKYWT.js";
11
12
  import {
12
13
  getBaseUrl
13
- } from "./chunk-V4AQBAVG.js";
14
+ } from "./chunk-SJN64NJX.js";
14
15
 
15
16
  // src/api.ts
16
17
  import { randomUUID } from "crypto";
@@ -53,6 +54,11 @@ var DECISION_STOP_REASON = "Approval was cancelled or became unavailable. Retry
53
54
  var isDecisionStopped = (result) => result.handoffAction === "stop" || ["cancelled", "unavailable", "stopped", "missing"].includes(result.status ?? "");
54
55
  var isLocalQuestionHandoff = (result) => result.suppressed === true || result.noDevices === true || result.status === "terminal" || result.status === "notified";
55
56
  var STOPPED = { answered: false, handoffAction: "stop" };
57
+ var UNVERIFIABLE_APPROVAL_REASON = "Pushary could not create a verifiable approval. Retry the action.";
58
+ var unavailableQuestionUnlessSubscriptionRequired = (error) => {
59
+ if (error instanceof SubscriptionRequiredError) throw error;
60
+ return { correlationId: "", status: "unavailable" };
61
+ };
56
62
  var askUser = async (apiKey, params, timeoutMs = 15e3) => {
57
63
  const identified = params.toolUseId || params.requestId ? params : { ...params, requestId: randomUUID() };
58
64
  const result = await callMcpTool(apiKey, "ask_user", { ...identified, wait: false }, { maxRetries: 3, timeoutMs });
@@ -156,6 +162,8 @@ export {
156
162
  DECISION_STOP_REASON,
157
163
  isDecisionStopped,
158
164
  isLocalQuestionHandoff,
165
+ UNVERIFIABLE_APPROVAL_REASON,
166
+ unavailableQuestionUnlessSubscriptionRequired,
159
167
  askUser,
160
168
  waitForAnswer,
161
169
  nextLongPollMs,
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  SETUP_FLAGS
3
- } from "./chunk-CVALVGA7.js";
3
+ } from "./chunk-I7B2DNVU.js";
4
4
  import {
5
5
  parseFlags
6
6
  } from "./chunk-GMXKITVA.js";
@@ -14,7 +14,7 @@ import {
14
14
  CLAUDE_CONNECTOR_URL,
15
15
  COWORK_CONNECTOR_LIMIT,
16
16
  COWORK_STANDING_INSTRUCTIONS
17
- } from "./chunk-YROGUPOZ.js";
17
+ } from "./chunk-WWTLAZQA.js";
18
18
 
19
19
  // src/cli/cowork.ts
20
20
  import { readFileSync } from "fs";
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  registerTranscript
3
- } from "./chunk-EAQMWJPG.js";
3
+ } from "./chunk-ZMWX5SDA.js";
4
4
  import {
5
5
  writeDegraded
6
- } from "./chunk-V6QRSU2G.js";
6
+ } from "./chunk-6QZK4O36.js";
7
7
 
8
8
  // src/tool-hook-input.ts
9
9
  var OPTIONAL_TEXT_FIELDS = ["hook_event_name", "session_id", "cwd", "transcript_path", "tool_use_id", "permission_mode"];
@@ -4,14 +4,14 @@ import {
4
4
  } from "./chunk-XHKBHWLX.js";
5
5
  import {
6
6
  readControlMode
7
- } from "./chunk-UOAQ6QMP.js";
7
+ } from "./chunk-VSGKQ3HI.js";
8
8
  import {
9
9
  getMachineId
10
- } from "./chunk-XNCMT7YY.js";
10
+ } from "./chunk-HOIGXMI7.js";
11
11
  import {
12
12
  pusharyDir,
13
13
  resolveGlobalBinary
14
- } from "./chunk-DNJUSFBL.js";
14
+ } from "./chunk-NYBZJ44G.js";
15
15
  import {
16
16
  readJsonSafe,
17
17
  writeFileAtomic,
@@ -20,10 +20,10 @@ import {
20
20
  } from "./chunk-T7E75EKA.js";
21
21
  import {
22
22
  getApiKey
23
- } from "./chunk-V4AQBAVG.js";
23
+ } from "./chunk-SJN64NJX.js";
24
24
  import {
25
25
  getPackageVersion
26
- } from "./chunk-YROGUPOZ.js";
26
+ } from "./chunk-WWTLAZQA.js";
27
27
 
28
28
  // src/daemon-lifecycle.ts
29
29
  import { dirname, join, win32 as win32Path } from "path";
@@ -6,10 +6,10 @@ import {
6
6
  codexHooksJsonIn,
7
7
  isPusharyCodexCommand,
8
8
  resolvePathBinary
9
- } from "./chunk-DNJUSFBL.js";
9
+ } from "./chunk-NYBZJ44G.js";
10
10
  import {
11
11
  getPackageVersion
12
- } from "./chunk-YROGUPOZ.js";
12
+ } from "./chunk-WWTLAZQA.js";
13
13
 
14
14
  // src/codex-app-server.ts
15
15
  import { existsSync, realpathSync } from "fs";
@@ -0,0 +1,45 @@
1
+ import {
2
+ DEFAULT_SESSION
3
+ } from "./chunk-N6IOSZOE.js";
4
+
5
+ // src/answer.ts
6
+ var denyReasonFrom = (value, note) => {
7
+ const trimmed = note?.trim();
8
+ if (trimmed) return `Denied from your phone: ${trimmed}`;
9
+ return value && value !== "no" && value !== "yes" ? `Denied from your phone: ${value}` : "Denied via push notification";
10
+ };
11
+ var isDeferAnswer = (value) => value === "defer";
12
+
13
+ // src/session-notice.ts
14
+ import { existsSync, writeFileSync } from "fs";
15
+ import { tmpdir } from "os";
16
+ import { join } from "path";
17
+ var SUBSCRIPTION_REQUIRED_NOTICE_KIND = "subscription-required";
18
+ var oncePerSession = (kind, sessionId) => {
19
+ try {
20
+ const key = (sessionId || DEFAULT_SESSION).replace(/[^a-zA-Z0-9_-]/g, "");
21
+ const marker = join(tmpdir(), `pushary-${kind}-${key}`);
22
+ if (existsSync(marker)) return false;
23
+ writeFileSync(marker, "1", "utf-8");
24
+ return true;
25
+ } catch {
26
+ return false;
27
+ }
28
+ };
29
+ var subscriptionRequiredNotice = (upgradeUrl) => `[pushary] Your Pushary workspace has no active subscription, so approvals stay with your agent. Subscribe: ${upgradeUrl}`;
30
+ var announceSubscriptionRequired = (upgradeUrl, sessionId) => {
31
+ try {
32
+ if (!oncePerSession(SUBSCRIPTION_REQUIRED_NOTICE_KIND, sessionId)) return;
33
+ process.stderr.write(`${subscriptionRequiredNotice(upgradeUrl)}
34
+ `);
35
+ } catch {
36
+ return;
37
+ }
38
+ };
39
+
40
+ export {
41
+ denyReasonFrom,
42
+ isDeferAnswer,
43
+ oncePerSession,
44
+ announceSubscriptionRequired
45
+ };
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  SETUP_AGENT_CHOICES
3
- } from "./chunk-YROGUPOZ.js";
3
+ } from "./chunk-WWTLAZQA.js";
4
4
 
5
5
  // src/cli/commands.ts
6
6
  var NPX_LATEST_COMMAND = "npx @pushary/agent-hooks@latest";
@@ -3,10 +3,10 @@ import {
3
3
  } from "./chunk-KZERVKTD.js";
4
4
  import {
5
5
  renderCommandHelp
6
- } from "./chunk-B3UXMSMW.js";
6
+ } from "./chunk-65RN6ZCO.js";
7
7
  import {
8
8
  getPackageVersion
9
- } from "./chunk-YROGUPOZ.js";
9
+ } from "./chunk-WWTLAZQA.js";
10
10
 
11
11
  // src/cli/usage.ts
12
12
  var exitOnHelpFlag = (command) => {