@pushary/agent-hooks 1.9.72 → 1.9.75

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 (126) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/dist/{api-K4UGZNOE.js → api-L3BIISGS.js} +10 -10
  3. package/dist/bin/pushary-bell-hook.js +26 -25
  4. package/dist/bin/pushary-bell.js +14 -14
  5. package/dist/bin/pushary-claude.js +54 -82
  6. package/dist/bin/pushary-clean.js +37 -37
  7. package/dist/bin/pushary-codex-bridge.js +24 -24
  8. package/dist/bin/pushary-codex-hook.js +35 -34
  9. package/dist/bin/pushary-codex-session.js +8 -8
  10. package/dist/bin/pushary-codex.js +15 -15
  11. package/dist/bin/pushary-connect.js +26 -26
  12. package/dist/bin/pushary-cowork.js +5 -5
  13. package/dist/bin/pushary-daemon.js +1292 -63
  14. package/dist/bin/pushary-disconnect.js +31 -31
  15. package/dist/bin/pushary-display-hook.d.ts +1 -0
  16. package/dist/bin/pushary-display-hook.js +57 -0
  17. package/dist/bin/pushary-doctor.js +50 -50
  18. package/dist/bin/pushary-elicitation-hook.js +28 -27
  19. package/dist/bin/pushary-gemini-bridge.js +16 -16
  20. package/dist/bin/pushary-gemini-hook.js +30 -29
  21. package/dist/bin/pushary-hook.js +30 -29
  22. package/dist/bin/pushary-login.js +20 -20
  23. package/dist/bin/pushary-logout.js +30 -30
  24. package/dist/bin/pushary-mcp.js +14 -14
  25. package/dist/bin/pushary-mode.js +18 -18
  26. package/dist/bin/pushary-notification-hook.js +25 -24
  27. package/dist/bin/pushary-opencode-hook.js +29 -28
  28. package/dist/bin/pushary-permission-denied-hook.js +30 -29
  29. package/dist/bin/pushary-permission-hook.js +30 -29
  30. package/dist/bin/pushary-post-hook.js +31 -29
  31. package/dist/bin/pushary-prompt-hook.js +31 -29
  32. package/dist/bin/pushary-session-end-hook.js +27 -26
  33. package/dist/bin/pushary-session-start-hook.js +35 -34
  34. package/dist/bin/pushary-setup.js +61 -60
  35. package/dist/bin/pushary-stats.js +6 -6
  36. package/dist/bin/pushary-status.js +31 -31
  37. package/dist/bin/pushary-stop-hook.js +31 -26
  38. package/dist/bin/pushary-stopfailure-hook.js +25 -24
  39. package/dist/bin/pushary-suggestions.js +18 -18
  40. package/dist/bin/pushary-transcript-register.js +17 -16
  41. package/dist/bin/pushary-transcripts.js +19 -19
  42. package/dist/bin/pushary-upgrade.js +45 -45
  43. package/dist/bin/pushary-wait.js +18 -18
  44. package/dist/bin/pushary.js +7 -7
  45. package/dist/{chunk-QFF3QVIV.js → chunk-2DB7TBA7.js} +2 -2
  46. package/dist/{chunk-N4NSX5MB.js → chunk-2J4IVUEA.js} +3 -2
  47. package/dist/{chunk-E56VJY23.js → chunk-32JJ5KV3.js} +4 -4
  48. package/dist/{chunk-VFF7TP5B.js → chunk-3PPL64AG.js} +3 -3
  49. package/dist/{chunk-KFGRDWAW.js → chunk-3SAF4QXU.js} +4 -4
  50. package/dist/{chunk-TM4JH7YR.js → chunk-4DH54LS7.js} +1 -1
  51. package/dist/{chunk-RXR44U2J.js → chunk-4GWKKUQ5.js} +5 -5
  52. package/dist/{chunk-4U7F7VMC.js → chunk-4T25BBBO.js} +4 -4
  53. package/dist/{chunk-BMVWGPYB.js → chunk-53D6FFVE.js} +1 -1
  54. package/dist/{chunk-EW7CZHYP.js → chunk-5EQOUZYE.js} +2 -2
  55. package/dist/{chunk-PIO66HPG.js → chunk-6BFLDHPM.js} +3 -3
  56. package/dist/{chunk-D6COE26P.js → chunk-7FNSQMQ2.js} +4 -4
  57. package/dist/{chunk-R4B5AVFC.js → chunk-7O54GE5C.js} +3 -3
  58. package/dist/{chunk-GUJBS6TK.js → chunk-AUDYAVNM.js} +3 -3
  59. package/dist/{chunk-5XYYBB5Z.js → chunk-BGEUAJYL.js} +2 -2
  60. package/dist/{chunk-2KDTIUC7.js → chunk-BRG4XS2H.js} +1 -1
  61. package/dist/{chunk-EPARIXRC.js → chunk-CI6MDBQB.js} +7 -7
  62. package/dist/{chunk-LKXOSW2L.js → chunk-CLHRSK5L.js} +4 -4
  63. package/dist/{chunk-5KQN7AIL.js → chunk-D4X4BQVQ.js} +10 -10
  64. package/dist/{chunk-ZWCKS65X.js → chunk-DARPCQPY.js} +2 -2
  65. package/dist/{chunk-QDISUNK3.js → chunk-DCHZ5JOI.js} +6 -6
  66. package/dist/{chunk-N7A44RIA.js → chunk-DOG3BQFO.js} +6 -6
  67. package/dist/{chunk-ILWEGI4H.js → chunk-DUOBFECC.js} +1 -1
  68. package/dist/{chunk-LT2DOCFI.js → chunk-DX65IDBI.js} +1 -1
  69. package/dist/{chunk-37OD7FUI.js → chunk-E5XYFIYL.js} +6 -6
  70. package/dist/{chunk-HIHA3GOR.js → chunk-EH5OLIP2.js} +2 -2
  71. package/dist/{chunk-3S5X7VZJ.js → chunk-EM2RSWDA.js} +5 -5
  72. package/dist/{chunk-DU7L7R4A.js → chunk-EOGVWCLF.js} +5 -5
  73. package/dist/{chunk-UTVNXB6U.js → chunk-EVBG5YUO.js} +3 -3
  74. package/dist/{chunk-YDSOTAZ6.js → chunk-FEC3JUIH.js} +3 -3
  75. package/dist/{chunk-KYEZFN6K.js → chunk-GAAN3Y7V.js} +1 -1
  76. package/dist/{chunk-LISVMDVS.js → chunk-HRRGIVIL.js} +3 -3
  77. package/dist/{chunk-RRW2LVVR.js → chunk-HXV2SY2V.js} +2 -2
  78. package/dist/{chunk-2BVLEMGX.js → chunk-I6TIZMM3.js} +5 -5
  79. package/dist/{chunk-4Y7EI5W5.js → chunk-I73UQU7K.js} +1 -1
  80. package/dist/{chunk-QQ7VXRIN.js → chunk-JCZWWBBA.js} +5 -5
  81. package/dist/{chunk-EEFFP64A.js → chunk-JFVLMWKY.js} +241 -52
  82. package/dist/{chunk-5KS3E3T5.js → chunk-JOMG6J64.js} +1 -1
  83. package/dist/{chunk-EEQG7VF2.js → chunk-KDUWFGY2.js} +4 -4
  84. package/dist/{chunk-6YZ72YP3.js → chunk-LIDUPDEY.js} +4 -4
  85. package/dist/{chunk-DNCEZWRC.js → chunk-LRYZXY4V.js} +1 -1
  86. package/dist/{chunk-L6F62LKX.js → chunk-M7BXAWUC.js} +3 -3
  87. package/dist/{chunk-CR3HOHJC.js → chunk-MD4UJICD.js} +6 -5
  88. package/dist/{chunk-SMVTNLXU.js → chunk-NCTABQMC.js} +4 -3
  89. package/dist/{chunk-GNDPZBG3.js → chunk-P4TWTQ6V.js} +8 -8
  90. package/dist/{chunk-6HTAOF6H.js → chunk-PGLNE556.js} +1 -1
  91. package/dist/{chunk-PZV27E4J.js → chunk-PV5XEBFI.js} +9 -9
  92. package/dist/{chunk-S6BD346V.js → chunk-QDUDXJAF.js} +3 -3
  93. package/dist/{chunk-5XKJH5GD.js → chunk-QRS7FQHE.js} +504 -175
  94. package/dist/{chunk-LFBQ55OO.js → chunk-R7V6BPEV.js} +1 -1
  95. package/dist/{chunk-VZG76CWC.js → chunk-RPKFXNOJ.js} +1 -1
  96. package/dist/{chunk-4W7CKRWR.js → chunk-SFJOGZQV.js} +3 -3
  97. package/dist/{chunk-WHM3OVBA.js → chunk-SHW6KK2K.js} +2 -2
  98. package/dist/{chunk-4TRA3GDE.js → chunk-SIJHPGUW.js} +5 -5
  99. package/dist/{chunk-XGGAZJWJ.js → chunk-SNUHX5GQ.js} +6 -6
  100. package/dist/{chunk-IBHZ3JFE.js → chunk-SOHJ37NA.js} +5 -5
  101. package/dist/{chunk-CAUNUTYE.js → chunk-SONZCJY5.js} +26 -4
  102. package/dist/{chunk-SAXNYDPS.js → chunk-T6RWREWU.js} +1 -1
  103. package/dist/{chunk-LCBZLTHW.js → chunk-TPYZFYQ5.js} +4 -4
  104. package/dist/{chunk-XHVHGDMB.js → chunk-UBANQ4EZ.js} +13 -13
  105. package/dist/{chunk-OGGUOTIK.js → chunk-UEJTWQVF.js} +1 -1
  106. package/dist/{chunk-7KMLITXH.js → chunk-UIS332AZ.js} +1 -1
  107. package/dist/{chunk-PWVQALO2.js → chunk-UU3CDLIX.js} +2 -2
  108. package/dist/{chunk-MWR25RQZ.js → chunk-VGHWK5Z2.js} +5 -3
  109. package/dist/{chunk-OFTTOPTD.js → chunk-WL3V2UV4.js} +6 -6
  110. package/dist/{chunk-NVM4S37G.js → chunk-XO7LP3JC.js} +1 -1
  111. package/dist/{chunk-L3S2ZTQZ.js → chunk-Z7ORL6SC.js} +2 -2
  112. package/dist/{chunk-RO5G3OEN.js → chunk-ZX3KEXO3.js} +343 -84
  113. package/dist/{codex-app-cards-switch-PWLWIWKG.js → codex-app-cards-switch-CN3G7GZW.js} +11 -11
  114. package/dist/{codex-desktop-launch-S3PDGLAY.js → codex-desktop-launch-GFZXCDSL.js} +1 -1
  115. package/dist/{codex-session-launch-PY5IMFJU.js → codex-session-launch-4F2UGQYE.js} +18 -18
  116. package/dist/{config-SWV4YOP3.js → config-BLHDUDCS.js} +3 -3
  117. package/dist/{global-upgrade-DI3LGOQP.js → global-upgrade-IZEC22BQ.js} +10 -10
  118. package/dist/{identity-AY35DGAO.js → identity-3HGTEP5Q.js} +4 -4
  119. package/dist/{live-capabilities-IH3EGKL3.js → live-capabilities-AZK664T7.js} +5 -5
  120. package/dist/{paths-I4Q77ULZ.js → paths-ROMZUSEX.js} +3 -3
  121. package/dist/{reapply-OAV4IAG3.js → reapply-IJYRR4YK.js} +27 -27
  122. package/dist/src/index.d.ts +2 -6
  123. package/dist/src/index.js +31 -30
  124. package/dist/{upgrade-lock-7U5BOK6V.js → upgrade-lock-FGEZPX56.js} +4 -4
  125. package/dist/{usage-NTER4GHT.js → usage-HCURE44F.js} +3 -3
  126. package/package.json +5 -2
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  repoKeyFor
3
- } from "./chunk-ILWEGI4H.js";
3
+ } from "./chunk-DUOBFECC.js";
4
4
  import {
5
5
  cancelQuestion,
6
6
  clientErrorKindOf,
@@ -8,29 +8,33 @@ import {
8
8
  reportClientError,
9
9
  sendNotification,
10
10
  waitForAnswer
11
- } from "./chunk-DU7L7R4A.js";
11
+ } from "./chunk-EOGVWCLF.js";
12
12
  import {
13
13
  withRetry
14
- } from "./chunk-E56VJY23.js";
14
+ } from "./chunk-32JJ5KV3.js";
15
15
  import {
16
16
  getMachineId
17
- } from "./chunk-5XYYBB5Z.js";
17
+ } from "./chunk-BGEUAJYL.js";
18
18
  import {
19
19
  reportedInstallSource
20
- } from "./chunk-ZWCKS65X.js";
20
+ } from "./chunk-DARPCQPY.js";
21
21
  import {
22
22
  IDLE_PROMPT_NOTIFICATION,
23
23
  NOTIFICATION_PUSH_TYPES,
24
24
  POSTTOOLUSE_MIRROR_ONLY_TOOLS,
25
- canonicalJson
26
- } from "./chunk-N4NSX5MB.js";
25
+ canonicalJson,
26
+ claudeConfigDir
27
+ } from "./chunk-2J4IVUEA.js";
27
28
  import {
28
- writeFileAtomic
29
- } from "./chunk-GUJBS6TK.js";
29
+ compareVersion,
30
+ parseVersion,
31
+ writeFileAtomic,
32
+ writeJsonAtomicDurable
33
+ } from "./chunk-AUDYAVNM.js";
30
34
  import {
31
35
  getApiKey,
32
36
  getBaseUrl
33
- } from "./chunk-BMVWGPYB.js";
37
+ } from "./chunk-53D6FFVE.js";
34
38
  import {
35
39
  ACTION_BODY_MAX,
36
40
  AGENTS,
@@ -38,25 +42,30 @@ import {
38
42
  BLANK_TASK_INPUT_TOOL,
39
43
  BLOCKED_ACTION_GRANT_TTL_SECONDS,
40
44
  CODEX_SHELL_COMMAND_FLAGS,
45
+ COMMAND_RECOVERY,
41
46
  DECISION_LINE_MAX,
42
47
  FILE_TARGET_TOOLS,
43
48
  GATE_MAX_TOOL_INPUTS,
44
49
  HOOK_COMPATIBLE_PROFILES,
45
50
  HOOK_SOURCES,
46
- PENDING_COMMAND_PREFIX,
47
51
  REPO_AWARE_AGENTS,
48
52
  TASK_OUTPUT_TOOL,
49
53
  TASK_STOP_TOOL,
50
54
  blockedActionIdentity,
55
+ canCarryPendingCommand,
51
56
  canonicalToolName,
57
+ claudeTurnEndOutput,
52
58
  conversationText,
53
59
  gateOutputFor,
54
60
  hookCompatibleProfileBySource,
55
61
  isAutoApprove,
62
+ isRecord,
56
63
  isTokenCount,
57
64
  modeStateFromResponse,
65
+ nativeRecord,
58
66
  parseMcpToolName,
59
67
  pendingCommandEvents,
68
+ pendingCommandInstruction,
60
69
  questionLimitHandBack,
61
70
  redactSecrets,
62
71
  redactSecretsDeep,
@@ -66,7 +75,7 @@ import {
66
75
  toToolLookup,
67
76
  truncateLine,
68
77
  unansweredRefusalReason
69
- } from "./chunk-5XKJH5GD.js";
78
+ } from "./chunk-QRS7FQHE.js";
70
79
 
71
80
  // ../hook-mapping/src/describe.ts
72
81
  import { isAbsolute, relative, resolve } from "path";
@@ -844,7 +853,7 @@ var transcriptUsageRequest = (hook) => {
844
853
  import { join } from "path";
845
854
  import { homedir, tmpdir } from "os";
846
855
  import { createHash } from "crypto";
847
- import { existsSync, mkdirSync, writeFileSync, readFileSync, readdirSync, renameSync, unlinkSync, rmSync, statSync } from "fs";
856
+ import { openSync, closeSync, fsyncSync, lstatSync, existsSync, mkdirSync, writeFileSync, readFileSync, readdirSync, unlinkSync, rmSync, statSync } from "fs";
848
857
  var PENDING_DIR = join(tmpdir(), "pushary-pending");
849
858
  var commandRoot = () => process.env.PUSHARY_COMMAND_DIR?.trim() || join(homedir(), ".pushary", "run", "commands");
850
859
  var DEFAULT_SESSION = "_no_session";
@@ -860,16 +869,23 @@ var legacyCommandFile = (sessionId, namespace = "") => join(commandDirFor(sessio
860
869
  var commandFile = (sessionId, id, namespace = "") => join(commandDirFor(sessionId, namespace), `${createHash("sha256").update(id).digest("hex")}.json`);
861
870
  var readCommandFile = (file) => {
862
871
  try {
872
+ if (!lstatSync(file).isFile()) return null;
863
873
  const value = JSON.parse(readFileSync(file, "utf-8"));
864
- if (typeof value.id !== "string" || typeof value.text !== "string" || typeof value.expiresAtMs !== "number") return null;
865
- if (value.expiresAtMs <= Date.now()) {
874
+ if (!nativeRecord(value) || typeof value.id !== "string" || typeof value.text !== "string" || typeof value.expiresAtMs !== "number" || !Number.isFinite(value.expiresAtMs)) return null;
875
+ if (value.expiresAtMs + COMMAND_RECOVERY.receiptRetentionMs <= Date.now()) {
866
876
  unlinkSync(file);
877
+ rmSync(`${file}.emission`, { force: true });
867
878
  return null;
868
879
  }
869
880
  return {
870
- ...value,
881
+ id: value.id,
882
+ text: value.text,
883
+ expiresAtMs: value.expiresAtMs,
884
+ ...value.attempted === true ? { attempted: true } : {},
885
+ ...typeof value.attemptedAtMs === "number" ? { attemptedAtMs: value.attemptedAtMs } : {},
886
+ ...typeof value.sessionId === "string" ? { sessionId: value.sessionId } : {},
871
887
  delivered: value.delivered === true,
872
- ...value.failed === true ? { failed: true } : {}
888
+ ...value.failed === true || !value.delivered && !value.attempted && value.expiresAtMs <= Date.now() ? { failed: true } : {}
873
889
  };
874
890
  } catch {
875
891
  return null;
@@ -915,7 +931,7 @@ var pruneExpiredCommandStore = () => {
915
931
  };
916
932
  var readPendingCommand = (sessionId, namespace = "") => {
917
933
  const commands = pruneExpiredCommands(commandDirFor(sessionId, namespace));
918
- return commands.find((command) => !command.delivered && !command.failed) ?? commands[0] ?? null;
934
+ return commands.find((command) => !command.delivered && !command.failed && !command.attempted) ?? commands[0] ?? null;
919
935
  };
920
936
  var savePendingCommand = (sessionId, command, namespace = "") => {
921
937
  const dir = commandDirFor(sessionId, namespace);
@@ -923,7 +939,21 @@ var savePendingCommand = (sessionId, command, namespace = "") => {
923
939
  mkdirSync(dir, { recursive: true, mode: 448 });
924
940
  pruneExpiredCommandStore();
925
941
  if (readPendingCommandById(sessionId, command.id, namespace)) return false;
926
- writeFileSync(commandFile(sessionId, command.id, namespace), JSON.stringify(command), { encoding: "utf-8", mode: 384, flag: "wx" });
942
+ writeFileSync(commandFile(sessionId, command.id, namespace), JSON.stringify({ ...command, sessionId }), { encoding: "utf-8", mode: 384, flag: "wx" });
943
+ const descriptor = openSync(commandFile(sessionId, command.id, namespace), "r");
944
+ try {
945
+ fsyncSync(descriptor);
946
+ } finally {
947
+ closeSync(descriptor);
948
+ }
949
+ if (process.platform !== "win32") {
950
+ const directory = openSync(dir, "r");
951
+ try {
952
+ fsyncSync(directory);
953
+ } finally {
954
+ closeSync(directory);
955
+ }
956
+ }
927
957
  return true;
928
958
  } catch {
929
959
  return false;
@@ -934,19 +964,36 @@ var updatePendingCommand = (sessionId, id, update, namespace = "") => {
934
964
  if (!command) return false;
935
965
  const currentFile = commandFile(sessionId, id, namespace);
936
966
  const file = existsSync(currentFile) ? currentFile : legacyCommandFile(sessionId, namespace);
937
- const temporary = `${file}.${process.pid}.tmp`;
938
967
  try {
939
- writeFileSync(temporary, JSON.stringify({ ...command, ...update, text: "" }), { encoding: "utf-8", mode: 384 });
940
- renameSync(temporary, file);
968
+ writeJsonAtomicDurable(file, { ...command, ...update, text: "" }, 384);
941
969
  return true;
942
970
  } catch {
971
+ return false;
972
+ }
973
+ };
974
+ var markPendingCommandAttempted = (sessionId, id, namespace = "") => {
975
+ const command = readPendingCommandById(sessionId, id, namespace);
976
+ if (!command || command.attempted || command.delivered || command.failed) return false;
977
+ try {
978
+ const descriptor = openSync(`${commandFile(sessionId, id, namespace)}.emission`, "wx", 384);
943
979
  try {
944
- unlinkSync(temporary);
945
- } catch {
980
+ fsyncSync(descriptor);
981
+ } finally {
982
+ closeSync(descriptor);
946
983
  }
984
+ return updatePendingCommand(sessionId, id, { attempted: true, attemptedAtMs: Date.now() }, namespace);
985
+ } catch {
947
986
  return false;
948
987
  }
949
988
  };
989
+ var pendingCommandReceipts = (namespace) => {
990
+ const root = join(commandRoot(), createHash("sha256").update(namespace).digest("hex").slice(0, 16));
991
+ try {
992
+ 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 })));
993
+ } catch {
994
+ return [];
995
+ }
996
+ };
950
997
  var markPendingCommandDelivered = (sessionId, id, namespace = "") => updatePendingCommand(sessionId, id, { delivered: true, failed: void 0 }, namespace);
951
998
  var markPendingCommandFailed = (sessionId, id, namespace = "") => updatePendingCommand(sessionId, id, { delivered: false, failed: true }, namespace);
952
999
  var removePendingCommand = (sessionId, id, namespace = "") => {
@@ -955,6 +1002,10 @@ var removePendingCommand = (sessionId, id, namespace = "") => {
955
1002
  unlinkSync(commandFile(sessionId, id, namespace));
956
1003
  } catch {
957
1004
  }
1005
+ try {
1006
+ rmSync(`${commandFile(sessionId, id, namespace)}.emission`, { force: true });
1007
+ } catch {
1008
+ }
958
1009
  try {
959
1010
  if (readCommandFile(legacyCommandFile(sessionId, namespace))?.id === id) {
960
1011
  unlinkSync(legacyCommandFile(sessionId, namespace));
@@ -1011,6 +1062,48 @@ var removePendingSession = (sessionId) => {
1011
1062
  }
1012
1063
  };
1013
1064
 
1065
+ // src/hook-command-delivery.ts
1066
+ import { AsyncLocalStorage } from "async_hooks";
1067
+ var delivery = new AsyncLocalStorage();
1068
+ var stageHookCommand = (command) => {
1069
+ const staged = delivery.getStore();
1070
+ if (!staged) return false;
1071
+ staged.push(command);
1072
+ return true;
1073
+ };
1074
+ var writeOutput = (output) => new Promise((resolve3, reject) => {
1075
+ process.stdout.write(output, (error) => error ? reject(error) : resolve3());
1076
+ });
1077
+ var emitHookOutput = async (render, write = writeOutput) => {
1078
+ const staged = [];
1079
+ const output = await delivery.run(staged, render);
1080
+ if (!output) return;
1081
+ for (const command of staged) {
1082
+ if (!markPendingCommandAttempted(
1083
+ command.sessionId,
1084
+ command.commandId,
1085
+ command.namespace
1086
+ )) {
1087
+ throw new Error("Could not persist instruction delivery intent");
1088
+ }
1089
+ }
1090
+ await write(output);
1091
+ for (const command of staged) {
1092
+ if (!markPendingCommandDelivered(
1093
+ command.sessionId,
1094
+ command.commandId,
1095
+ command.namespace
1096
+ ))
1097
+ continue;
1098
+ if (await command.acknowledge())
1099
+ removePendingCommand(
1100
+ command.sessionId,
1101
+ command.commandId,
1102
+ command.namespace
1103
+ );
1104
+ }
1105
+ };
1106
+
1014
1107
  // src/policy.ts
1015
1108
  import { createHash as createHash2 } from "crypto";
1016
1109
  import { existsSync as existsSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
@@ -1155,7 +1248,7 @@ var fetchModeState = async (apiKey, sessionId, reuseWithinMs = 0, signal) => {
1155
1248
  var fetchModeOverride = async (apiKey) => (await fetchModeState(apiKey)).mode;
1156
1249
 
1157
1250
  // src/usage.ts
1158
- import { closeSync, mkdirSync as mkdirSync2, openSync, readFileSync as readFileSync3, readSync, statSync as statSync2, writeFileSync as writeFileSync3 } from "fs";
1251
+ import { closeSync as closeSync2, mkdirSync as mkdirSync2, openSync as openSync2, readFileSync as readFileSync3, readSync, statSync as statSync2, writeFileSync as writeFileSync3 } from "fs";
1159
1252
  import { createHash as createHash3 } from "crypto";
1160
1253
  import { join as join3 } from "path";
1161
1254
  import { tmpdir as tmpdir3 } from "os";
@@ -1191,7 +1284,7 @@ var readState = (path) => {
1191
1284
  return emptyState();
1192
1285
  };
1193
1286
  var readRange = (path, start, end) => {
1194
- const fd = openSync(path, "r");
1287
+ const fd = openSync2(path, "r");
1195
1288
  try {
1196
1289
  const chunks = [];
1197
1290
  let position = start;
@@ -1205,7 +1298,7 @@ var readRange = (path, start, end) => {
1205
1298
  }
1206
1299
  return Buffer.concat(chunks);
1207
1300
  } finally {
1208
- closeSync(fd);
1301
+ closeSync2(fd);
1209
1302
  }
1210
1303
  };
1211
1304
  var applyLine = (state, line) => {
@@ -1582,14 +1675,78 @@ var forgetBlockedActionHint = (fingerprint) => {
1582
1675
  }
1583
1676
  };
1584
1677
 
1678
+ // src/claude-session-record.ts
1679
+ import { readFileSync as readFileSync6, readdirSync as readdirSync4, statSync as statSync6 } from "fs";
1680
+ import { join as join7 } from "path";
1681
+ var UNREADABLE = { kind: "unreadable" };
1682
+ var OTHER_SESSION = { kind: "other_session" };
1683
+ var PROCESS_ID = /^[1-9]\d*$/;
1684
+ var SESSION_FILE_SUFFIX = ".json";
1685
+ var SESSION_FILE_MAX_BYTES = 64 * 1024;
1686
+ var isRunning = (pid) => {
1687
+ try {
1688
+ process.kill(pid, 0);
1689
+ return true;
1690
+ } catch (error) {
1691
+ return error instanceof Error && "code" in error && error.code === "EPERM";
1692
+ }
1693
+ };
1694
+ var isSmallRegularFile = (path) => {
1695
+ try {
1696
+ const stats = statSync6(path);
1697
+ return stats.isFile() && stats.size <= SESSION_FILE_MAX_BYTES;
1698
+ } catch {
1699
+ return false;
1700
+ }
1701
+ };
1702
+ var readSessionFile = (path, sessionId) => {
1703
+ if (!isSmallRegularFile(path)) return UNREADABLE;
1704
+ try {
1705
+ const parsed = JSON.parse(readFileSync6(path, "utf-8"));
1706
+ if (!isRecord(parsed) || typeof parsed.sessionId !== "string") return UNREADABLE;
1707
+ if (parsed.sessionId !== sessionId) return OTHER_SESSION;
1708
+ return { kind: "this_session", version: typeof parsed.version === "string" ? parsed.version : null };
1709
+ } catch {
1710
+ return UNREADABLE;
1711
+ }
1712
+ };
1713
+ var runningSessionFiles = (sessions) => {
1714
+ try {
1715
+ return readdirSync4(sessions).filter((name) => name.endsWith(SESSION_FILE_SUFFIX)).map((name) => name.slice(0, -SESSION_FILE_SUFFIX.length)).filter((pid) => PROCESS_ID.test(pid) && isRunning(Number(pid))).map((pid) => join7(sessions, `${pid}${SESSION_FILE_SUFFIX}`));
1716
+ } catch {
1717
+ return [];
1718
+ }
1719
+ };
1720
+ var parsedSessionVersion = (version) => {
1721
+ const release = version === null ? null : parseVersion(version);
1722
+ return version === null || release === null ? null : { version, release };
1723
+ };
1724
+ var isParsed = (entry) => entry !== null;
1725
+ var lowerVersion = (lowest, entry) => compareVersion(entry.release, lowest.release) < 0 ? entry : lowest;
1726
+ var oldestVersionAcross = (readings) => {
1727
+ if (readings.some((reading) => reading.kind === "unreadable")) return null;
1728
+ const parsed = readings.flatMap((reading) => reading.kind === "this_session" ? [reading.version] : []).map(parsedSessionVersion);
1729
+ if (parsed.length === 0 || !parsed.every(isParsed)) return null;
1730
+ return parsed.filter(isParsed).reduce(lowerVersion).version;
1731
+ };
1732
+ var claudeSessionVersion = (sessionId, environment = process.env) => {
1733
+ if (!sessionId) return null;
1734
+ try {
1735
+ const sessions = join7(claudeConfigDir(void 0, environment), "sessions");
1736
+ return oldestVersionAcross(runningSessionFiles(sessions).map((path) => readSessionFile(path, sessionId)));
1737
+ } catch {
1738
+ return null;
1739
+ }
1740
+ };
1741
+
1585
1742
  // src/background-work.ts
1586
1743
  import { createHash as createHash7 } from "crypto";
1587
- import { mkdirSync as mkdirSync6, rmSync as rmSync5, statSync as statSync6, writeFileSync as writeFileSync6 } from "fs";
1744
+ import { mkdirSync as mkdirSync6, rmSync as rmSync5, statSync as statSync7, writeFileSync as writeFileSync6 } from "fs";
1588
1745
  import { tmpdir as tmpdir7 } from "os";
1589
- import { join as join7 } from "path";
1746
+ import { join as join8 } from "path";
1590
1747
  var BACKGROUND_WORK_TTL_MS = 60 * 60 * 1e3;
1591
- var backgroundWorkDir = () => join7(tmpdir7(), "pushary-background-work");
1592
- var markerFor = (sessionKey) => join7(backgroundWorkDir(), createHash7("sha256").update(sessionKey).digest("hex"));
1748
+ var backgroundWorkDir = () => join8(tmpdir7(), "pushary-background-work");
1749
+ var markerFor = (sessionKey) => join8(backgroundWorkDir(), createHash7("sha256").update(sessionKey).digest("hex"));
1593
1750
  var hasRunningBackgroundTasks = (backgroundTasks) => Array.isArray(backgroundTasks) && backgroundTasks.length > 0;
1594
1751
  var noteBackgroundWork = (sessionKey, running) => {
1595
1752
  try {
@@ -1605,21 +1762,21 @@ var noteBackgroundWork = (sessionKey, running) => {
1605
1762
  };
1606
1763
  var isWaitingOnBackgroundWork = (sessionKey, nowMs = Date.now()) => {
1607
1764
  try {
1608
- return nowMs - statSync6(markerFor(sessionKey)).mtimeMs < BACKGROUND_WORK_TTL_MS;
1765
+ return nowMs - statSync7(markerFor(sessionKey)).mtimeMs < BACKGROUND_WORK_TTL_MS;
1609
1766
  } catch {
1610
1767
  return false;
1611
1768
  }
1612
1769
  };
1613
1770
 
1614
1771
  // src/events.ts
1615
- import { basename as basename2, join as join8 } from "path";
1616
- import { existsSync as existsSync6, mkdirSync as mkdirSync7, readFileSync as readFileSync6, statSync as statSync7, writeFileSync as writeFileSync7 } from "fs";
1772
+ import { basename as basename2, join as join9 } from "path";
1773
+ import { existsSync as existsSync6, mkdirSync as mkdirSync7, readFileSync as readFileSync7, statSync as statSync8, writeFileSync as writeFileSync7 } from "fs";
1617
1774
  import { tmpdir as tmpdir8 } from "os";
1618
- var INTENT_DIR = join8(tmpdir8(), "pushary-intent");
1775
+ var INTENT_DIR = join9(tmpdir8(), "pushary-intent");
1619
1776
  var INTENT_MAX_BYTES = 2048;
1620
1777
  var INTENT_GRACE_MS = 10 * 60 * 1e3;
1621
1778
  var sanitizeSession = (sessionId) => sessionId.replace(/[^A-Za-z0-9_-]/g, "_").slice(0, 128) || DEFAULT_SESSION;
1622
- var intentFile = (sessionId) => join8(INTENT_DIR, sanitizeSession(sessionId));
1779
+ var intentFile = (sessionId) => join9(INTENT_DIR, sanitizeSession(sessionId));
1623
1780
  var saveLastPrompt = (sessionId, prompt) => {
1624
1781
  try {
1625
1782
  const trimmed = prompt.replace(/\s+/g, " ").trim().slice(0, INTENT_MAX_BYTES);
@@ -1632,8 +1789,8 @@ var saveLastPrompt = (sessionId, prompt) => {
1632
1789
  var readLastPrompt = (sessionId) => {
1633
1790
  try {
1634
1791
  const path = intentFile(sessionId);
1635
- if (isDefaultSession(sessionId) && statSync7(path).mtimeMs < Date.now() - INTENT_GRACE_MS) return void 0;
1636
- const value = readFileSync6(path, "utf-8").trim();
1792
+ if (isDefaultSession(sessionId) && statSync8(path).mtimeMs < Date.now() - INTENT_GRACE_MS) return void 0;
1793
+ const value = readFileSync7(path, "utf-8").trim();
1637
1794
  return value.length > 0 ? value : void 0;
1638
1795
  } catch {
1639
1796
  return void 0;
@@ -1698,7 +1855,7 @@ var readFreshCachedPolicy = (apiKey, agentType, sessionId) => {
1698
1855
  const request = policyRequestFor(agentType);
1699
1856
  const path = policyCacheFile(apiKey, request.agent, request.repoAware);
1700
1857
  if (!existsSync6(path)) return null;
1701
- const cached = JSON.parse(readFileSync6(path, "utf-8"));
1858
+ const cached = JSON.parse(readFileSync7(path, "utf-8"));
1702
1859
  if (!isPolicyConfig(cached)) return null;
1703
1860
  if ((cached._sessionId ?? null) !== (sessionId ?? null)) return null;
1704
1861
  if (!cached._cachedAt || Date.now() - cached._cachedAt >= POLICY_CACHE_TTL_MS) return null;
@@ -1741,8 +1898,9 @@ var deriveAutoDecisionBeacon = (toolName, toolInput, liveMode, agentType, cwd, s
1741
1898
  };
1742
1899
  var AUTO_DECISION_WINDOW_MS = 15 * 60 * 1e3;
1743
1900
  var HOOK_ACTIVE_WINDOW_MS = 30 * 60 * 1e3;
1901
+ var hookSourceOfAgentType = (agentType) => HOOK_SOURCES.find((candidate) => AGENT_IDENTITIES[candidate].type === agentType);
1744
1902
  var deriveUsage = (payload, agent, event) => {
1745
- const source = HOOK_SOURCES.find((candidate) => AGENT_IDENTITIES[candidate].type === agent.type);
1903
+ const source = hookSourceOfAgentType(agent.type);
1746
1904
  const request = source ? transcriptUsageRequest({ source, event, payload, costTracking: process.env[COST_TRACKING_ENV] }) : null;
1747
1905
  if (!request) return void 0;
1748
1906
  try {
@@ -1751,16 +1909,19 @@ var deriveUsage = (payload, agent, event) => {
1751
1909
  return void 0;
1752
1910
  }
1753
1911
  };
1754
- var mergeAdditionalContext = (existing, reported) => {
1912
+ var pendingInstructionOf = (reported) => {
1755
1913
  const pendingCommand = reported.status === "fulfilled" ? reported.value?.pendingCommand : void 0;
1756
- if (typeof pendingCommand !== "string" || pendingCommand.trim().length === 0) return existing;
1757
- const injected = `${PENDING_COMMAND_PREFIX} ${pendingCommand.trim()}`;
1914
+ return typeof pendingCommand === "string" ? pendingCommandInstruction(pendingCommand) : void 0;
1915
+ };
1916
+ var mergeAdditionalContext = (existing, reported) => {
1917
+ const injected = pendingInstructionOf(reported);
1918
+ if (injected === void 0) return existing;
1758
1919
  return existing ? `${existing}
1759
1920
 
1760
1921
  ${injected}` : injected;
1761
1922
  };
1762
1923
  var consumesActivityContext = (agentType) => {
1763
- const source = HOOK_SOURCES.find((candidate) => AGENT_IDENTITIES[candidate].type === agentType);
1924
+ const source = hookSourceOfAgentType(agentType);
1764
1925
  if (!source) return false;
1765
1926
  return pendingCommandEvents(source).some((event) => event !== "Stop");
1766
1927
  };
@@ -1841,6 +2002,13 @@ var reportEvent = async (event, options = {}) => {
1841
2002
  const result = await res.json();
1842
2003
  const withoutPending = () => ({ ...result, pendingCommand: void 0, pendingCommandId: void 0 });
1843
2004
  if (storedCommand) {
2005
+ if (storedCommand.attempted && !storedCommand.delivered && Date.now() - (storedCommand.attemptedAtMs ?? 0) < COMMAND_RECOVERY.emissionRecoveryGraceMs) return withoutPending();
2006
+ if (!storedCommand.delivered && !storedCommand.failed && !storedCommand.attempted && stageHookCommand({
2007
+ sessionId: sessionKey,
2008
+ commandId: storedCommand.id,
2009
+ namespace: receiptNamespace,
2010
+ acknowledge: () => acknowledgePendingCommand(apiKey, baseUrl, storedCommand.id, event.sessionId, machineId, ACK_RESERVE_MS)
2011
+ })) return { ...withoutPending(), pendingCommand: storedCommand.text, pendingCommandId: storedCommand.id };
1844
2012
  const failed = storedCommand.failed === true;
1845
2013
  const acknowledged = await acknowledgePendingCommand(
1846
2014
  apiKey,
@@ -1849,11 +2017,11 @@ var reportEvent = async (event, options = {}) => {
1849
2017
  event.sessionId,
1850
2018
  machineId,
1851
2019
  ackTimeoutMs(options.timeoutMs ?? 1e4),
1852
- failed ? "failed" : "delivered",
2020
+ failed ? "failed" : storedCommand.attempted && !storedCommand.delivered ? "uncertain" : "delivered",
1853
2021
  options.signal
1854
2022
  );
1855
2023
  if (!acknowledged) return withoutPending();
1856
- if (storedCommand.delivered || failed) {
2024
+ if (storedCommand.delivered || storedCommand.attempted || failed) {
1857
2025
  removePendingCommand(sessionKey, storedCommand.id, receiptNamespace);
1858
2026
  return withoutPending();
1859
2027
  }
@@ -1868,6 +2036,13 @@ var reportEvent = async (event, options = {}) => {
1868
2036
  delivered: false
1869
2037
  }, receiptNamespace);
1870
2038
  if (!saved) return withoutPending();
2039
+ const commandId = result.pendingCommandId;
2040
+ if (stageHookCommand({
2041
+ sessionId: sessionKey,
2042
+ commandId,
2043
+ namespace: receiptNamespace,
2044
+ acknowledge: () => acknowledgePendingCommand(apiKey, baseUrl, commandId, event.sessionId, machineId, ACK_RESERVE_MS)
2045
+ })) return result;
1871
2046
  const acknowledged = await acknowledgePendingCommand(
1872
2047
  apiKey,
1873
2048
  baseUrl,
@@ -1998,7 +2173,10 @@ var handleUserPrompt = async (input, agent = CLAUDE_CODE_AGENT) => {
1998
2173
  }
1999
2174
  };
2000
2175
  var UNUSED_APPROVAL_INSTRUCTION = "The person allowed, from Pushary, the call auto mode blocked. Send that call again now, exactly as it was with nothing changed, to use their approval. It covers only that exact call.";
2001
- var phoneInstructionOf = (pendingCommand) => typeof pendingCommand === "string" && pendingCommand.trim().length > 0 ? `The user sent a new instruction from their phone via Pushary: ${pendingCommand.trim()}` : null;
2176
+ var carriesInstructionAtTurnEnd = (agentType) => {
2177
+ const source = hookSourceOfAgentType(agentType);
2178
+ return source !== void 0 && canCarryPendingCommand(source, "Stop");
2179
+ };
2002
2180
  var handleStop = async (input, agent = CLAUDE_CODE_AGENT) => {
2003
2181
  const deadline = Date.now() + HOOK_BUDGET_MS;
2004
2182
  const signal = AbortSignal.timeout(HOOK_BUDGET_MS);
@@ -2007,6 +2185,7 @@ var handleStop = async (input, agent = CLAUDE_CODE_AGENT) => {
2007
2185
  const sessionKey = input.session_id || DEFAULT_SESSION;
2008
2186
  noteBackgroundWork(sessionKey, hasRunningBackgroundTasks(input.background_tasks));
2009
2187
  const late = await reconcilePendingQuestions(sessionKey, void 0, signal);
2188
+ const carriesInstruction = carriesInstructionAtTurnEnd(agent.type);
2010
2189
  const tasks = [
2011
2190
  reportEvent({
2012
2191
  event: "session_end",
@@ -2015,7 +2194,12 @@ var handleStop = async (input, agent = CLAUDE_CODE_AGENT) => {
2015
2194
  action: summarizeStop(input),
2016
2195
  ...sessionIdentityOf(input),
2017
2196
  usage: deriveUsage(input, agent, "Stop")
2018
- }, { maxAttempts: 1, budgetMs: Math.min(DRAINING_REPORT_BUDGET_MS, Math.max(0, deadline - Date.now())), signal })
2197
+ }, {
2198
+ maxAttempts: 1,
2199
+ budgetMs: Math.min(DRAINING_REPORT_BUDGET_MS, Math.max(0, deadline - Date.now())),
2200
+ signal,
2201
+ skipDrain: !carriesInstruction
2202
+ })
2019
2203
  ];
2020
2204
  if (late.length > 0) {
2021
2205
  tasks.push(notifyLateAnswers(getApiKey(), late, `${agent.label} - ${projectName}`, input.session_id, signal));
@@ -2023,10 +2207,13 @@ var handleStop = async (input, agent = CLAUDE_CODE_AGENT) => {
2023
2207
  }
2024
2208
  if (!isDefaultSession(sessionKey) && listPendingQuestions(sessionKey).length === 0) removePendingSession(sessionKey);
2025
2209
  const [reported] = await Promise.allSettled(tasks);
2026
- const pendingCommand = reported.status === "fulfilled" ? reported.value?.pendingCommand : void 0;
2027
2210
  const unusedApproval = !input.stop_hook_active && approvalAwaitingRetry(input.session_id) !== null;
2028
- const instructions = [unusedApproval ? UNUSED_APPROVAL_INSTRUCTION : null, phoneInstructionOf(pendingCommand)].filter((instruction) => instruction !== null);
2029
- return instructions.length > 0 ? { decision: "block", reason: instructions.join("\n\n") } : void 0;
2211
+ const instruction = carriesInstruction ? pendingInstructionOf(reported) : void 0;
2212
+ return claudeTurnEndOutput({
2213
+ instruction,
2214
+ continueReason: unusedApproval ? UNUSED_APPROVAL_INSTRUCTION : void 0,
2215
+ claudeVersion: instruction === void 0 ? null : claudeSessionVersion(input.session_id)
2216
+ });
2030
2217
  } catch {
2031
2218
  return void 0;
2032
2219
  }
@@ -2217,14 +2404,16 @@ export {
2217
2404
  isCodexGuardianRolloutHead,
2218
2405
  scanCodexGuardianReviews,
2219
2406
  codexReviewerDenialsSince,
2220
- phoneDrivenEnv,
2221
2407
  DEFAULT_SESSION,
2222
2408
  readPendingCommandById,
2223
2409
  readPendingCommand,
2224
2410
  savePendingCommand,
2411
+ pendingCommandReceipts,
2225
2412
  markPendingCommandDelivered,
2226
2413
  markPendingCommandFailed,
2227
2414
  removePendingCommand,
2415
+ emitHookOutput,
2416
+ phoneDrivenEnv,
2228
2417
  claimThrottle,
2229
2418
  releaseThrottle,
2230
2419
  policyRequestFor,
@@ -7,7 +7,7 @@ import {
7
7
  recordSealerFor,
8
8
  uploadTranscriptRecords,
9
9
  wrapSessionKey
10
- } from "./chunk-CAUNUTYE.js";
10
+ } from "./chunk-SONZCJY5.js";
11
11
 
12
12
  // src/live-session.ts
13
13
  var MAX_TRANSCRIPT_QUEUE = 100;
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  realpath
3
- } from "./chunk-S6BD346V.js";
3
+ } from "./chunk-QDUDXJAF.js";
4
4
  import {
5
5
  findNativeClaudeInstall
6
- } from "./chunk-LFBQ55OO.js";
6
+ } from "./chunk-R7V6BPEV.js";
7
7
  import {
8
8
  readGlobalPrefixAsync,
9
9
  resolveGlobalBinary,
@@ -13,10 +13,10 @@ import {
13
13
  resolvePackageVersionFromBinary,
14
14
  resolvePathBinary,
15
15
  resolvePathBinaryAsync
16
- } from "./chunk-GUJBS6TK.js";
16
+ } from "./chunk-AUDYAVNM.js";
17
17
  import {
18
18
  MANAGED_PROVIDERS
19
- } from "./chunk-5XKJH5GD.js";
19
+ } from "./chunk-QRS7FQHE.js";
20
20
 
21
21
  // src/live-capabilities.ts
22
22
  import { homedir } from "os";
@@ -1,19 +1,19 @@
1
1
  import {
2
2
  resolveInstallSourceFor
3
- } from "./chunk-ZWCKS65X.js";
3
+ } from "./chunk-DARPCQPY.js";
4
4
  import {
5
5
  backupFile,
6
6
  readJsonSafe,
7
7
  writeFileAtomic,
8
8
  writeJsonAtomic
9
- } from "./chunk-GUJBS6TK.js";
9
+ } from "./chunk-AUDYAVNM.js";
10
10
  import {
11
11
  configFilePath
12
- } from "./chunk-BMVWGPYB.js";
12
+ } from "./chunk-53D6FFVE.js";
13
13
  import {
14
14
  API_KEY_PREFIX_PATTERN,
15
15
  isRecord
16
- } from "./chunk-5XKJH5GD.js";
16
+ } from "./chunk-QRS7FQHE.js";
17
17
 
18
18
  // src/shell-alias.ts
19
19
  import { homedir } from "os";
@@ -7,7 +7,7 @@ import {
7
7
  } from "./chunk-KZERVKTD.js";
8
8
  import {
9
9
  redactSecretsFromContent
10
- } from "./chunk-5XKJH5GD.js";
10
+ } from "./chunk-QRS7FQHE.js";
11
11
 
12
12
  // src/cli/json.ts
13
13
  var writeStdoutFully = (text) => new Promise((resolve, reject) => {
@@ -3,11 +3,11 @@ import {
3
3
  } from "./chunk-POFBE6ZY.js";
4
4
  import {
5
5
  autoUpdateState
6
- } from "./chunk-N4NSX5MB.js";
6
+ } from "./chunk-2J4IVUEA.js";
7
7
  import {
8
8
  readJsonSafe,
9
9
  writeJsonAtomic
10
- } from "./chunk-GUJBS6TK.js";
10
+ } from "./chunk-AUDYAVNM.js";
11
11
  import {
12
12
  CLI_PACKAGE_NAMES,
13
13
  POSIX_GLOBAL_ROOT_SEGMENTS,
@@ -17,7 +17,7 @@ import {
17
17
  } from "./chunk-QTOLNRET.js";
18
18
  import {
19
19
  isAutoUpdateResult
20
- } from "./chunk-5XKJH5GD.js";
20
+ } from "./chunk-QRS7FQHE.js";
21
21
 
22
22
  // src/auto-update.ts
23
23
  import { spawn } from "child_process";