pushary 1.9.134 → 1.9.136

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 (133) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/dist/{api-7CZR7B53.js → api-5H25QRNM.js} +11 -11
  3. package/dist/bin/pushary-audit.js +3 -3
  4. package/dist/bin/pushary-bell-hook.js +28 -27
  5. package/dist/bin/pushary-bell.js +10 -10
  6. package/dist/bin/pushary-claude.js +47 -33
  7. package/dist/bin/pushary-clean.js +35 -34
  8. package/dist/bin/pushary-codex-bridge.js +23 -22
  9. package/dist/bin/pushary-codex-hook.js +58 -41
  10. package/dist/bin/pushary-codex-session.js +8 -8
  11. package/dist/bin/pushary-codex.js +16 -15
  12. package/dist/bin/pushary-connect.js +29 -28
  13. package/dist/bin/pushary-cowork.js +5 -5
  14. package/dist/bin/pushary-daemon.js +78 -59
  15. package/dist/bin/pushary-disconnect.js +28 -27
  16. package/dist/bin/pushary-display-hook.js +44 -17
  17. package/dist/bin/pushary-doctor.js +43 -42
  18. package/dist/bin/pushary-elicitation-hook.js +28 -27
  19. package/dist/bin/pushary-gemini-bridge.js +20 -17
  20. package/dist/bin/pushary-gemini-hook.js +32 -31
  21. package/dist/bin/pushary-hook.js +35 -33
  22. package/dist/bin/pushary-login.js +21 -21
  23. package/dist/bin/pushary-logout.js +27 -26
  24. package/dist/bin/pushary-mcp.js +12 -12
  25. package/dist/bin/pushary-mode.js +15 -15
  26. package/dist/bin/pushary-notification-hook.js +27 -26
  27. package/dist/bin/pushary-opencode-hook.js +31 -30
  28. package/dist/bin/pushary-permission-denied-hook.js +35 -33
  29. package/dist/bin/pushary-permission-hook.js +39 -35
  30. package/dist/bin/pushary-post-hook.js +33 -27
  31. package/dist/bin/pushary-prompt-hook.js +27 -26
  32. package/dist/bin/pushary-session-end-hook.js +27 -26
  33. package/dist/bin/pushary-session-start-hook.js +36 -35
  34. package/dist/bin/pushary-setup.js +50 -49
  35. package/dist/bin/pushary-stats.js +5 -5
  36. package/dist/bin/pushary-status.js +33 -32
  37. package/dist/bin/pushary-stop-hook.js +27 -26
  38. package/dist/bin/pushary-stopfailure-hook.js +27 -26
  39. package/dist/bin/pushary-suggestions.js +15 -15
  40. package/dist/bin/pushary-transcript-register.js +17 -17
  41. package/dist/bin/pushary-transcripts.js +25 -25
  42. package/dist/bin/pushary-upgrade.js +41 -40
  43. package/dist/bin/pushary-wait.js +15 -15
  44. package/dist/bin/pushary.js +7 -7
  45. package/dist/{chunk-ZJPBTJED.js → chunk-2E46RALI.js} +6 -6
  46. package/dist/{chunk-ZXJTTV7I.js → chunk-2MVKT7KH.js} +2 -2
  47. package/dist/{chunk-XWUPOTJV.js → chunk-2R4OVJHS.js} +5 -5
  48. package/dist/{chunk-IZJDVOJV.js → chunk-2XDVEBHI.js} +5 -5
  49. package/dist/{chunk-VO55SJSD.js → chunk-3HXOR6E5.js} +11 -8
  50. package/dist/{chunk-GKZMBU6T.js → chunk-3QBI5C6P.js} +2 -2
  51. package/dist/{chunk-RZBV5KTF.js → chunk-43DT3QWZ.js} +18 -5
  52. package/dist/{chunk-LBMCQWRX.js → chunk-4T72AHIA.js} +4 -4
  53. package/dist/{chunk-3TKTZOKG.js → chunk-54TZGPZP.js} +3 -3
  54. package/dist/{chunk-XHC3UNOK.js → chunk-5XDFQ6E3.js} +1 -1
  55. package/dist/{chunk-KIWFCW7M.js → chunk-62GD26OE.js} +2 -2
  56. package/dist/{chunk-RLQDVWZ4.js → chunk-6SWFS4GU.js} +2 -2
  57. package/dist/{chunk-JYCAAQUW.js → chunk-A2YJ2FBR.js} +3 -2
  58. package/dist/{chunk-RVGYXUOC.js → chunk-A4YOXS24.js} +3 -3
  59. package/dist/{chunk-7MIT5C7B.js → chunk-A7R43KOX.js} +3 -3
  60. package/dist/{chunk-MRHGK3Q3.js → chunk-D7AAAD6W.js} +193 -121
  61. package/dist/{chunk-X23GSBOH.js → chunk-DBIPNY7J.js} +3 -3
  62. package/dist/{chunk-M65LPNIO.js → chunk-DL67HX3D.js} +53 -10
  63. package/dist/{chunk-UJZ5YHSR.js → chunk-EJHD44ZN.js} +2 -2
  64. package/dist/{chunk-PHKCTMAV.js → chunk-EQZB5P6I.js} +2 -2
  65. package/dist/{chunk-3DCCMO23.js → chunk-FAF5RDWT.js} +6 -6
  66. package/dist/{chunk-FT5MTCKU.js → chunk-G73ACPR2.js} +2 -2
  67. package/dist/{chunk-QBJVECFY.js → chunk-GACQSKNG.js} +2 -2
  68. package/dist/{chunk-A44MRVWG.js → chunk-GS5FH3C2.js} +3 -3
  69. package/dist/{chunk-EA5EASYT.js → chunk-H2RE3IPK.js} +1 -1
  70. package/dist/{chunk-FJST6ZXC.js → chunk-HKJLPVPZ.js} +5 -5
  71. package/dist/{chunk-HW63OEVM.js → chunk-HY4ATVCT.js} +3 -3
  72. package/dist/{chunk-UZUDMZES.js → chunk-ICCM3DLT.js} +2 -2
  73. package/dist/{chunk-MF5ZQX7C.js → chunk-IHAKBLW2.js} +4 -4
  74. package/dist/{chunk-T6HX6OTY.js → chunk-IKBTG3OT.js} +1 -1
  75. package/dist/{chunk-RGVQOOGD.js → chunk-IRIXOH4U.js} +1 -1
  76. package/dist/{chunk-YNV5GURD.js → chunk-JZJ5CM6X.js} +5 -5
  77. package/dist/{chunk-4EQOCSJW.js → chunk-K7YJBVR3.js} +1 -1
  78. package/dist/{chunk-FTH4TSJ2.js → chunk-KLV2NQVT.js} +4 -4
  79. package/dist/{chunk-YI6RR4JP.js → chunk-KTVMIG32.js} +4 -4
  80. package/dist/{chunk-WZ4SIRMH.js → chunk-KYD3LHFB.js} +1 -1
  81. package/dist/{chunk-J4UNR655.js → chunk-L2GADKFL.js} +6 -6
  82. package/dist/{chunk-I6MI4NWX.js → chunk-L7Q57R4J.js} +10 -10
  83. package/dist/{chunk-MJVDFNP2.js → chunk-LAWI6NQK.js} +1 -1
  84. package/dist/{chunk-BKH4LQJC.js → chunk-LNF36PTF.js} +2 -2
  85. package/dist/{chunk-GQPXW6BL.js → chunk-MENTZJHA.js} +1 -1
  86. package/dist/{chunk-6JY44IZI.js → chunk-MQ3I5OIC.js} +8 -8
  87. package/dist/{chunk-SZYKS433.js → chunk-MQMVJDII.js} +1 -1
  88. package/dist/{chunk-75FJZR2M.js → chunk-N3VCBXI6.js} +3 -3
  89. package/dist/{chunk-EPPH5RMJ.js → chunk-NN43KGTN.js} +4 -4
  90. package/dist/{chunk-PFHRW3LR.js → chunk-NZD6ISCL.js} +3 -3
  91. package/dist/{chunk-QVIDOVT6.js → chunk-O5T53LZ5.js} +42 -25
  92. package/dist/{chunk-V2T5ZDMP.js → chunk-PHA6AFOR.js} +3 -3
  93. package/dist/{chunk-CVBSETPW.js → chunk-PNVTPEO5.js} +3 -3
  94. package/dist/{chunk-PPUIIP3O.js → chunk-POV2TBFA.js} +11 -11
  95. package/dist/{chunk-LR3PIGRS.js → chunk-Q2VV77IY.js} +3 -3
  96. package/dist/{chunk-NYFUU23B.js → chunk-QXATIKHQ.js} +1 -1
  97. package/dist/{chunk-N5P2FLOJ.js → chunk-R7CQF2P3.js} +20 -5
  98. package/dist/{chunk-W4FJR57E.js → chunk-R7KC57PC.js} +2 -2
  99. package/dist/{chunk-VME5NXCM.js → chunk-RPFSVNH6.js} +3 -3
  100. package/dist/{chunk-VRUEKURE.js → chunk-RRUP72V6.js} +1 -1
  101. package/dist/{chunk-237FYIVC.js → chunk-TLK5YLIE.js} +4 -4
  102. package/dist/{chunk-2YS7YEUW.js → chunk-U3OLXV5Z.js} +2 -2
  103. package/dist/chunk-UP5QXEUU.js +31 -0
  104. package/dist/{chunk-L5RSLIBQ.js → chunk-V7ZY7P2X.js} +6 -6
  105. package/dist/{chunk-NSBWFX7H.js → chunk-VBWJ756P.js} +5 -5
  106. package/dist/{chunk-3PGANQ4K.js → chunk-VO77WHK4.js} +6 -6
  107. package/dist/{chunk-C4UH6YPD.js → chunk-VV7WRIQT.js} +4 -4
  108. package/dist/{chunk-42PVKPHM.js → chunk-VVN3RPV6.js} +1 -1
  109. package/dist/{chunk-XMZVQF2J.js → chunk-WM7OY63L.js} +1 -1
  110. package/dist/{chunk-DS7XMYUB.js → chunk-WNT4JHK3.js} +1 -1
  111. package/dist/{chunk-2KN53MPE.js → chunk-WXUEOHYY.js} +5 -5
  112. package/dist/chunk-Y47BMZUR.js +36 -0
  113. package/dist/{chunk-HOPJEPL4.js → chunk-YHX5YF77.js} +4 -4
  114. package/dist/{chunk-KWICQAP7.js → chunk-YOV3LHOL.js} +3 -3
  115. package/dist/{chunk-2U4MCUVZ.js → chunk-YQQGIDBC.js} +1 -1
  116. package/dist/{chunk-UDAXXR43.js → chunk-YURIU333.js} +5 -5
  117. package/dist/{chunk-D2KJKAYM.js → chunk-ZWIJJYN3.js} +4 -4
  118. package/dist/{chunk-PG23ENSN.js → chunk-ZWLSXECG.js} +2 -2
  119. package/dist/{chunk-25UU4Q4V.js → chunk-ZWURBNA3.js} +1 -1
  120. package/dist/{codex-app-cards-switch-D2SODMQN.js → codex-app-cards-switch-Z7TMF2OR.js} +11 -11
  121. package/dist/{codex-card-routing-X2HUY2AV.js → codex-card-routing-LQJEJNTS.js} +2 -2
  122. package/dist/{codex-desktop-launch-CFKIJZVB.js → codex-desktop-launch-HKXGKL3T.js} +1 -1
  123. package/dist/{codex-session-launch-YGH5UUAH.js → codex-session-launch-DTGH7ZKM.js} +20 -19
  124. package/dist/{config-5SNDVPS2.js → config-34VUNHK6.js} +3 -3
  125. package/dist/{global-upgrade-U4V7KWH6.js → global-upgrade-5UAH5DCG.js} +9 -9
  126. package/dist/{identity-DCRNA3J3.js → identity-UFJEWGEU.js} +4 -4
  127. package/dist/{live-capabilities-H7J5ANZ3.js → live-capabilities-BL3SHFWX.js} +4 -4
  128. package/dist/{paths-3DUQGU4V.js → paths-YA5ROXGC.js} +3 -3
  129. package/dist/{reapply-LNVBPUWL.js → reapply-OPQF4DPQ.js} +29 -28
  130. package/dist/src/index.js +34 -32
  131. package/dist/{upgrade-lock-3T7JLBHT.js → upgrade-lock-AZLOOGKV.js} +4 -4
  132. package/dist/{usage-XLJK7OTL.js → usage-ZSV4LFDL.js} +3 -3
  133. package/package.json +1 -1
