@pushary/agent-hooks 1.7.1 → 1.7.3

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 (106) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/data/cursor-plugin/CHANGELOG.md +1 -0
  3. package/data/cursor-plugin/scripts/pushary-gate.mjs +3 -3
  4. package/data/cursor-plugin/scripts/pushary-gate.test.mjs +23 -1
  5. package/data/vscode-plugin/CHANGELOG.md +1 -0
  6. package/data/vscode-plugin/scripts/pushary-gate.mjs +3 -3
  7. package/dist/bin/pushary-bell-hook.js +23 -23
  8. package/dist/bin/pushary-bell.js +10 -10
  9. package/dist/bin/pushary-claude.js +48 -37
  10. package/dist/bin/pushary-clean.js +107 -60
  11. package/dist/bin/pushary-codex-bridge.js +18 -17
  12. package/dist/bin/pushary-codex-hook.js +53 -39
  13. package/dist/bin/pushary-codex.js +14 -12
  14. package/dist/bin/pushary-connect.js +16 -16
  15. package/dist/bin/pushary-cowork.js +5 -5
  16. package/dist/bin/pushary-daemon.js +27 -27
  17. package/dist/bin/pushary-disconnect.js +19 -17
  18. package/dist/bin/pushary-doctor.js +63 -35
  19. package/dist/bin/pushary-elicitation-hook.js +35 -26
  20. package/dist/bin/pushary-gemini-bridge.js +12 -12
  21. package/dist/bin/pushary-gemini-hook.js +47 -32
  22. package/dist/bin/pushary-hook.js +34 -32
  23. package/dist/bin/pushary-login.js +16 -16
  24. package/dist/bin/pushary-logout.js +8 -8
  25. package/dist/bin/pushary-mcp.js +9 -8
  26. package/dist/bin/pushary-mode.js +10 -10
  27. package/dist/bin/pushary-notification-hook.js +29 -27
  28. package/dist/bin/pushary-opencode-hook.js +46 -32
  29. package/dist/bin/pushary-permission-denied-hook.js +34 -32
  30. package/dist/bin/pushary-permission-hook.js +33 -31
  31. package/dist/bin/pushary-post-hook.js +29 -27
  32. package/dist/bin/pushary-prompt-hook.js +29 -27
  33. package/dist/bin/pushary-session-end-hook.js +29 -27
  34. package/dist/bin/pushary-session-start-hook.js +33 -31
  35. package/dist/bin/pushary-setup.js +69 -56
  36. package/dist/bin/pushary-stats.js +5 -5
  37. package/dist/bin/pushary-status.js +19 -19
  38. package/dist/bin/pushary-stop-hook.js +29 -27
  39. package/dist/bin/pushary-stopfailure-hook.js +29 -27
  40. package/dist/bin/pushary-suggestions.js +5 -5
  41. package/dist/bin/pushary-transcript-register.js +19 -19
  42. package/dist/bin/pushary-transcripts.js +7 -7
  43. package/dist/bin/pushary-upgrade.js +20 -20
  44. package/dist/bin/pushary-wait.js +10 -10
  45. package/dist/bin/pushary.js +6 -6
  46. package/dist/{chunk-MWVCDP64.js → chunk-3EUBQARL.js} +1 -1
  47. package/dist/{chunk-3QORH5EJ.js → chunk-5GB22QYL.js} +6 -4
  48. package/dist/chunk-5OP5MQI7.js +45 -0
  49. package/dist/{chunk-6PV2JQ64.js → chunk-5S2VBSJI.js} +2 -2
  50. package/dist/{chunk-UPKP67TA.js → chunk-62CVUUNU.js} +2 -2
  51. package/dist/{chunk-42XR73I2.js → chunk-62VT3BPH.js} +35 -35
  52. package/dist/chunk-6JQFIHZC.js +99 -0
  53. package/dist/{chunk-2FU43B5Q.js → chunk-7OA34PNW.js} +2 -2
  54. package/dist/{chunk-7GI6QC3M.js → chunk-7RUJKCR4.js} +2 -2
  55. package/dist/{chunk-H6JV44TE.js → chunk-AWVNAYGD.js} +1 -1
  56. package/dist/{chunk-XMN37YJH.js → chunk-BGBLLHZ4.js} +1 -1
  57. package/dist/{chunk-VCMFKZON.js → chunk-BIH2Y2HP.js} +2 -2
  58. package/dist/{chunk-YZKS3ME3.js → chunk-D6OODZCM.js} +2 -2
  59. package/dist/{chunk-JTQDP4ZL.js → chunk-DUQO4C4I.js} +3 -3
  60. package/dist/{chunk-3QE3BHBW.js → chunk-EADXTSHE.js} +2 -2
  61. package/dist/{chunk-43SSQCWO.js → chunk-EEEJ2JIL.js} +3 -3
  62. package/dist/{chunk-4C4NRDIC.js → chunk-EUGNVLMG.js} +1 -1
  63. package/dist/{chunk-WSC53O7I.js → chunk-G3OMOTJ4.js} +3 -3
  64. package/dist/{chunk-XFNVG3OV.js → chunk-G4GLCLKK.js} +44 -9
  65. package/dist/{chunk-62TEPRWQ.js → chunk-G62JL2NT.js} +1 -1
  66. package/dist/{chunk-QPR33SHS.js → chunk-G6RRVJOG.js} +6 -6
  67. package/dist/{chunk-Y6GKOZBT.js → chunk-IPSRMLCJ.js} +1 -1
  68. package/dist/{chunk-MCNS4725.js → chunk-IRZG3HEU.js} +1 -1
  69. package/dist/{chunk-55RO62FV.js → chunk-IWJKSIB4.js} +1 -1
  70. package/dist/{chunk-NID4UZDN.js → chunk-KEL5MXRL.js} +1 -1
  71. package/dist/{chunk-BYVB3RX3.js → chunk-KJXUIPS4.js} +2 -2
  72. package/dist/{chunk-SNWNA3MQ.js → chunk-LHC45YUC.js} +1 -1
  73. package/dist/{chunk-XPTCPU6V.js → chunk-LPNJLKBQ.js} +1 -1
  74. package/dist/{chunk-WZWXBTRF.js → chunk-MIXWMA73.js} +3 -3
  75. package/dist/{chunk-454KMJHF.js → chunk-MZBRL5H5.js} +34 -1
  76. package/dist/{chunk-KSNAAMU5.js → chunk-N5MZH5A6.js} +4 -4
  77. package/dist/chunk-N6IOSZOE.js +185 -0
  78. package/dist/{chunk-XPMOCJHN.js → chunk-NOI3XTCY.js} +1 -1
  79. package/dist/{chunk-NAAZCNKI.js → chunk-O3JDWNLU.js} +351 -37
  80. package/dist/{chunk-7GYGVQBU.js → chunk-OTNOANMX.js} +8 -8
  81. package/dist/{chunk-P5CBNSYL.js → chunk-OVC5QXN5.js} +1 -1
  82. package/dist/{chunk-O5JD7UI5.js → chunk-P2VIBGXA.js} +3 -3
  83. package/dist/{chunk-3T35OE7C.js → chunk-PRNFSORD.js} +4 -4
  84. package/dist/{chunk-OAL44FM2.js → chunk-R6OYCUNK.js} +1 -1
  85. package/dist/{chunk-6SJQ442W.js → chunk-RLW3JWDY.js} +2 -2
  86. package/dist/{chunk-SVEMF2VO.js → chunk-TCVMK6GI.js} +4 -4
  87. package/dist/{chunk-O5PRIYFZ.js → chunk-U4TMAANR.js} +3 -0
  88. package/dist/{chunk-WM3U2RUK.js → chunk-U6RVDUJ5.js} +75 -232
  89. package/dist/{chunk-KBLIVEFJ.js → chunk-UOQRM7JQ.js} +5 -5
  90. package/dist/{chunk-NQFGVAT6.js → chunk-VE7TXLR4.js} +19 -2
  91. package/dist/{chunk-IGBDOZUO.js → chunk-VKVSRCBO.js} +1 -1
  92. package/dist/{chunk-ZEMSCGDD.js → chunk-VYPJHGG6.js} +8 -3
  93. package/dist/{chunk-YQN7S57T.js → chunk-WGMW7OXW.js} +6 -6
  94. package/dist/{chunk-OTZHT34O.js → chunk-WPX67AKT.js} +1 -1
  95. package/dist/{chunk-XDASEYRS.js → chunk-WTJM7PSL.js} +3 -3
  96. package/dist/{chunk-Q7QRR6YL.js → chunk-Y4ZH64UK.js} +2 -2
  97. package/dist/{chunk-ZIIYTQHC.js → chunk-YC27PROF.js} +12 -4
  98. package/dist/{chunk-AHWJHLG5.js → chunk-YPUBA2FL.js} +7 -5
  99. package/dist/{chunk-EIZW73P5.js → chunk-YYCTF4Q5.js} +1 -1
  100. package/dist/{chunk-AAGLDXH4.js → chunk-ZKJ34T2F.js} +5 -5
  101. package/dist/{reapply-66GQIBXN.js → reapply-PH5T4BGR.js} +9 -9
  102. package/dist/src/index.d.ts +1 -0
  103. package/dist/src/index.js +22 -20
  104. package/package.json +2 -1
  105. package/dist/chunk-JMBG47WV.js +0 -336
  106. package/dist/chunk-YHG74UFF.js +0 -12
