@pushary/agent-hooks 0.89.9 → 0.89.10

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 (67) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/dist/bin/pushary-bell-hook.js +4 -4
  3. package/dist/bin/pushary-bell.js +7 -7
  4. package/dist/bin/pushary-claude.js +64 -33
  5. package/dist/bin/pushary-clean.js +52 -45
  6. package/dist/bin/pushary-codex-bridge.js +8 -8
  7. package/dist/bin/pushary-codex-hook.js +12 -8
  8. package/dist/bin/pushary-codex.js +7 -7
  9. package/dist/bin/pushary-connect.js +6 -6
  10. package/dist/bin/pushary-daemon.js +16 -16
  11. package/dist/bin/pushary-disconnect.js +34 -9
  12. package/dist/bin/pushary-doctor.js +19 -19
  13. package/dist/bin/pushary-elicitation-hook.js +4 -4
  14. package/dist/bin/pushary-gemini-bridge.js +7 -7
  15. package/dist/bin/pushary-gemini-hook.js +8 -8
  16. package/dist/bin/pushary-hook.js +11 -10
  17. package/dist/bin/pushary-login.js +4 -4
  18. package/dist/bin/pushary-logout.js +4 -4
  19. package/dist/bin/pushary-mode.js +3 -3
  20. package/dist/bin/pushary-notification-hook.js +7 -7
  21. package/dist/bin/pushary-opencode-hook.js +8 -8
  22. package/dist/bin/pushary-permission-denied-hook.js +11 -10
  23. package/dist/bin/pushary-permission-hook.js +11 -10
  24. package/dist/bin/pushary-post-hook.js +7 -7
  25. package/dist/bin/pushary-prompt-hook.js +7 -7
  26. package/dist/bin/pushary-session-end-hook.js +7 -7
  27. package/dist/bin/pushary-session-start-hook.js +7 -7
  28. package/dist/bin/pushary-setup.js +52 -54
  29. package/dist/bin/pushary-stats.js +2 -2
  30. package/dist/bin/pushary-status.js +8 -8
  31. package/dist/bin/pushary-stop-hook.js +7 -7
  32. package/dist/bin/pushary-stopfailure-hook.js +7 -7
  33. package/dist/bin/pushary-upgrade.js +15 -15
  34. package/dist/bin/pushary-wait.js +3 -3
  35. package/dist/{chunk-SS72Z7FV.js → chunk-253WC6O5.js} +1 -1
  36. package/dist/{chunk-T3LRWZKI.js → chunk-2LPEZYMC.js} +1 -1
  37. package/dist/{chunk-O5B7EK5U.js → chunk-34GN3IPX.js} +1 -1
  38. package/dist/{chunk-V6N4Q223.js → chunk-3CUDMTL4.js} +1 -1
  39. package/dist/{chunk-VZKXBFUA.js → chunk-3HFK3UXM.js} +1 -1
  40. package/dist/{chunk-543KNJKC.js → chunk-3SQYKLBW.js} +3 -3
  41. package/dist/{chunk-ATBKJNWS.js → chunk-6EKQZA6R.js} +5 -1
  42. package/dist/{chunk-NKHEHEUM.js → chunk-6XUABEX2.js} +1 -1
  43. package/dist/{chunk-I5L6SK5V.js → chunk-BLODMQKJ.js} +1 -1
  44. package/dist/{chunk-DKDDLF5P.js → chunk-DKWM5T3P.js} +1 -1
  45. package/dist/{chunk-BAE45YLP.js → chunk-FZ73G2RO.js} +3 -3
  46. package/dist/{chunk-EJT2ER6G.js → chunk-GSSFSVAQ.js} +15 -21
  47. package/dist/{chunk-4XZCDSNG.js → chunk-GTZLL7F4.js} +5 -5
  48. package/dist/{chunk-PMHTCDEO.js → chunk-IUN6VOKV.js} +1 -1
  49. package/dist/{chunk-OFRNZWC4.js → chunk-IUWRVTQK.js} +1 -1
  50. package/dist/{chunk-J2JZJZKB.js → chunk-KVE5RCQL.js} +1 -1
  51. package/dist/{chunk-A4O5ROZK.js → chunk-MZGWKIUZ.js} +2 -2
  52. package/dist/chunk-NCVIWGVA.js +18 -0
  53. package/dist/{chunk-C623LWJR.js → chunk-OLAGKKSR.js} +1 -1
  54. package/dist/{chunk-EA3E74P2.js → chunk-PZRWFWSR.js} +6 -6
  55. package/dist/{chunk-VA7VUNRD.js → chunk-Q223OM5J.js} +58 -8
  56. package/dist/{chunk-GMI5QZLG.js → chunk-QA72MKIT.js} +5 -1
  57. package/dist/{chunk-AIGDBRIJ.js → chunk-SYUIXXQW.js} +1 -1
  58. package/dist/{chunk-TF247BET.js → chunk-TEZ2FZD6.js} +1 -1
  59. package/dist/{chunk-KOPUTJQC.js → chunk-TG3T3M4K.js} +1 -1
  60. package/dist/{chunk-FX5QSMPY.js → chunk-TRVIRETT.js} +1 -1
  61. package/dist/{chunk-U5TQCQ25.js → chunk-TY5HVMXT.js} +1 -1
  62. package/dist/chunk-UDBJHQMG.js +240 -0
  63. package/dist/{chunk-GKAKAOKC.js → chunk-UXHMWF4H.js} +64 -11
  64. package/dist/{chunk-PZA7G4CN.js → chunk-X4ZUUFKV.js} +1 -0
  65. package/dist/{chunk-L4C2JXJS.js → chunk-X6RXX2NF.js} +5 -0
  66. package/dist/src/index.js +11 -10
  67. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,28 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.89.10