@@ -27,11 +27,11 @@ import {
27
27
  toolResultErrorText,
28
28
  transcriptTokensIn,
29
29
  transcriptUsageRequest
30
- } from "./chunk-JYCAAQUW.js";
30
+ } from "./chunk-A2YJ2FBR.js";
31
31
  import {
32
32
  branchFor,
33
33
  repoKeyFor
34
- } from "./chunk-RGVQOOGD.js";
34
+ } from "./chunk-IRIXOH4U.js";
35
35
  import {
36
36
  cancelQuestion,
37
37
  clientErrorKindOf,
@@ -39,35 +39,39 @@ import {
39
39
  reportClientError,
40
40
  sendNotification,
41
41
  waitForAnswer
42
- } from "./chunk-J4UNR655.js";
42
+ } from "./chunk-L2GADKFL.js";
43
+ import {
44
+ RETRY_ACK,
45
+ readCommandAck,
46
+ refusedReceiptMessage
47
+ } from "./chunk-Y47BMZUR.js";
43
48
  import {
44
49
  isRetryable,
45
50
  withRetry
46
- } from "./chunk-2KN53MPE.js";
51
+ } from "./chunk-WXUEOHYY.js";
47
52
  import {
48
53
  fetchAsCredential
49
- } from "./chunk-42PVKPHM.js";
54
+ } from "./chunk-VVN3RPV6.js";
50
55
  import {
51
56
  getMachineId
52
- } from "./chunk-DS7XMYUB.js";
57
+ } from "./chunk-WNT4JHK3.js";
53
58
  import {
54
59
  reportedInstallSource
55
- } from "./chunk-BKH4LQJC.js";
60
+ } from "./chunk-LNF36PTF.js";
56
61
  import {
57
62
  IDLE_PROMPT_NOTIFICATION,
58
63
  NOTIFICATION_PUSH_TYPES,
59
64
  POSTTOOLUSE_MIRROR_ONLY_TOOLS,
60
- canonicalJson,
61
65
  claudeConfigDir
62
- } from "./chunk-W4FJR57E.js";
66
+ } from "./chunk-R7KC57PC.js";
63
67
  import {
64
68
  compareVersion,
65
69
  parseVersion
66
- } from "./chunk-A44MRVWG.js";
70
+ } from "./chunk-GS5FH3C2.js";
67
71
  import {
68
72
  getApiKey,
69
73
  getBaseUrl
70
- } from "./chunk-T6HX6OTY.js";
74
+ } from "./chunk-IKBTG3OT.js";
71
75
  import {
72
76
  writeFileAtomic,
73
77
  writeJsonAtomicDurable
@@ -96,7 +100,7 @@ import {
96
100
  resolveAutoResolveOrigin,
97
101
  resolvePolicy,
98
102
  unansweredRefusalReason
99
- } from "./chunk-M65LPNIO.js";
103
+ } from "./chunk-DL67HX3D.js";
100
104
 