@@ -30,6 +30,8 @@ var truthyLevel = (value) => {
30
30
  const parsed = Number(value);
31
31
  return Number.isFinite(parsed) ? parsed : 1;
32
32
  };
33
+ var ANSI_RESET = "\x1B[0m";
34
+ var ansiPainter = (enabled) => (open) => (text) => enabled() ? `${open}${text}${ANSI_RESET}` : text;
33
35
  var useColor = (context = {}, stream = process.stdout) => {
34
36
  const forced = truthyLevel(process.env.FORCE_COLOR);
35
37
  if (forced !== void 0) return forced > 0;
@@ -103,6 +105,7 @@ var createIo = (stream = process.stdout, context = {}) => {
103
105
  export {
104
106
  clipToRow,
105
107
  terminalColumns,
108
+ ansiPainter,
106
109
  useColor,
107
110
  createIo
108
111
  };
@@ -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-4C4NRDIC.js";
16
+ } from "./chunk-EUGNVLMG.js";
5
17
  import {
6
18
  cancelQuestion,
7
19
  isPolicyConfig,
8
20
  sendNotification,
9
21
  waitForAnswer
10
- } from "./chunk-ZIIYTQHC.js";
22
+ } from "./chunk-YC27PROF.js";
11
23
  import {
12
24
  clientErrorKindOf,
13
25
  reportClientError
14
- } from "./chunk-KSNAAMU5.js";
26
+ } from "./chunk-N5MZH5A6.js";
15
27
  import {
16
28
  withRetry
17
- } from "./chunk-NQFGVAT6.js";
29
+ } from "./chunk-VE7TXLR4.js";
18
30
  import {
19
31
  getMachineId
20
- } from "./chunk-P5CBNSYL.js";
32
+ } from "./chunk-OVC5QXN5.js";
21
33
  import {
22
34
  reportedInstallSource
23
- } from "./chunk-UPKP67TA.js";
35
+ } from "./chunk-62CVUUNU.js";
24
36
  import {
25
37
  getApiKey,
26
38
  getBaseUrl
27
- } from "./chunk-OAL44FM2.js";
39
+ } from "./chunk-R6OYCUNK.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-XFNVG3OV.js";
63
+ } from "./chunk-G4GLCLKK.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,
@@ -3,28 +3,28 @@ import {
3
3
  addPusharyToolPermissions,
4
4
  isPusharyHook,
5
5
  removeClaudeMcpServers
6
- } from "./chunk-WSC53O7I.js";
6
+ } from "./chunk-G3OMOTJ4.js";
7
7
  import {
8
8
  CLAUDE_HOOK_EVENTS,
9
9
  addGeminiMcpServer,
10
10
  removeGeminiMcpServer
11
- } from "./chunk-WZWXBTRF.js";
11
+ } from "./chunk-MIXWMA73.js";
12
12
  import {
13
13
  CLAUDE_CODE_AGENT,
14
14
  parseApplyPatchFiles
15
- } from "./chunk-WM3U2RUK.js";
15
+ } from "./chunk-U6RVDUJ5.js";
16
16
  import {
17
17
  guardBinary,
18
18
  holdBudgetOf,
19
19
  registerHookEntry
20
- } from "./chunk-454KMJHF.js";
20
+ } from "./chunk-MZBRL5H5.js";
21
21
  import {
22
22
  PHONE_ANSWERED_TOOLS,
23
23
  hookCompatibleProfile,
24
24
  hookCompatibleProfileBySource,
25
25
  toToolLookup,
26
26
  withHookHoldBudget
27
- } from "./chunk-XFNVG3OV.js";
27
+ } from "./chunk-G4GLCLKK.js";
28
28
 