4
+
5
+ ### Phone control delivery is crash-safe
6
+
7
+ Phone instructions now use per-command receipts across polling and relay
8
+ delivery. A process restart acknowledges the recorded outcome instead of
9
+ silently losing or repeating an instruction, multiple queued commands no longer
10
+ overwrite each other, and expired receipts are pruned when new commands arrive.
11
+
12
+ ### Setup and clean reverse only Pushary-owned changes
13
+
14
+ Setup records the Claude and Hermes configuration it replaces. Clean restores
15
+ that prior shape without deleting later user edits, and retains the receipt when
16
+ service removal, Hermes rollback, or plugin uninstall is incomplete so a retry
17
+ can finish safely. Installations created by older releases keep their compatible
18
+ best-effort cleanup path.
19
+
20
+ ### Native permission modes keep their intended owner
21
+
22
+ Codex now reads the permission mode it already reports, and Claude
23
+ `PermissionRequest` remains answerable from Pushary after the agent itself asks
24
+ for a human. Kill switches and standing denies still take precedence.
25
+
3
26
  ## 0.89.9
4
27
 
5
28
  ### Setup proves managed control reaches the server
@@ -1,16 +1,16 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  handleBell
4
- } from "../chunk-FX5QSMPY.js";
4
+ } from "../chunk-TRVIRETT.js";
5
5
  import {
6
6
  readHookInput
7
7
  } from "../chunk-6CUUA7HO.js";
8
- import "../chunk-V6N4Q223.js";
8
+ import "../chunk-3CUDMTL4.js";
9
9
  import "../chunk-BC3VCZ3E.js";
10
- import "../chunk-OFRNZWC4.js";
10
+ import "../chunk-IUWRVTQK.js";
11
11
  import "../chunk-XX2V4QS5.js";
12
12
  import "../chunk-2UMNXADU.js";
13
- import "../chunk-L4C2JXJS.js";
13
+ import "../chunk-X6RXX2NF.js";
14
14
 
15
15
  // bin/pushary-bell-hook.ts
16
16
  var main = async () => {
@@ -5,18 +5,18 @@ import {
5
5
  liveAgentCount,
6
6
  ring,
7
7
  upgradeThreshold
8
- } from "../chunk-FX5QSMPY.js";
8
+ } from "../chunk-TRVIRETT.js";
9
9
  import {
10
10
  guardBinary
11
- } from "../chunk-AIGDBRIJ.js";
12
- import "../chunk-PZA7G4CN.js";
11
+ } from "../chunk-SYUIXXQW.js";
12
+ import "../chunk-X4ZUUFKV.js";
13
13
  import {
14
14
  claudeSettings
15
- } from "../chunk-GMI5QZLG.js";
15
+ } from "../chunk-QA72MKIT.js";
16
16
  import {
17
17
  createIo
18
18
  } from "../chunk-5RUIFDTP.js";
19
- import "../chunk-V6N4Q223.js";
19
+ import "../chunk-3CUDMTL4.js";
20
20
  import {
21
21
  parseFlags
22
22
  } from "../chunk-GMXKITVA.js";
@@ -26,7 +26,7 @@ import {
26
26
  import "../chunk-3XTFX4CL.js";
27
27
  import "../chunk-BC3VCZ3E.js";
28
28
  import "../chunk-DINDL2CF.js";
29
- import "../chunk-OFRNZWC4.js";
29
+ import "../chunk-IUWRVTQK.js";
30
30
  import "../chunk-7QLSKOSU.js";
31
31
  import {
32
32
  EXIT
@@ -36,7 +36,7 @@ import {
36
36
  writeJsonAtomic
37
37
  } from "../chunk-XX2V4QS5.js";
38
38
  import "../chunk-2UMNXADU.js";
39
- import "../chunk-L4C2JXJS.js";
39
+ import "../chunk-X6RXX2NF.js";
40
40
 
41
41
  // src/bell/install.ts
42
42
  import { join } from "path";
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  ensureDaemonRunning
4
- } from "../chunk-543KNJKC.js";
4
+ } from "../chunk-3SQYKLBW.js";
5
5
  import {
6
6
  buildTranscriptRecords,
7
7
  decodePublicKey,
@@ -9,16 +9,16 @@ import {
9
9
  generateSessionKey,
10
10
  uploadTranscriptRecords,
11
11
  wrapSessionKey
12
- } from "../chunk-PMHTCDEO.js";
12
+ } from "../chunk-IUN6VOKV.js";
13
13
  import {
14
14
  startProcessOwnership,
15
15
  withSpawnProcessOwnership
16
- } from "../chunk-TF247BET.js";
16
+ } from "../chunk-TEZ2FZD6.js";
17
17
  import {
18
18
  needsShell,
19
19
  spawnClaude
20
20
  } from "../chunk-XHKBHWLX.js";