101
105
  // src/pending.ts
102
106
  import { join } from "path";
@@ -116,6 +120,7 @@ var commandDirFor = (sessionId, namespace = "") => join(
116
120
  );
117
121
  var legacyCommandFile = (sessionId, namespace = "") => join(commandDirFor(sessionId, namespace), "command.json");
118
122
  var commandFile = (sessionId, id, namespace = "") => join(commandDirFor(sessionId, namespace), `${createHash("sha256").update(id).digest("hex")}.json`);
123
+ var wasRefusedByTheServer = (command) => command.refused === true;
119
124
  var readCommandFile = (file) => {
120
125
  try {
121
126
  if (!lstatSync(file).isFile()) return null;
@@ -132,6 +137,7 @@ var readCommandFile = (file) => {
132
137
  expiresAtMs: value.expiresAtMs,
133
138
  ...value.attempted === true ? { attempted: true } : {},
134
139
  ...typeof value.attemptedAtMs === "number" ? { attemptedAtMs: value.attemptedAtMs } : {},
140
+ ...value.refused === true ? { refused: true } : {},
135
141
  ...typeof value.sessionId === "string" ? { sessionId: value.sessionId } : {},
136
142
  delivered: value.delivered === true,
137
143
  ...value.failed === true || !value.delivered && !value.attempted && value.expiresAtMs <= Date.now() ? { failed: true } : {}
@@ -179,7 +185,7 @@ var pruneExpiredCommandStore = () => {
179
185
  }
180
186
  };
181
187
  var readPendingCommand = (sessionId, namespace = "") => {
182
- const commands = pruneExpiredCommands(commandDirFor(sessionId, namespace));
188
+ const commands = pruneExpiredCommands(commandDirFor(sessionId, namespace)).filter((command) => !wasRefusedByTheServer(command));
183
189
  return commands.find((command) => !command.delivered && !command.failed && !command.attempted) ?? commands[0] ?? null;
184
190
  };
185
191
  var writeNewCommandFile = (file, contents) => {
@@ -228,7 +234,7 @@ var updatePendingCommand = (sessionId, id, update, namespace = "") => {
228
234
  };
229
235
  var markPendingCommandAttempted = (sessionId, id, namespace = "") => {
230
236
  const command = readPendingCommandById(sessionId, id, namespace);
231
- if (!command || command.attempted || command.delivered || command.failed) return false;
237
+ if (!command || command.attempted || command.delivered || command.failed || wasRefusedByTheServer(command)) return false;
232
238
  try {
233
239
  const descriptor = openSync(`${commandFile(sessionId, id, namespace)}.emission`, "wx", 384);
234
240
  try {
@@ -244,13 +250,17 @@ var markPendingCommandAttempted = (sessionId, id, namespace = "") => {
244
250
  var pendingCommandReceipts = (namespace) => {
245
251
  const root = join(commandRoot(), createHash("sha256").update(namespace).digest("hex").slice(0, 16));
246
252
  try {
247
- return readdirSync(root, { withFileTypes: true }).filter((entry) => entry.isDirectory()).flatMap((entry) => pruneExpiredCommands(join(root, entry.name)).filter((command) => command.delivered || command.failed || command.attempted && Date.now() - (command.attemptedAtMs ?? 0) >= COMMAND_RECOVERY.emissionRecoveryGraceMs).map((command) => ({ sessionId: command.sessionId ?? entry.name, command })));
253
+ return readdirSync(root, { withFileTypes: true }).filter((entry) => entry.isDirectory()).flatMap((entry) => pruneExpiredCommands(join(root, entry.name)).filter((command) => !wasRefusedByTheServer(command) && (command.delivered || command.failed || command.attempted && Date.now() - (command.attemptedAtMs ?? 0) >= COMMAND_RECOVERY.emissionRecoveryGraceMs)).map((command) => ({ sessionId: command.sessionId ?? entry.name, command })));
248
254
  } catch {
249
255
  return [];
250
256
  }
251
257
  };
252
258
  var markPendingCommandDelivered = (sessionId, id, namespace = "") => updatePendingCommand(sessionId, id, { delivered: true, failed: void 0 }, namespace);
253
259
  var markPendingCommandFailed = (sessionId, id, namespace = "") => updatePendingCommand(sessionId, id, { delivered: false, failed: true }, namespace);
260
+ var REFUSED_AFTER_DELIVERY = { refused: true };
261
+ var REFUSED_BEFORE_CONFIRMED_DELIVERY = { refused: true, failed: true };
262
+ var refusalTombstoneFor = (command) => command?.delivered ? REFUSED_AFTER_DELIVERY : REFUSED_BEFORE_CONFIRMED_DELIVERY;
263
+ var markPendingCommandRefused = (sessionId, id, namespace = "") => updatePendingCommand(sessionId, id, refusalTombstoneFor(readPendingCommandById(sessionId, id, namespace)), namespace);
254
264
  var removePendingCommand = (sessionId, id, namespace = "") => {
255
265
  if (!readPendingCommandById(sessionId, id, namespace)) return;
256
266
  try {
@@ -350,12 +360,9 @@ var emitHookOutput = async (render, write = writeOutput) => {
350
360
  command.namespace
351
361
  ))
352
362
  continue;
353
- if (await command.acknowledge())
354
- removePendingCommand(
355
- command.sessionId,
356
- command.commandId,
357
- command.namespace
358
- );
363
+ const ack = await command.acknowledge();
364
+ if (ack.outcome === "settled") removePendingCommand(command.sessionId, command.commandId, command.namespace);
365
+ if (ack.outcome === "refused") markPendingCommandRefused(command.sessionId, command.commandId, command.namespace);
359
366
  }
360
367
  };
361
368
 
@@ -788,12 +795,48 @@ var permissionTimeoutDecision = (timeoutAction, paused) => {
788
795
  return codexPass();
789
796
  };
790
797
 
791
- // src/codex-call-decision.ts
792
- import { createHash as createHash4 } from "crypto";
793
- import { existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync as readFileSync4, readdirSync as readdirSync3, rmSync as rmSync2, statSync as statSync3, writeFileSync as writeFileSync4 } from "fs";
794
- import { tmpdir as tmpdir4, userInfo } from "os";
798
+ // src/driver.ts
799
+ var DRIVER_ENV = "PUSHARY_DRIVER";
800
+ var PHONE_DRIVER = "phone";
801
+ var driverFromEnv = (env) => env[DRIVER_ENV] === PHONE_DRIVER ? PHONE_DRIVER : void 0;
802
+ var phoneDrivenEnv = (base) => ({
803
+ ...base,
804
+ [DRIVER_ENV]: PHONE_DRIVER
805
+ });
806
+
807
+ // src/throttle.ts
795
808
  import { join as join4 } from "path";
796
- var CODEX_DECISION_TTL_MS = 60 * 1e3;
809
+ import { tmpdir as tmpdir4 } from "os";
810
+ import { createHash as createHash4 } from "crypto";
811
+ import { existsSync as existsSync3, mkdirSync as mkdirSync3, statSync as statSync3, writeFileSync as writeFileSync4, rmSync as rmSync2 } from "fs";
812
+ var THROTTLE_DIR = join4(tmpdir4(), "pushary-throttle");
813
+ var markerPath = (key) => join4(THROTTLE_DIR, createHash4("sha256").update(key).digest("hex").slice(0, 16));
814
+ var claimThrottle = (key, windowMs) => {
815
+ try {
816
+ const path = markerPath(key);
817
+ if (existsSync3(path) && Date.now() - statSync3(path).mtimeMs < windowMs) return "throttled";
818
+ mkdirSync3(THROTTLE_DIR, { recursive: true });
819
+ writeFileSync4(path, "", "utf-8");
820
+ return "passed";
821
+ } catch {
822
+ return "unavailable";
823
+ }
824
+ };
825
+ var releaseThrottle = (key) => {
826
+ rmSync2(markerPath(key), { force: true });
827
+ };
828
+
829
+ // src/blocked-action-hint.ts
830
+ import { createHash as createHash5 } from "crypto";
831
+ import { existsSync as existsSync4, mkdirSync as mkdirSync5, readFileSync as readFileSync5, readdirSync as readdirSync4, rmSync as rmSync4, statSync as statSync4, utimesSync as utimesSync2 } from "fs";
832
+ import { tmpdir as tmpdir6 } from "os";
833
+ import { join as join6 } from "path";
834
+
835
+ // src/call-decision-store.ts
836
+ import { randomUUID } from "crypto";
837
+ import { lstatSync as lstatSync3, mkdirSync as mkdirSync4, readFileSync as readFileSync4, readdirSync as readdirSync3, renameSync, rmSync as rmSync3, writeFileSync as writeFileSync5 } from "fs";
838
+ import { tmpdir as tmpdir5, userInfo } from "os";
839
+ import { join as join5 } from "path";
797
840
  var userScope = () => {
798
841
  try {
799
842
  const info = userInfo();
@@ -803,8 +846,6 @@ var userScope = () => {
803
846
  return "default";
804
847
  }
805
848
  };
806
- var decisionDir = () => join4(tmpdir4(), `pushary-codex-decisions-${userScope()}`);
807
- var decisionFile = (key) => join4(decisionDir(), key);
808
849
  var PRESENTATION_ONLY_INPUT_KEYS = ["description"];
809
850
  var identifyingToolInput = (toolInput) => {
810
851
  if (!toolInput) return {};
@@ -815,105 +856,128 @@ var identifyingToolInput = (toolInput) => {
815
856
  }
816
857
  return identifying;
817
858
  };
818
- var codexCallKey = (identity) => {
819
- if (!identity.sessionId || !identity.turnId || !identity.toolName) return null;
820
- const material = canonicalJson([
821
- identity.sessionId,
822
- identity.turnId,
823
- identity.toolName,
824
- identifyingToolInput(identity.toolInput)
825
- ]);
826
- return createHash4("sha256").update(material, "utf-8").digest("hex").slice(0, 32);
859
+ var PRIVATE_FOLDER_MODE = 448;
860
+ var PRIVATE_FILE_MODE = 384;
861
+ var GROUP_OR_OTHER_PERMISSIONS = 63;
862
+ var isPrivateFolder = (path) => {
863
+ try {
864
+ const folder = lstatSync3(path);
865
+ if (!folder.isDirectory()) return false;
866
+ if (typeof process.getuid !== "function") return true;
867
+ return folder.uid === process.getuid() && (folder.mode & GROUP_OR_OTHER_PERMISSIONS) === 0;
868
+ } catch {
869
+ return false;
870
+ }
827
871
  };
828
- var parseDecision = (raw) => {
872
+ var privateFolderAt = (path) => {
829
873
  try {
830
- const parsed = JSON.parse(raw);
831
- if (parsed.kind === "approved") {
832
- return typeof parsed.toolUseId === "string" && parsed.toolUseId ? { kind: "approved", toolUseId: parsed.toolUseId } : null;
833
- }
834
- if (parsed.kind === "handled") return { kind: "handled" };
835
- if (parsed.kind === "denied") {
836
- return { kind: "denied", reason: typeof parsed.reason === "string" ? parsed.reason : "" };
837
- }
838
- return null;
874
+ mkdirSync4(path, { recursive: true, mode: PRIVATE_FOLDER_MODE });
839
875
  } catch {
840
876
  return null;
841
877
  }
878
+ return isPrivateFolder(path) ? path : null;
842
879
  };
843
- var sweepExpired = (dir, nowMs) => {
880
+ var removeQuietly = (path) => {
844
881
  try {
845
- for (const name of readdirSync3(dir)) {
846
- const path = join4(dir, name);
847
- try {
848
- if (nowMs - statSync3(path).mtimeMs >= CODEX_DECISION_TTL_MS) rmSync2(path, { force: true });
849
- } catch {
850
- continue;
851
- }
852
- }
882
+ rmSync3(path, { force: true });
853
883
  } catch {
854
884
  return;
855
885
  }
856
886
  };
857
- var recordCodexCallDecision = (key, decision) => {
858
- if (!key) return;
887
+ var reportedUnrecordedDecision = false;
888
+ var reportUnrecordedDecision = (reason) => {
889
+ if (reportedUnrecordedDecision) return;
890
+ reportedUnrecordedDecision = true;
859
891
  try {
860
- const dir = decisionDir();
861
- if (!existsSync3(dir)) mkdirSync3(dir, { recursive: true, mode: 448 });
862
- else sweepExpired(dir, Date.now());
863
- writeFileSync4(decisionFile(key), JSON.stringify(decision), { encoding: "utf-8", mode: 384 });
892
+ process.stderr.write(`[pushary] Could not record this decision for the agent's next hook: ${reason}
893
+ `);
864
894
  } catch {
865
895
  return;
866
896
  }
867
897
  };
868
- var consumeCodexCallDecision = (key) => {
869
- if (!key) return null;
870
- const path = decisionFile(key);
898
+ var parseDecision = (raw) => {
899
+ let parsed;
871
900
  try {
872
- const fresh = Date.now() - statSync3(path).mtimeMs < CODEX_DECISION_TTL_MS;
873
- const raw = readFileSync4(path, "utf-8");
874
- rmSync2(path, { force: true });
875
- return fresh ? parseDecision(raw) : null;
901
+ parsed = JSON.parse(raw);
876
902
  } catch {
877
903
  return null;
878
904
  }
905
+ if (!isRecord(parsed)) return null;
906
+ if (parsed.kind === "approved") {
907
+ return typeof parsed.toolUseId === "string" && parsed.toolUseId ? { kind: "approved", toolUseId: parsed.toolUseId } : null;
908
+ }
909
+ if (parsed.kind === "handled") return { kind: "handled" };
910
+ if (parsed.kind === "denied") {
911
+ return { kind: "denied", reason: typeof parsed.reason === "string" ? parsed.reason : "" };
912
+ }
913
+ return null;
879
914
  };
880
-
881
- // src/driver.ts
882
- var DRIVER_ENV = "PUSHARY_DRIVER";
883
- var PHONE_DRIVER = "phone";
884
- var driverFromEnv = (env) => env[DRIVER_ENV] === PHONE_DRIVER ? PHONE_DRIVER : void 0;
885
- var phoneDrivenEnv = (base) => ({
886
- ...base,
887
- [DRIVER_ENV]: PHONE_DRIVER
888
- });
889
-
890
- // src/throttle.ts
891
- import { join as join5 } from "path";
892
- import { tmpdir as tmpdir5 } from "os";
893
- import { createHash as createHash5 } from "crypto";
894
- import { existsSync as existsSync4, mkdirSync as mkdirSync4, statSync as statSync4, writeFileSync as writeFileSync5, rmSync as rmSync3 } from "fs";
895
- var THROTTLE_DIR = join5(tmpdir5(), "pushary-throttle");
896
- var markerPath = (key) => join5(THROTTLE_DIR, createHash5("sha256").update(key).digest("hex").slice(0, 16));
897
- var claimThrottle = (key, windowMs) => {
915
+ var sweepExpired = (dir, nowMs, ttlMs) => {
898
916
  try {
899
- const path = markerPath(key);
900
- if (existsSync4(path) && Date.now() - statSync4(path).mtimeMs < windowMs) return "throttled";
901
- mkdirSync4(THROTTLE_DIR, { recursive: true });
902
- writeFileSync5(path, "", "utf-8");
903
- return "passed";
917
+ for (const name of readdirSync3(dir)) {
918
+ const path = join5(dir, name);
919
+ try {
920
+ if (nowMs - lstatSync3(path).mtimeMs >= ttlMs) rmSync3(path, { force: true });
921
+ } catch {
922
+ continue;
923
+ }
924
+ }
904
925
  } catch {
905
- return "unavailable";
926
+ return;
906
927
  }
907
928
  };
908
- var releaseThrottle = (key) => {
909
- rmSync3(markerPath(key), { force: true });
929
+ var readClaimed = (claimed, ttlMs) => {
930
+ try {
931
+ const record = lstatSync3(claimed);
932
+ if (!record.isFile() || Date.now() - record.mtimeMs >= ttlMs) return null;
933
+ return parseDecision(readFileSync4(claimed, "utf-8"));
934
+ } catch {
935
+ return null;
936
+ } finally {
937
+ removeQuietly(claimed);
938
+ }
939
+ };
940
+ var callDecisionStore = (name, ttlMs) => {
941
+ const decisionDir = () => join5(tmpdir5(), `pushary-${name}-decisions-${userScope()}`);
942
+ return {
943
+ record: (key, decision) => {
944
+ if (!key) return;
945
+ const dir = privateFolderAt(decisionDir());
946
+ if (!dir) {
947
+ reportUnrecordedDecision(`${decisionDir()} is not a folder only this user can open`);
948
+ return;
949
+ }
950
+ sweepExpired(dir, Date.now(), ttlMs);
951
+ const pending = join5(dir, `${key}.${randomUUID()}.pending`);
952
+ try {
953
+ writeFileSync5(pending, JSON.stringify(decision), { encoding: "utf-8", mode: PRIVATE_FILE_MODE, flag: "wx" });
954
+ renameSync(pending, join5(dir, key));
955
+ } catch (error) {
956
+ removeQuietly(pending);
957
+ reportUnrecordedDecision(error instanceof Error ? error.message : String(error));
958
+ }
959
+ },
960
+ consume: (key) => {
961
+ if (!key) return null;
962
+ const dir = decisionDir();
963
+ if (!isPrivateFolder(dir)) return null;
964
+ const claimed = join5(dir, `${key}.${randomUUID()}.claimed`);
965
+ try {
966
+ renameSync(join5(dir, key), claimed);
967
+ } catch {
968
+ return null;
969
+ }
970
+ return readClaimed(claimed, ttlMs);
971
+ },
972
+ forget: (key) => {
973
+ if (!key) return;
974
+ const dir = decisionDir();
975
+ if (isPrivateFolder(dir)) removeQuietly(join5(dir, key));
976
+ }
977
+ };
910
978
  };
911
979
 
912
980
  // src/blocked-action-hint.ts
913
- import { createHash as createHash6 } from "crypto";
914
- import { existsSync as existsSync5, mkdirSync as mkdirSync5, readFileSync as readFileSync5, readdirSync as readdirSync4, rmSync as rmSync4, statSync as statSync5, utimesSync as utimesSync2 } from "fs";
915
- import { tmpdir as tmpdir6 } from "os";
916
- import { join as join6 } from "path";
917
981
  var BLOCKED_ACTION_HINT_TTL_MS = BLOCKED_ACTION_GRANT_TTL_SECONDS * 1e3;
918
982
  var FINGERPRINT = /^[0-9a-f]{64}$/;
919
983
  var isFingerprint = (value) => typeof value === "string" && FINGERPRINT.test(value);
@@ -928,7 +992,7 @@ var blockedActionFingerprint = ({ source = "claude", sessionId, toolName, toolIn
928
992
  const [canonicalInput] = canonical.toolInputs;
929
993
  if (!canonicalInput) return null;
930
994
  const subject = blockedActionSubject(source, sessionId, canonical.toolName, canonicalInput, cwd);
931
- return subject ? createHash6("sha256").update(blockedActionIdentity(subject), "utf8").digest("hex") : null;
995
+ return subject ? createHash5("sha256").update(blockedActionIdentity(subject), "utf8").digest("hex") : null;
932
996
  };
933
997
  var hintDir = () => join6(tmpdir6(), `pushary-blocked-action-hints-${userScope()}`);
934
998
  var hintFile = (fingerprint) => join6(hintDir(), fingerprint);
@@ -938,7 +1002,7 @@ var sweepFilesOlderThan = (dir, ttlMs, nowMs) => {
938
1002
  for (const name of readdirSync4(dir)) {
939
1003
  const path = join6(dir, name);
940
1004
  try {
941
- if (nowMs - statSync5(path).mtimeMs >= ttlMs) rmSync4(path, { force: true });
1005
+ if (nowMs - statSync4(path).mtimeMs >= ttlMs) rmSync4(path, { force: true });
942
1006
  } catch {
943
1007
  continue;
944
1008
  }
@@ -951,7 +1015,7 @@ var recordBlockedActionHint = (fingerprint, approved) => {
951
1015
  if (!isFingerprint(fingerprint)) return false;
952
1016
  try {
953
1017
  const dir = hintDir();
954
- if (!existsSync5(dir)) mkdirSync5(dir, { recursive: true, mode: 448 });
1018
+ if (!existsSync4(dir)) mkdirSync5(dir, { recursive: true, mode: 448 });
955
1019
  else sweepFilesOlderThan(dir, BLOCKED_ACTION_HINT_TTL_MS, Date.now());
956
1020
  writeFileAtomic(hintFile(fingerprint), JSON.stringify({ fingerprint, ...approved }), 384);
957
1021
  return true;
@@ -962,7 +1026,7 @@ var recordBlockedActionHint = (fingerprint, approved) => {
962
1026
  var readLiveHint = (fingerprint) => {
963
1027
  const path = hintFile(fingerprint);
964
1028
  try {
965
- const stat = statSync5(path);
1029
+ const stat = statSync4(path);
966
1030
  if (!stat.isFile()) return null;
967
1031
  if (isExpired(stat.mtimeMs, Date.now())) {
968
1032
  rmSync4(path, { force: true });
@@ -995,7 +1059,7 @@ var approvalAwaitingRetry = (sessionId) => {
995
1059
  var markApprovalReminded = (approval) => {
996
1060
  const path = hintFile(approval.fingerprint);
997
1061
  try {
998
- const { atime, mtime } = statSync5(path);
1062
+ const { atime, mtime } = statSync4(path);
999
1063
  const { fingerprint, sessionId, toolName } = approval;
1000
1064
  writeFileAtomic(path, JSON.stringify({ fingerprint, sessionId, toolName, reminded: true }), 384);
1001
1065
  utimesSync2(path, atime, mtime);
@@ -1013,7 +1077,7 @@ var forgetBlockedActionHint = (fingerprint) => {
1013
1077
  };
1014
1078
 
1015
1079
  // src/claude-session-record.ts
1016
- import { readFileSync as readFileSync6, readdirSync as readdirSync5, statSync as statSync6 } from "fs";
1080
+ import { readFileSync as readFileSync6, readdirSync as readdirSync5, statSync as statSync5 } from "fs";
1017
1081
  import { join as join7 } from "path";
1018
1082
  var UNREADABLE = { kind: "unreadable" };
1019
1083
  var OTHER_SESSION = { kind: "other_session" };
@@ -1030,7 +1094,7 @@ var isRunning = (pid) => {
1030
1094
  };
1031
1095
  var isSmallRegularFile = (path) => {
1032
1096
  try {
1033
- const stats = statSync6(path);
1097
+ const stats = statSync5(path);
1034
1098
  return stats.isFile() && stats.size <= SESSION_FILE_MAX_BYTES;
1035
1099
  } catch {
1036
1100
  return false;
@@ -1077,13 +1141,13 @@ var claudeSessionVersion = (sessionId, environment = process.env) => {
1077
1141
  };
1078
1142
 
1079
1143
  // src/background-work.ts
1080
- import { createHash as createHash7 } from "crypto";
1081
- import { mkdirSync as mkdirSync6, rmSync as rmSync5, statSync as statSync7, writeFileSync as writeFileSync6 } from "fs";
1144
+ import { createHash as createHash6 } from "crypto";
1145
+ import { mkdirSync as mkdirSync6, rmSync as rmSync5, statSync as statSync6, writeFileSync as writeFileSync6 } from "fs";
1082
1146
  import { tmpdir as tmpdir7 } from "os";
1083
1147
  import { join as join8 } from "path";
1084
1148
  var BACKGROUND_WORK_TTL_MS = 60 * 60 * 1e3;
1085
1149
  var backgroundWorkDir = () => join8(tmpdir7(), "pushary-background-work");
1086
- var markerFor = (sessionKey) => join8(backgroundWorkDir(), createHash7("sha256").update(sessionKey).digest("hex"));
1150
+ var markerFor = (sessionKey) => join8(backgroundWorkDir(), createHash6("sha256").update(sessionKey).digest("hex"));
1087
1151
  var hasRunningBackgroundTasks = (backgroundTasks) => Array.isArray(backgroundTasks) && backgroundTasks.length > 0;
1088
1152
  var noteBackgroundWork = (sessionKey, running) => {
1089
1153
  try {
@@ -1099,7 +1163,7 @@ var noteBackgroundWork = (sessionKey, running) => {
1099
1163
  };
1100
1164
  var isWaitingOnBackgroundWork = (sessionKey, nowMs = Date.now()) => {
1101
1165
  try {
1102
- return nowMs - statSync7(markerFor(sessionKey)).mtimeMs < BACKGROUND_WORK_TTL_MS;
1166
+ return nowMs - statSync6(markerFor(sessionKey)).mtimeMs < BACKGROUND_WORK_TTL_MS;
1103
1167
  } catch {
1104
1168
  return false;
1105
1169
  }
@@ -1107,7 +1171,7 @@ var isWaitingOnBackgroundWork = (sessionKey, nowMs = Date.now()) => {
1107
1171
 
1108
1172
  // src/events.ts
1109
1173
  import { basename, join as join9 } from "path";
1110
- import { existsSync as existsSync6, mkdirSync as mkdirSync7, readFileSync as readFileSync7, statSync as statSync8, writeFileSync as writeFileSync7 } from "fs";
1174
+ import { existsSync as existsSync5, mkdirSync as mkdirSync7, readFileSync as readFileSync7, statSync as statSync7, writeFileSync as writeFileSync7 } from "fs";
1111
1175
  import { tmpdir as tmpdir8 } from "os";
1112
1176
  var INTENT_DIR = join9(tmpdir8(), "pushary-intent");
1113
1177
  var INTENT_MAX_BYTES = 2048;
@@ -1126,7 +1190,7 @@ var saveLastPrompt = (sessionId, prompt) => {
1126
1190
  var readLastPrompt = (sessionId) => {
1127
1191
  try {
1128
1192
  const path = intentFile(sessionId);
1129
- if (isDefaultSession(sessionId) && statSync8(path).mtimeMs < Date.now() - INTENT_GRACE_MS) return void 0;
1193
+ if (isDefaultSession(sessionId) && statSync7(path).mtimeMs < Date.now() - INTENT_GRACE_MS) return void 0;
1130
1194
  const value = readFileSync7(path, "utf-8").trim();
1131
1195
  return value.length > 0 ? value : void 0;
1132
1196
  } catch {
@@ -1191,7 +1255,7 @@ var POLICY_CACHE_TTL_MS = 5 * 60 * 1e3;
1191
1255
  var readFreshCachedPolicy = (apiKey, agentType, sessionId) => {
1192
1256
  const request = policyRequestFor(agentType);
1193
1257
  const path = policyCacheFile(apiKey, request.agent, request.repoAware, sessionId);
1194
- if (!existsSync6(path)) return null;
1258
+ if (!existsSync5(path)) return null;
1195
1259
  const cached = JSON.parse(readFileSync7(path, "utf-8"));
1196
1260
  if (!isCachedPolicyRead(cached)) return null;
1197
1261
  if ((cached._sessionId ?? null) !== (sessionId ?? null)) return null;
@@ -1266,7 +1330,7 @@ var detectInstallMode = () => (process.argv[1] ?? "").includes("_npx") ? "npx" :
1266
1330
  var acknowledgePendingCommand = async (apiKey, baseUrl, commandId, sessionId, machineId, timeoutMs, state = "delivered", signal) => {
1267
1331
  try {
1268
1332
  signal?.throwIfAborted();
1269
- if (timeoutMs <= 0) return false;
1333
+ if (timeoutMs <= 0) return RETRY_ACK;
1270
1334
  const response = await fetchAsCredential(apiKey, `${baseUrl}/api/agent/command/ack`, {
1271
1335
  method: "POST",
1272
1336
  headers: {
@@ -1282,9 +1346,12 @@ var acknowledgePendingCommand = async (apiKey, baseUrl, commandId, sessionId, ma
1282
1346
  }),
1283
1347
  signal: AbortSignal.any([AbortSignal.timeout(timeoutMs), ...signal ? [signal] : []])
1284
1348
  });
1285
- return response.ok;
1349
+ const ack = await readCommandAck(response);
1350
+ if (ack.outcome === "refused") process.stderr.write(`${refusedReceiptMessage(commandId, ack.status)}
1351
+ `);
1352
+ return ack;
1286
1353
  } catch {
1287
- return false;
1354
+ return RETRY_ACK;
1288
1355
  }
1289
1356
  };
1290
1357
  var ACK_RESERVE_MS = 1500;
@@ -1358,7 +1425,11 @@ var reportEvent = async (event, options = {}) => {
1358
1425
  failed ? "failed" : storedCommand.attempted && !storedCommand.delivered ? "uncertain" : "delivered",
1359
1426
  options.signal
1360
1427
  );
1361
- if (!acknowledged) return withoutPending();
1428
+ if (acknowledged.outcome === "retry") return withoutPending();
1429
+ if (acknowledged.outcome === "refused") {
1430
+ markPendingCommandRefused(sessionKey, storedCommand.id, receiptNamespace);
1431
+ return withoutPending();
1432
+ }
1362
1433
  if (storedCommand.delivered || storedCommand.attempted || failed) {
1363
1434
  removePendingCommand(sessionKey, storedCommand.id, receiptNamespace);
1364
1435
  return withoutPending();
@@ -1391,7 +1462,8 @@ var reportEvent = async (event, options = {}) => {
1391
1462
  "delivered",
1392
1463
  options.signal
1393
1464
  );
1394
- if (!acknowledged || !markPendingCommandDelivered(sessionKey, result.pendingCommandId, receiptNamespace)) {
1465
+ if (acknowledged.outcome === "refused") markPendingCommandRefused(sessionKey, result.pendingCommandId, receiptNamespace);
1466
+ if (acknowledged.outcome !== "settled" || !markPendingCommandDelivered(sessionKey, result.pendingCommandId, receiptNamespace)) {
1395
1467
  return withoutPending();
1396
1468
  }
1397
1469
  }
@@ -1734,6 +1806,7 @@ export {
1734
1806
  pendingCommandReceipts,
1735
1807
  markPendingCommandDelivered,
1736
1808
  markPendingCommandFailed,
1809
+ markPendingCommandRefused,
1737
1810
  removePendingCommand,
1738
1811
  emitHookOutput,
1739
1812
  PHONE_DRIVER,
@@ -1759,9 +1832,8 @@ export {
1759
1832
  codexQuestionLimitDecision,
1760
1833
  permissionTimeoutDecision,
1761
1834
  userScope,
1762
- codexCallKey,
1763
- recordCodexCallDecision,
1764
- consumeCodexCallDecision,
1835
+ identifyingToolInput,
1836
+ callDecisionStore,
1765
1837
  blockedActionFingerprint,
1766
1838
  sweepFilesOlderThan,
1767
1839
  recordBlockedActionHint,
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  realpath
3
- } from "./chunk-UJZ5YHSR.js";
3
+ } from "./chunk-EJHD44ZN.js";
4
4
  import {
5
5
  findNativeClaudeInstall
6
6
  } from "./chunk-CYTPQDQY.js";
@@ -13,10 +13,10 @@ import {
13
13
  resolvePackageVersionFromBinary,
14
14
  resolvePathBinary,
15
15
  resolvePathBinaryAsync
16
- } from "./chunk-A44MRVWG.js";
16
+ } from "./chunk-GS5FH3C2.js";
17
17
  import {
18
18
  MANAGED_PROVIDERS
19
- } from "./chunk-M65LPNIO.js";
19
+ } from "./chunk-DL67HX3D.js";
20
20
 
21
21
  // src/live-capabilities.ts
22
22
  import { homedir } from "os";