29
29
  // src/compatible-config.ts
30
30
  import { homedir } from "os";
@@ -1,9 +1,12 @@
1
+ import {
2
+ keyCheckFromResponse
3
+ } from "./chunk-EEEJ2JIL.js";
1
4
  import {
2
5
  installNetworkDefaults
3
- } from "./chunk-JTQDP4ZL.js";
6
+ } from "./chunk-DUQO4C4I.js";
4
7
  import {
5
8
  getBaseUrl
6
- } from "./chunk-OAL44FM2.js";
9
+ } from "./chunk-R6OYCUNK.js";
7
10
 
8
11
  // src/retry.ts
9
12
  var isRetryable = (err) => {
@@ -35,6 +38,17 @@ var withRetry = async (fn, options = {}) => {
35
38
 
36
39
  // src/mcp-http.ts
37
40
  installNetworkDefaults();
41
+ var SUBSCRIPTION_REQUIRED_MESSAGE = "Pushary MCP error: 403 Subscription required";
42
+ var SubscriptionRequiredError = class extends Error {
43
+ plan;
44
+ upgradeUrl;
45
+ constructor(plan, upgradeUrl) {
46
+ super(SUBSCRIPTION_REQUIRED_MESSAGE);
47
+ this.name = "SubscriptionRequiredError";
48
+ this.plan = plan;
49
+ this.upgradeUrl = upgradeUrl;
50
+ }
51
+ };
38
52
  var parseSseMessages = (body) => {
39
53
  const messages = [];
40
54
  for (const event of body.split(/\r?\n\r?\n/)) {
@@ -77,6 +91,8 @@ var sendMcpRequest = async (apiKey, message, options = {}) => {
77
91
  body: JSON.stringify(message),
78
92
  signal: options.timeoutMs ? AbortSignal.any([AbortSignal.timeout(options.timeoutMs), ...options.signal ? [options.signal] : []]) : options.signal
79
93
  });
94
+ const lock = response.ok ? null : await keyCheckFromResponse(response, baseUrl);
95
+ if (lock?.kind === "locked") throw new SubscriptionRequiredError(lock.plan, lock.upgradeUrl);
80
96
  const body = await response.text();
81
97
  const contentType = response.headers.get("content-type");
82
98
  let data = null;
@@ -115,6 +131,7 @@ var callMcpTool = async (apiKey, toolName, params, options = {}) => {
115
131
 
116
132
  export {
117
133
  withRetry,
134
+ SubscriptionRequiredError,
118
135
  parseSseMessages,
119
136
  sendMcpRequest,
120
137
  callMcpTool
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  HOOK_HOLD_BUDGET_ENV,
3
3
  hookHoldBudgetProof
4
- } from "./chunk-XFNVG3OV.js";
4
+ } from "./chunk-G4GLCLKK.js";
5
5
 
6
6
  // src/hold-budget.ts
7
7
  var takeHookHoldBudget = (args = process.argv.slice(2), env = process.env) => {
@@ -1,6 +1,9 @@
1
+ import {
2
+ NpmMissingError
3
+ } from "./chunk-MZBRL5H5.js";
1
4
  import {
2
5
  NPX_LATEST_COMMAND
3
- } from "./chunk-EIZW73P5.js";
6
+ } from "./chunk-YYCTF4Q5.js";
4
7
 
5
8
  // src/setup/agent-failure.ts
6
9
  var AgentConfigError = class extends Error {
@@ -21,12 +24,14 @@ var AGENT_FAILURE_DETAILS = {
21
24
  var describedAgentFailure = (agent, kind) => ({
22
25
  agent,
23
26
  kind,
24
- detail: AGENT_FAILURE_DETAILS[kind]
27
+ detail: AGENT_FAILURE_DETAILS[kind],
28
+ remedy: "retry-agent"
25
29
  });
26
30
  var agentFailureFromError = (agent, error, describeError) => ({
27
31
  agent,
28
32
  kind: error instanceof AgentConfigError ? error.kind : "installer-error",
29
- detail: describeError(error)
33
+ detail: describeError(error),
34
+ remedy: error instanceof NpmMissingError ? "install-node" : "retry-agent"
30
35
  });
31
36
  var retryAgentCommand = (agent) => `${NPX_LATEST_COMMAND} setup --agents ${agent}`;
32
37
  var REMAINING_AGENTS_CONTINUE = "Other agents will continue. Re-run setup to retry.";
@@ -2,27 +2,27 @@ import {
2
2
  detectProviderLiveCapabilities,
3
3
  hasSpawnProvider,
4
4
  resolveControlPolicy
5
- } from "./chunk-Q7QRR6YL.js";
5
+ } from "./chunk-Y4ZH64UK.js";
6
6
  import {
7
7
  daemonHasRemoteProof,
8
8
  daemonServiceManagerAvailable,
9
9
  hasDaemonServiceDefinition,
10
10
  readDaemonNeedsLogin,
11
11
  readDaemonStatus
12
- } from "./chunk-AAGLDXH4.js";
12
+ } from "./chunk-ZKJ34T2F.js";
13
13
  import {
14
14
  readControlMode,
15
15
  readKeySource
16
- } from "./chunk-YZKS3ME3.js";
16
+ } from "./chunk-D6OODZCM.js";
17
17
  import {
18
18
  getMachineId
19
- } from "./chunk-P5CBNSYL.js";
19
+ } from "./chunk-OVC5QXN5.js";
20
20
  import {
21
21
  getBaseUrl
22
- } from "./chunk-OAL44FM2.js";
22
+ } from "./chunk-R6OYCUNK.js";
23
23
  import {
24
24
  getPackageVersion
25
- } from "./chunk-XFNVG3OV.js";
25
+ } from "./chunk-G4GLCLKK.js";
26
26
 
27
27
  // src/machines.ts
28
28
  import { hostname } from "os";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  getBaseUrl
3
- } from "./chunk-OAL44FM2.js";
3
+ } from "./chunk-R6OYCUNK.js";
4
4
 
5
5
  // src/transcript-api.ts
6
6
  var fetchTranscriptRecipients = async (apiKey, baseUrl = getBaseUrl()) => {