21
- import "../chunk-PZA7G4CN.js";
21
+ import "../chunk-X4ZUUFKV.js";
22
22
  import {
23
23
  isDeferAnswer
24
24
  } from "../chunk-YHG74UFF.js";
@@ -27,7 +27,7 @@ import {
27
27
  getPolicy,
28
28
  repoKeyFor,
29
29
  reportEvent
30
- } from "../chunk-4XZCDSNG.js";
30
+ } from "../chunk-GTZLL7F4.js";
31
31
  import {
32
32
  DEFAULT_SESSION,
33
33
  askUser,
@@ -35,23 +35,24 @@ import {
35
35
  markPendingCommandDelivered,
36
36
  markPendingCommandFailed,
37
37
  readPendingCommand,
38
+ readPendingCommandById,
38
39
  removePendingCommand,
39
40
  savePendingCommand,
40
41
  waitForAnswer
41
- } from "../chunk-GKAKAOKC.js";
42
+ } from "../chunk-UXHMWF4H.js";
42
43
  import {
43
44
  deriveToolTarget,
44
45
  describeToolCall,
45
46
  scopePathFor
46
- } from "../chunk-KOPUTJQC.js";
47
+ } from "../chunk-TG3T3M4K.js";
47
48
  import {
48
49
  getMachineId
49
- } from "../chunk-DKDDLF5P.js";
50
- import "../chunk-GMI5QZLG.js";
50
+ } from "../chunk-DKWM5T3P.js";
51
+ import "../chunk-QA72MKIT.js";
51
52
  import "../chunk-DINDL2CF.js";
52
53
  import "../chunk-BSZYIAZL.js";
53
54
  import "../chunk-SAF6HGAA.js";
54
- import "../chunk-OFRNZWC4.js";
55
+ import "../chunk-IUWRVTQK.js";
55
56
  import "../chunk-7QLSKOSU.js";
56
57
  import "../chunk-KZERVKTD.js";
57
58
  import "../chunk-XX2V4QS5.js";
@@ -62,7 +63,7 @@ import {
62
63
  import {
63
64
  KILL_REASON,
64
65
  resolveGate
65
- } from "../chunk-L4C2JXJS.js";
66
+ } from "../chunk-X6RXX2NF.js";
66
67
 
67
68
  // src/wrapper/claudeBinary.ts
68
69
  import { statSync } from "fs";
@@ -664,20 +665,8 @@ var pollAgent = async (apiKey, sessionId, wantCommand, signal, baseUrl = getBase
664
665
  try {
665
666
  const sessionKey = sessionId || DEFAULT_SESSION;
666
667
  const receiptNamespace = `${apiKey}:${machineId}:claude_code`;
667
- const storedCommand = wantCommand && machineId ? readPendingCommand(sessionKey, receiptNamespace) : null;
668
- const response = await fetch(`${baseUrl}/api/agent/poll`, {
669
- method: "POST",
670
- headers: { "Content-Type": "application/json", Authorization: `Bearer ${apiKey}` },
671
- body: JSON.stringify({
672
- ...sessionId ? { sessionId } : {},
673
- ...machineId ? { machineId } : {},
674
- ...wantCommand && !storedCommand ? {} : { drain: false },
675
- ...wantCommand && !storedCommand && machineId ? { ackMode: true } : {}
676
- }),
677
- signal
678
- });
679
- if (!response.ok) return { command: null, mode: null };
680
- const data = await response.json();
668
+ let storedCommand = machineId ? readPendingCommand(sessionKey, receiptNamespace) : null;
669
+ let acknowledgedCommandId = null;
681
670
  const acknowledge = async (id, state) => {
682
671
  const persisted = state === "delivered" ? markPendingCommandDelivered(sessionKey, id, receiptNamespace) : markPendingCommandFailed(sessionKey, id, receiptNamespace);
683
672
  if (!persisted) return false;
@@ -700,6 +689,29 @@ var pollAgent = async (apiKey, sessionId, wantCommand, signal, baseUrl = getBase
700
689
  return false;
701
690
  }
702
691
  };
692
+ if (storedCommand?.delivered || storedCommand?.failed) {
693
+ const acknowledged = await acknowledge(
694
+ storedCommand.id,
695
+ storedCommand.failed ? "failed" : "delivered"
696
+ );
697
+ if (acknowledged) {
698
+ acknowledgedCommandId = storedCommand.id;
699
+ storedCommand = null;
700
+ }
701
+ }
702
+ const response = await fetch(`${baseUrl}/api/agent/poll`, {
703
+ method: "POST",
704
+ headers: { "Content-Type": "application/json", Authorization: `Bearer ${apiKey}` },
705
+ body: JSON.stringify({
706
+ ...sessionId ? { sessionId } : {},
707
+ ...machineId ? { machineId } : {},
708
+ ...wantCommand && !storedCommand ? {} : { drain: false },
709
+ ...wantCommand && !storedCommand && machineId ? { ackMode: true } : {}
710
+ }),
711
+ signal
712
+ });
713
+ if (!response.ok) return { command: null, mode: null };
714
+ const data = await response.json();
703
715
  const claimedCommand = (id, text) => ({
704
716
  id,
705
717
  text,
@@ -707,15 +719,10 @@ var pollAgent = async (apiKey, sessionId, wantCommand, signal, baseUrl = getBase
707
719
  });
708
720
  let command = typeof data.command === "string" && data.command.length > 0 ? data.command : null;
709
721
  if (storedCommand) {
710
- if (storedCommand.delivered || storedCommand.failed) {
711
- await acknowledge(storedCommand.id, storedCommand.failed ? "failed" : "delivered");
712
- command = null;
713
- } else {
714
- command = claimedCommand(storedCommand.id, storedCommand.text);
715
- }
722
+ command = storedCommand.delivered || storedCommand.failed ? null : claimedCommand(storedCommand.id, storedCommand.text);
716
723
  } else if (data.command && typeof data.command === "object") {
717
724
  const claimed = data.command;
718
- if (typeof claimed.id === "string" && typeof claimed.text === "string") {
725
+ if (typeof claimed.id === "string" && typeof claimed.text === "string" && claimed.id !== acknowledgedCommandId) {
719
726
  const saved = savePendingCommand(sessionKey, {
720
727
  id: claimed.id,
721
728
  text: claimed.text,
@@ -780,6 +787,7 @@ var decodeServerFrame = (raw) => {
780
787
  var DEFAULT_HEARTBEAT_MS = 2e4;
781
788
  var DEFAULT_PONG_TIMEOUT_MS = 1e4;
782
789
  var DEFAULT_MAX_SEEN_COMMANDS = 256;
790
+ var COMMAND_RECEIPT_TTL_MS = 36e5;
783
791
  var defaultBackoff = (attempt) => Math.min(1e3 * 2 ** attempt, 3e4);
784
792
  var defaultSocketFactory = (url) => {
785
793
  const WS = globalThis.WebSocket;
@@ -810,6 +818,17 @@ var createRelayClient = (options) => {
810
818
  const seenCommands = [];
811
819
  const seenSet = /* @__PURE__ */ new Set();
812
820
  const log = (message) => options.log?.(message);
821
+ const receiptNamespace = `${options.apiKey}:${options.machineId}:${options.agentType}`;
822
+ const acknowledgeStored = (id) => {
823
+ const receipt = readPendingCommandById(
824
+ options.getSessionId() || DEFAULT_SESSION,
825
+ id,
826
+ receiptNamespace
827
+ );
828
+ if (!receipt || !receipt.delivered && !receipt.failed) return false;
829
+ sendFrame(receipt.failed ? { v: PROTOCOL_VERSION, t: "ack", id, state: "failed", failureCode: "consumer_rejected" } : { v: PROTOCOL_VERSION, t: "ack", id, state: "delivered" });
830
+ return true;
831
+ };
813
832
  const clearTimers = () => {
814
833
  if (heartbeatTimer) clearInterval(heartbeatTimer);
815
834
  if (pongTimer) clearTimeout(pongTimer);
@@ -870,12 +889,24 @@ var createRelayClient = (options) => {
870
889
  sendFrame({ v: PROTOCOL_VERSION, t: "ack", id: frame.id, state: "delivered" });
871
890
  break;
872
891
  }
892
+ if (acknowledgeStored(frame.id)) break;
893
+ const sessionKey = options.getSessionId() || DEFAULT_SESSION;
894
+ if (!savePendingCommand(sessionKey, {
895
+ id: frame.id,
896
+ text: frame.text,
897
+ expiresAtMs: Date.now() + COMMAND_RECEIPT_TTL_MS,
898
+ delivered: false,
899
+ failed: true
900
+ }, receiptNamespace)) {
901
+ acknowledgeStored(frame.id);
902
+ break;
903
+ }
873
904
  let accepted = false;
874
905
  try {
875
906
  accepted = options.onCommand(frame.text) !== false;
876
907
  } catch {
877
908
  }
878
- if (accepted) {
909
+ if (accepted && markPendingCommandDelivered(sessionKey, frame.id, receiptNamespace)) {
879
910
  rememberCommand(frame.id);
880
911
  sendFrame({ v: PROTOCOL_VERSION, t: "ack", id: frame.id, state: "delivered" });
881
912
  } else {
@@ -1,10 +1,15 @@
1
1
  #!/usr/bin/env node
2
+ import {
3
+ HERMES_CONFIG_SCRIPT,
4
+ resolveHermesPython,
5
+ runHermesPythonScript
6
+ } from "../chunk-UDBJHQMG.js";
2
7
  import {
3
8
  removeInstructionBlock,
4
9
  shortenHome,
5
10
  unregisterPluginLocation,
6
11
  vscodeSettingsTargets
7
- } from "../chunk-A4O5ROZK.js";
12
+ } from "../chunk-MZGWKIUZ.js";
8
13
  import {
9
14
  OPENCODE_PLUGIN_SIBLING,
10
15
  OPENCODE_PLUGIN_SIBLING_CONTENTS,
@@ -14,25 +19,28 @@ import {
14
19
  openCodePluginDir,
15
20
  openCodePluginFile,
16
21
  removeOpenCodeMcpServer
17
- } from "../chunk-O5B7EK5U.js";
22
+ } from "../chunk-34GN3IPX.js";
18
23
  import {
24
+ isClaudeMcpSnapshot,
19
25
  removeClaudeMcpServers,
20
26
  removeGeminiSettings,
21
- removePusharySettings
22
- } from "../chunk-VA7VUNRD.js";
23
- import "../chunk-ATBKJNWS.js";
24
- import "../chunk-AIGDBRIJ.js";
27
+ removePusharySettings,
28
+ restoreClaudeMcpServer
29
+ } from "../chunk-Q223OM5J.js";
30
+ import "../chunk-6EKQZA6R.js";
31
+ import "../chunk-SYUIXXQW.js";
25
32
  import {
26
33
  disableDaemonService
27
- } from "../chunk-543KNJKC.js";
34
+ } from "../chunk-3SQYKLBW.js";
28
35
  import "../chunk-XHKBHWLX.js";
29
36
  import {
30
37
  execNpm
31
- } from "../chunk-PZA7G4CN.js";
32
- import "../chunk-KOPUTJQC.js";
33
- import "../chunk-DKDDLF5P.js";
38
+ } from "../chunk-X4ZUUFKV.js";
39
+ import "../chunk-TG3T3M4K.js";
40
+ import "../chunk-DKWM5T3P.js";
34
41
  import {
35
42
  claudeJson,
43
+ claudeMcpReceipt,
36
44
  claudeSettings,
37
45
  claudeSettingsLocal,
38
46
  claudeSkillDir,
@@ -46,11 +54,12 @@ import {
46
54
  cursorUserMcp,
47
55
  geminiMd,
48
56
  geminiSettings,
57
+ hermesConfigReceipt,
49
58
  pusharyDir,
50
59
  removeCodexHookTrust,
51
60
  removeCodexHooks,
52
61
  vscodePluginDir
53
- } from "../chunk-GMI5QZLG.js";
62
+ } from "../chunk-QA72MKIT.js";
54
63
  import {
55
64
  rejectUnknownFlags
56
65
  } from "../chunk-GMXKITVA.js";
@@ -71,13 +80,12 @@ import "../chunk-2UMNXADU.js";
71
80
  import {
72
81
  CURSOR_GATE_EVENTS,
73
82
  HOOK_LOCATOR_BINARY
74
- } from "../chunk-L4C2JXJS.js";
83
+ } from "../chunk-X6RXX2NF.js";
75
84
 
76
85
  // bin/pushary-clean.ts
77
86
  import { existsSync, readFileSync, copyFileSync, readdirSync } from "fs";
78
87
  import { join } from "path";
79
88
  import { homedir, tmpdir } from "os";
80
- import { execSync as execSync2 } from "child_process";
81
89
  import { confirm } from "@inquirer/prompts";
82
90
  import { parse as parseTOML, stringify as stringifyTOML } from "smol-toml";
83
91
 
@@ -128,6 +136,8 @@ var incompleteClean = false;
128
136
  var CLAUDE_SETTINGS = claudeSettings();
129
137
  var CLAUDE_SETTINGS_LOCAL = claudeSettingsLocal();
130
138
  var CLAUDE_JSON = claudeJson();
139
+ var CLAUDE_MCP_RECEIPT = claudeMcpReceipt();
140
+ var HERMES_CONFIG_RECEIPT = hermesConfigReceipt();
131
141
  var SKILL_DIR = claudeSkillDir();
132
142
  var CURSOR_MCP = cursorUserMcp();
133
143
  var CURSOR_PLUGIN_DIR = cursorPluginDir();
@@ -140,22 +150,6 @@ var removalScope = () => ({
140
150
  keepAppWiring: true,
141
151
  keepMcpServer: macAppInstalled()
142
152
  });
143
- var resolveHermesPython = () => {
144
- try {
145
- const launcher = execSync2("command -v hermes", { encoding: "utf-8", stdio: "pipe", timeout: 5e3 }).trim();
146
- if (launcher) {
147
- const shebang = readFileSync(launcher, "utf-8").split("\n", 1)[0];
148
- if (shebang.startsWith("#!")) {
149
- const interpreter = shebang.slice(2).trim().split(/\s+/)[0];
150
- if (interpreter && /python/i.test(interpreter) && existsSync(interpreter)) return interpreter;
151
- }
152
- }
153
- } catch {
154
- }
155
- const venvPython = join(homedir(), ".hermes", "hermes-agent", "venv", "bin", "python3");
156
- if (existsSync(venvPython)) return venvPython;
157
- return null;
158
- };
159
153
  var readJson = (path) => {
160
154
  try {
161
155
  return JSON.parse(readFileSync(path, "utf-8"));
@@ -305,12 +299,21 @@ var main = async () => {
305
299
  if (keepShared) {
306
300
  console.log(` ${skip} Claude Code MCP servers ${dim("(kept: the Mac app uses it)")}`);
307
301
  } else if (claudeJson2) {
308
- if (removeClaudeMcpServers(claudeJson2)) {
302
+ const rawReceipt = readJson(CLAUDE_MCP_RECEIPT);
303
+ const receipt = isClaudeMcpSnapshot(rawReceipt) ? rawReceipt : null;
304
+ if (existsSync(CLAUDE_MCP_RECEIPT) && !receipt) {
305
+ incompleteClean = true;
306
+ console.log(` ${skip} Claude Code MCP servers ${dim("(setup receipt is unreadable; left unchanged)")}`);
307
+ } else if (receipt && restoreClaudeMcpServer(claudeJson2, receipt) || !receipt && removeClaudeMcpServers(claudeJson2)) {
309
308
  writeJson(CLAUDE_JSON, claudeJson2);
310
- console.log(` ${check} Claude Code MCP servers ${dim("(removed from ~/.claude.json)")}`);
309
+ console.log(` ${check} Claude Code MCP servers ${dim(receipt ? "(previous entry restored)" : "(removed from ~/.claude.json)")}`);
311
310
  } else {
312
- console.log(` ${skip} Claude Code MCP servers ${dim("(no pushary entries)")}`);
311
+ if (receipt) incompleteClean = true;
312
+ console.log(` ${skip} Claude Code MCP servers ${dim(receipt ? "(changed after setup; left unchanged)" : "(no pushary entries)")}`);
313
313
  }
314
+ } else if (existsSync(CLAUDE_MCP_RECEIPT)) {
315
+ incompleteClean = true;
316
+ console.log(` ${skip} Claude Code MCP servers ${dim("(config missing; setup receipt kept for retry)")}`);
314
317
  } else {
315
318
  console.log(` ${skip} Claude Code MCP servers ${dim("(not found)")}`);
316
319
  }
@@ -487,19 +490,23 @@ var main = async () => {
487
490
  }
488
491
  const hermesPython = resolveHermesPython();
489
492
  if (hermesPython) {
490
- const snippet = 'from hermes_cli.config import load_config, save_config; c = load_config(); p = c.get("plugins") if isinstance(c.get("plugins"), dict) else {}; e = p.get("enabled") if isinstance(p.get("enabled"), list) else []; p["enabled"] = [x for x in e if x != "pushary"]; c["plugins"] = p; a = c.get("agent") if isinstance(c.get("agent"), dict) else {}; d = a.get("disabled_toolsets") if isinstance(a.get("disabled_toolsets"), list) else []; a["disabled_toolsets"] = [x for x in d if x != "clarify"]; c["agent"] = a; save_config(c)';
491
- try {
492
- guardedExec(`"${hermesPython}" -c '${snippet}'`, { stdio: "pipe", timeout: 15e3 }, "hermes plugin config update");
493
- console.log(` ${check} Hermes config ${dim("(plugin disabled, clarify toolset restored)")}`);
494
- } catch {
493
+ const configUpdated = dryRun ? (wouldNote("run ", "hermes plugin config restore"), true) : runHermesPythonScript(hermesPython, HERMES_CONFIG_SCRIPT, "clean", HERMES_CONFIG_RECEIPT);
494
+ if (!configUpdated) {
495
+ incompleteClean = true;
495
496
  console.log(` ${skip} Hermes config ${dim("(could not update config.yaml)")}`);
497
+ } else {
498
+ console.log(` ${check} Hermes config ${dim("(setup-owned changes restored)")}`);
499
+ try {
500
+ guardedExec(`"${hermesPython}" -m pip uninstall -y hermes-plugin-pushary`, { stdio: "pipe", timeout: 6e4 }, "pip uninstall hermes-plugin-pushary");
501
+ console.log(` ${check} Hermes plugin ${dim("(pip uninstalled)")}`);
502
+ } catch {
503
+ incompleteClean = true;
504
+ console.log(` ${skip} Hermes plugin ${dim("(could not uninstall; setup receipt kept for retry)")}`);
505
+ }
496
506
  }
497
- try {
498
- guardedExec(`"${hermesPython}" -m pip uninstall -y hermes-plugin-pushary`, { stdio: "pipe", timeout: 6e4 }, "pip uninstall hermes-plugin-pushary");
499
- console.log(` ${check} Hermes plugin ${dim("(pip uninstalled)")}`);
500
- } catch {
501
- console.log(` ${skip} Hermes plugin ${dim("(not installed)")}`);
502
- }
507
+ } else if (existsSync(HERMES_CONFIG_RECEIPT)) {
508
+ incompleteClean = true;
509
+ console.log(` ${skip} Hermes ${dim("(runtime not found; setup receipt kept for retry)")}`);
503
510
  } else {
504
511
  console.log(` ${skip} Hermes ${dim("(not found)")}`);
505
512
  }
@@ -534,7 +541,7 @@ var main = async () => {
534
541
  let removed = 0;
535
542
  const kept = [];
536
543
  for (const entry of readdirSync(PUSHARY_DIR)) {
537
- if (keepShared && isAppOwned(entry)) {
544
+ if (keepShared && isAppOwned(entry) || entry === "setup" && (keepShared || incompleteClean)) {
538
545
  kept.push(entry);
539
546
  continue;
540
547
  }
@@ -548,7 +555,7 @@ var main = async () => {
548
555
  console.log(` ${skip} Local state ${dim("(nothing of ours in ~/.pushary)")}`);
549
556
  }
550
557
  if (kept.length > 0) {
551
- console.log(` ${dim(`left ${kept.sort().join(", ")} for the Mac app`)}`);
558
+ console.log(` ${dim(`left ${kept.sort().join(", ")} for ${keepShared ? "the Mac app" : "a cleanup retry"}`)}`);
552
559
  }
553
560
  } else {
554
561
  console.log(` ${skip} Local state ${dim("(~/.pushary not found)")}`);
@@ -1,33 +1,33 @@
1
1
  #!/usr/bin/env node
2
- import "../chunk-NKHEHEUM.js";
2
+ import "../chunk-6XUABEX2.js";
3
3
  import {
4
4
  LiveTranscriptRelay,
5
5
  liveSessionSleep,
6
6
  postLiveSession,
7
7
  postLiveSessionUntilAccepted,
8
8
  startLiveSessionHeartbeat
9
- } from "../chunk-C623LWJR.js";
10
- import "../chunk-PMHTCDEO.js";
9
+ } from "../chunk-OLAGKKSR.js";
10
+ import "../chunk-IUN6VOKV.js";
11
11
  import {
12
12
  startProcessOwnership,
13
13
  withSpawnProcessOwnership
14
- } from "../chunk-TF247BET.js";
14
+ } from "../chunk-TEZ2FZD6.js";
15
15
  import {
16
16
  spawnClaude
17
17
  } from "../chunk-XHKBHWLX.js";
18
18
  import {
19
19
  resolveBinary
20
- } from "../chunk-PZA7G4CN.js";
20
+ } from "../chunk-X4ZUUFKV.js";
21
21
  import {
22
22
  getMachineId
23
- } from "../chunk-DKDDLF5P.js";
24
- import "../chunk-GMI5QZLG.js";
23
+ } from "../chunk-DKWM5T3P.js";
24
+ import "../chunk-QA72MKIT.js";
25
25
  import "../chunk-XX2V4QS5.js";
26
26
  import {
27
27
  getApiKey,
28
28
  getBaseUrl
29
29
  } from "../chunk-2UMNXADU.js";
30
- import "../chunk-L4C2JXJS.js";
30
+ import "../chunk-X6RXX2NF.js";
31
31
 
32
32
  // bin/pushary-codex-bridge.ts
33
33
  import { existsSync } from "fs";
@@ -1,8 +1,11 @@
1
1
  #!/usr/bin/env node
2
+ import {
3
+ defersToNativeMode
4
+ } from "../chunk-NCVIWGVA.js";
2
5
  import {
3
6
  isGatingMoment,
4
7
  recordKeylessMoment
5
- } from "../chunk-VZKXBFUA.js";
8
+ } from "../chunk-3HFK3UXM.js";
6
9
  import {
7
10
  readHookInput
8
11
  } from "../chunk-6CUUA7HO.js";
@@ -31,7 +34,7 @@ import {
31
34
  toCodexWire,
32
35
  toPolicyLookup,
33
36
  toPolicyLookups
34
- } from "../chunk-4XZCDSNG.js";
37
+ } from "../chunk-GTZLL7F4.js";
35
38
  import {
36
39
  DEFAULT_SESSION,
37
40
  askUser,
@@ -39,23 +42,23 @@ import {
39
42
  pollForAnswer,
40
43
  savePendingQuestion,
41
44
  sendNotification
42
- } from "../chunk-GKAKAOKC.js";
45
+ } from "../chunk-UXHMWF4H.js";
43
46
  import {
44
47
  deriveActionBody,
45
48
  deriveBlocker,
46
49
  deriveToolTarget,
47
50
  describeToolCall,
48
51
  scopePathFor
49
- } from "../chunk-KOPUTJQC.js";
52
+ } from "../chunk-TG3T3M4K.js";
50
53
  import {
51
54
  getMachineId
52
- } from "../chunk-DKDDLF5P.js";
55
+ } from "../chunk-DKWM5T3P.js";
53
56
  import {
54
57
  canonicalJson
55
- } from "../chunk-GMI5QZLG.js";
58
+ } from "../chunk-QA72MKIT.js";
56
59
  import "../chunk-BSZYIAZL.js";
57
60
  import "../chunk-SAF6HGAA.js";
58
- import "../chunk-OFRNZWC4.js";
61
+ import "../chunk-IUWRVTQK.js";
59
62
  import "../chunk-7QLSKOSU.js";
60
63
  import "../chunk-KZERVKTD.js";
61
64
  import "../chunk-XX2V4QS5.js";
@@ -69,7 +72,7 @@ import {
69
72
  hookWaitClamped,
70
73
  hookWaitDeadline,
71
74
  resolveGate
72
- } from "../chunk-L4C2JXJS.js";
75
+ } from "../chunk-X6RXX2NF.js";
73
76
 
74
77
  // bin/pushary-codex-hook.ts
75
78
  import { basename } from "path";
@@ -313,6 +316,7 @@ var decidePreToolUse = async (input) => {
313
316
  if (verdict.kind === "kill") return codexDeny(KILL_REASON);
314
317
  if (verdict.kind === "allow") return codexPass();
315
318
  if (verdict.kind === "deny") return codexDeny(verdict.reason);
319
+ if (defersToNativeMode(input.permission_mode, input.tool_name ?? "")) return codexPass();
316
320
  if (verdict.kind === "defer") return codexPass();
317
321
  const toolPolicy = verdict.policy;
318
322
  const callKey = callKeyFor(input);
@@ -1,26 +1,26 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  reportEvent
4
- } from "../chunk-4XZCDSNG.js";
4
+ } from "../chunk-GTZLL7F4.js";
5
5
  import {
6
6
  askUser,
7
7
  waitForAnswer
8
- } from "../chunk-GKAKAOKC.js";
9
- import "../chunk-KOPUTJQC.js";
8
+ } from "../chunk-UXHMWF4H.js";
9
+ import "../chunk-TG3T3M4K.js";
10
10
  import {
11
11
  getMachineId
12
- } from "../chunk-DKDDLF5P.js";
13
- import "../chunk-GMI5QZLG.js";
12
+ } from "../chunk-DKWM5T3P.js";
13
+ import "../chunk-QA72MKIT.js";
14
14
  import "../chunk-BSZYIAZL.js";
15
15
  import "../chunk-SAF6HGAA.js";
16
- import "../chunk-OFRNZWC4.js";
16
+ import "../chunk-IUWRVTQK.js";
17
17
  import "../chunk-7QLSKOSU.js";
18
18
  import "../chunk-KZERVKTD.js";
19
19
  import "../chunk-XX2V4QS5.js";
20
20
  import {
21
21
  getApiKey
22
22
  } from "../chunk-2UMNXADU.js";
23
- import "../chunk-L4C2JXJS.js";
23
+ import "../chunk-X6RXX2NF.js";
24
24
 
25
25
  // bin/pushary-codex.ts
26
26
  import { basename } from "path";
@@ -1,22 +1,22 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  printKeyCheck
4
- } from "../chunk-SS72Z7FV.js";
4
+ } from "../chunk-253WC6O5.js";
5
5
  import {
6
6
  confirmAppConnection,
7
7
  connectDevice,
8
8
  printConnectInstructions
9
- } from "../chunk-T3LRWZKI.js";
9
+ } from "../chunk-2LPEZYMC.js";
10
10
  import "../chunk-3EGEA4KH.js";
11
11
  import {
12
12
  checkApiKey
13
- } from "../chunk-I5L6SK5V.js";
13
+ } from "../chunk-BLODMQKJ.js";
14
14
  import {
15
15
  createIo
16
16
  } from "../chunk-5RUIFDTP.js";
17
17
  import {
18
18
  readKeySource
19
- } from "../chunk-V6N4Q223.js";
19
+ } from "../chunk-3CUDMTL4.js";
20
20
  import {
21
21
  parseFlags
22
22
  } from "../chunk-GMXKITVA.js";
@@ -28,7 +28,7 @@ import "../chunk-BC3VCZ3E.js";
28
28
  import "../chunk-DINDL2CF.js";
29
29
  import "../chunk-BSZYIAZL.js";
30
30
  import "../chunk-SAF6HGAA.js";
31
- import "../chunk-OFRNZWC4.js";
31
+ import "../chunk-IUWRVTQK.js";
32
32
  import {
33
33
  UsageError
34
34
  } from "../chunk-7QLSKOSU.js";
@@ -37,7 +37,7 @@ import {
37
37
  } from "../chunk-KZERVKTD.js";
38
38
  import "../chunk-XX2V4QS5.js";
39
39
  import "../chunk-2UMNXADU.js";
40
- import "../chunk-L4C2JXJS.js";
40
+ import "../chunk-X6RXX2NF.js";
41
41
 
42
42
  // bin/pushary-connect.ts
43
43
  exitOnHelpFlag("connect");