@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,26 +1,30 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  inspectConfiguredManagedProviders
4
- } from "../chunk-5KQN7AIL.js";
4
+ } from "../chunk-D4X4BQVQ.js";
5
5
  import {
6
6
  HEARTBEAT_INTERVAL_MS,
7
7
  sendHeartbeat
8
- } from "../chunk-QQ7VXRIN.js";
9
- import "../chunk-WHM3OVBA.js";
8
+ } from "../chunk-JCZWWBBA.js";
9
+ import "../chunk-SHW6KK2K.js";
10
10
  import {
11
11
  readSignedOutDiagnosis
12
- } from "../chunk-RRW2LVVR.js";
13
- import "../chunk-MWR25RQZ.js";
12
+ } from "../chunk-HXV2SY2V.js";
13
+ import {
14
+ HERMES_INSTALL_MARKER_FILE,
15
+ defaultHermesHome,
16
+ parseHermesInstallMarker
17
+ } from "../chunk-VGHWK5Z2.js";
14
18
  import {
15
19
  writeHuman
16
20
  } from "../chunk-QCGNGEGE.js";
17
- import "../chunk-CR3HOHJC.js";
18
- import "../chunk-R4B5AVFC.js";
19
- import "../chunk-PWVQALO2.js";
20
- import "../chunk-37OD7FUI.js";
21
+ import "../chunk-MD4UJICD.js";
22
+ import "../chunk-7O54GE5C.js";
23
+ import "../chunk-UU3CDLIX.js";
24
+ import "../chunk-E5XYFIYL.js";
21
25
  import {
22
26
  exitOnHelpFlag
23
- } from "../chunk-NVM4S37G.js";
27
+ } from "../chunk-XO7LP3JC.js";
24
28
  import {
25
29
  codexBinaryIsBroken,
26
30
  detectProviderLiveCapabilities,
@@ -29,8 +33,8 @@ import {
29
33
  launchableProviders,
30
34
  resolveControlPolicy,
31
35
  trackProviderInventory
32
- } from "../chunk-EEQG7VF2.js";
33
- import "../chunk-S6BD346V.js";
36
+ } from "../chunk-KDUWFGY2.js";
37
+ import "../chunk-QDUDXJAF.js";
34
38
  import {
35
39
  SESSION_REAP_SILENCE_MS,
36
40
  describeIdleWindow,
@@ -38,16 +42,16 @@ import {
38
42
  listProcessProofs,
39
43
  ownsLiveProcess,
40
44
  silentSpawnedProofs
41
- } from "../chunk-YDSOTAZ6.js";
42
- import "../chunk-LFBQ55OO.js";
45
+ } from "../chunk-FEC3JUIH.js";
46
+ import "../chunk-R7V6BPEV.js";
43
47
  import {
44
48
  readLocalAutoUpdateReport
45
- } from "../chunk-L6F62LKX.js";
49
+ } from "../chunk-M7BXAWUC.js";
46
50
  import {
47
51
  startRegisteredTranscripts
48
- } from "../chunk-RO5G3OEN.js";
49
- import "../chunk-SAXNYDPS.js";
50
- import "../chunk-KFGRDWAW.js";
52
+ } from "../chunk-ZX3KEXO3.js";
53
+ import "../chunk-T6RWREWU.js";
54
+ import "../chunk-3SAF4QXU.js";
51
55
  import {
52
56
  DAEMON_LEASE_RENEW_INTERVAL_MS,
53
57
  acquireDaemonLease,
@@ -64,52 +68,1273 @@ import {
64
68
  requestDaemonHandoff,
65
69
  stopDaemonRunning,
66
70
  writeDaemonNeedsLogin
67
- } from "../chunk-QDISUNK3.js";
68
- import "../chunk-LCBZLTHW.js";
69
- import "../chunk-SMVTNLXU.js";
70
- import "../chunk-TM4JH7YR.js";
71
+ } from "../chunk-DCHZ5JOI.js";
72
+ import {
73
+ hookOwnerOf
74
+ } from "../chunk-TPYZFYQ5.js";
75
+ import "../chunk-NCTABQMC.js";
76
+ import "../chunk-4DH54LS7.js";
71
77
  import "../chunk-NMUI3ZWG.js";
72
- import "../chunk-CAUNUTYE.js";
73
- import "../chunk-VZG76CWC.js";
78
+ import "../chunk-SONZCJY5.js";
79
+ import "../chunk-XMSDA5WA.js";
80
+ import "../chunk-RPKFXNOJ.js";
74
81
  import {
75
82
  spawnClaude
76
83
  } from "../chunk-XHKBHWLX.js";
77
84
  import {
78
85
  readControlMode
79
- } from "../chunk-6YZ72YP3.js";
80
- import "../chunk-EEFFP64A.js";
81
- import "../chunk-ILWEGI4H.js";
82
- import "../chunk-DU7L7R4A.js";
86
+ } from "../chunk-LIDUPDEY.js";
87
+ import {
88
+ pendingCommandReceipts,
89
+ removePendingCommand
90
+ } from "../chunk-JFVLMWKY.js";
91
+ import "../chunk-DUOBFECC.js";
92
+ import "../chunk-EOGVWCLF.js";
83
93
  import "../chunk-POFBE6ZY.js";
84
- import "../chunk-E56VJY23.js";
85
- import "../chunk-4TRA3GDE.js";
86
- import "../chunk-IBHZ3JFE.js";
94
+ import "../chunk-32JJ5KV3.js";
95
+ import "../chunk-SIJHPGUW.js";
96
+ import "../chunk-SOHJ37NA.js";
87
97
  import {
88
98
  getMachineId
89
- } from "../chunk-5XYYBB5Z.js";
90
- import "../chunk-PPY5N2E6.js";
99
+ } from "../chunk-BGEUAJYL.js";
100
+ import "../chunk-DARPCQPY.js";
91
101
  import {
92
- EXIT
93
- } from "../chunk-KZERVKTD.js";
94
- import "../chunk-ZWCKS65X.js";
95
- import "../chunk-N4NSX5MB.js";
102
+ claudeConfigDir,
103
+ codexHome
104
+ } from "../chunk-2J4IVUEA.js";
96
105
  import {
97
- resolveBinary
98
- } from "../chunk-GUJBS6TK.js";
106
+ resolveBinary,
107
+ writeJsonAtomicDurable
108
+ } from "../chunk-AUDYAVNM.js";
99
109
  import {
100
110
  getApiKey,
101
111
  getBaseUrl
102
- } from "../chunk-BMVWGPYB.js";
103
- import "../chunk-7KMLITXH.js";
112
+ } from "../chunk-53D6FFVE.js";
113
+ import "../chunk-PPY5N2E6.js";
114
+ import {
115
+ EXIT
116
+ } from "../chunk-KZERVKTD.js";
117
+ import "../chunk-UIS332AZ.js";
104
118
  import "../chunk-QTOLNRET.js";
105
- import "../chunk-5XKJH5GD.js";
119
+ import {
120
+ CLAUDE_INBOX,
121
+ CLAUDE_INBOX_MAX_SESSIONS,
122
+ CLAUDE_INBOX_RECEIVER,
123
+ CLAUDE_INBOX_SETTLEMENTS,
124
+ CLAUDE_WAKE,
125
+ CLAUDE_WAKE_API,
126
+ COMMAND_RECOVERY,
127
+ NATIVE_INPUT,
128
+ NativeInputAgent,
129
+ claudeInboxAction,
130
+ claudeWakeDecision,
131
+ claudeWakeWaitingOn,
132
+ hermesPendingAdmissionId,
133
+ hermesReplayAdmissionId,
134
+ isClaudeInboxReceipt,
135
+ isCodexInputReceipt,
136
+ isCodexQueuedReceipt,
137
+ isCommandAcknowledgementReceipt,
138
+ nativeIdentifier,
139
+ nativeInputAction,
140
+ nativeRecord,
141
+ parseClaudeInboxClaim,
142
+ parseClaudeInboxPoll,
143
+ parseClaudeInboxSession,
144
+ parseHermesAdmission,
145
+ parseHermesReplayCursor,
146
+ qualifyHermesReceiptMode
147
+ } from "../chunk-QRS7FQHE.js";
106
148
  import "../chunk-MLKGABMK.js";
107
149
 
108
- // src/spawn-daemon.ts
109
- import { execFileSync, spawn } from "child_process";
110
- import { existsSync, statSync } from "fs";
150
+ // src/native-host-receiver.ts
151
+ import { join as join2 } from "path";
111
152
  import { homedir } from "os";
112
- import { basename, dirname, join } from "path";
153
+ import { readFileSync as readFileSync3 } from "fs";
154
+
155
+ // src/native-host.ts
156
+ import WebSocket from "ws";
157
+ import { createConnection } from "net";
158
+ import { createHash } from "crypto";
159
+ import {
160
+ readdirSync,
161
+ lstatSync,
162
+ realpathSync,
163
+ readFileSync,
164
+ mkdirSync,
165
+ openSync,
166
+ writeSync,
167
+ fsyncSync,
168
+ closeSync
169
+ } from "fs";
170
+ import { dirname, join, resolve } from "path";
171
+ var privateNode = (path, kind) => {
172
+ try {
173
+ const s = lstatSync(path);
174
+ return s.uid === process.getuid?.() && (s.mode & 63) === 0 && (kind === "socket" ? s.isSocket() : s.isDirectory());
175
+ } catch {
176
+ return false;
177
+ }
178
+ };
179
+ var ownerControlledDirectory = (path) => {
180
+ try {
181
+ const node = lstatSync(path);
182
+ return node.isDirectory() && node.uid === process.getuid?.() && (node.mode & 18) === 0;
183
+ } catch {
184
+ return false;
185
+ }
186
+ };
187
+ var syncDirectory = (path) => {
188
+ const fd = openSync(path, "r");
189
+ try {
190
+ fsyncSync(fd);
191
+ } finally {
192
+ closeSync(fd);
193
+ }
194
+ };
195
+ var Rpc = class _Rpc {
196
+ constructor(socket, timeout) {
197
+ this.socket = socket;
198
+ this.timeout = timeout;
199
+ socket.on("message", (data) => {
200
+ let v;
201
+ try {
202
+ v = JSON.parse(data.toString());
203
+ } catch {
204
+ socket.terminate();
205
+ return;
206
+ }
207
+ if (!nativeRecord(v) || typeof v.id !== "number") return;
208
+ const p = this.pending.get(v.id);
209
+ if (!p) return;
210
+ this.pending.delete(v.id);
211
+ clearTimeout(p.timer);
212
+ if (v.error) p.reject(new Error("native RPC refused"));
213
+ else p.resolve(v.result);
214
+ });
215
+ socket.on("close", () => this.fail());
216
+ socket.on("error", () => this.fail());
217
+ }
218
+ sequence = 0;
219
+ pending = /* @__PURE__ */ new Map();
220
+ fail() {
221
+ for (const p of this.pending.values()) {
222
+ clearTimeout(p.timer);
223
+ p.reject(new Error("native RPC disconnected"));
224
+ }
225
+ this.pending.clear();
226
+ }
227
+ static async connect(url, protocols, timeout) {
228
+ const socket = new WebSocket(url, [...protocols], {
229
+ handshakeTimeout: timeout,
230
+ maxPayload: NATIVE_INPUT.maxFrameBytes,
231
+ followRedirects: false
232
+ });
233
+ const rpc = new _Rpc(socket, timeout);
234
+ await new Promise((resolve3, reject) => {
235
+ socket.once("open", resolve3);
236
+ socket.once("error", reject);
237
+ });
238
+ if (protocols.length && socket.protocol !== protocols[0]) {
239
+ rpc.close();
240
+ throw new Error("native protocol mismatch");
241
+ }
242
+ return rpc;
243
+ }
244
+ request(method, params) {
245
+ const id = ++this.sequence;
246
+ return new Promise((resolve3, reject) => {
247
+ const timer = setTimeout(() => {
248
+ this.pending.delete(id);
249
+ reject(new Error("native RPC timeout"));
250
+ }, this.timeout);
251
+ this.pending.set(id, { resolve: resolve3, reject, timer });
252
+ this.socket.send(
253
+ JSON.stringify({ jsonrpc: "2.0", id, method, params }),
254
+ (error) => {
255
+ if (error) {
256
+ clearTimeout(timer);
257
+ this.pending.delete(id);
258
+ reject(error);
259
+ }
260
+ }
261
+ );
262
+ });
263
+ }
264
+ notify(method) {
265
+ this.socket.send(JSON.stringify({ method }));
266
+ }
267
+ close() {
268
+ this.fail();
269
+ this.socket.terminate();
270
+ }
271
+ };
272
+ var controlRequest = (path, request, timeout) => new Promise((resolve3, reject) => {
273
+ const s = createConnection(path);
274
+ let buffer = "";
275
+ const timer = setTimeout(() => {
276
+ s.destroy();
277
+ reject(new Error("native control timeout"));
278
+ }, timeout);
279
+ s.on("connect", () => s.write(JSON.stringify(request) + "\n"));
280
+ s.on("error", reject);
281
+ s.on("close", () => {
282
+ clearTimeout(timer);
283
+ reject(new Error("native control closed"));
284
+ });
285
+ s.on("data", (chunk) => {
286
+ buffer += chunk.toString();
287
+ if (buffer.length > 65536) {
288
+ s.destroy();
289
+ return;
290
+ }
291
+ if (!buffer.includes("\n")) return;
292
+ try {
293
+ resolve3(JSON.parse(buffer.slice(0, buffer.indexOf("\n"))));
294
+ } catch {
295
+ reject(new Error("invalid native control response"));
296
+ }
297
+ s.destroy();
298
+ });
299
+ });
300
+ var NativeHostClient = class {
301
+ constructor(options) {
302
+ this.options = options;
303
+ this.timeout = options.timeoutMs ?? NATIVE_INPUT.rpcTimeoutMs;
304
+ }
305
+ timeout;
306
+ async connect() {
307
+ if ((this.options.platform ?? process.platform) !== "darwin")
308
+ throw new Error("native host platform unqualified");
309
+ const home = resolve(this.options.home);
310
+ if (this.options.agent === "codex" /* codex */) {
311
+ const published = join(home, NATIVE_INPUT.codexSocket);
312
+ const path2 = realpathSync(published);
313
+ if (lstatSync(published).uid !== process.getuid?.() || !privateNode(join(home, "app-server-control"), "directory") || !privateNode(dirname(path2), "directory") || !privateNode(path2, "socket"))
314
+ throw new Error("native control unavailable");
315
+ const rpc = await Rpc.connect(`ws+unix://${path2}:/`, [], this.timeout);
316
+ try {
317
+ const v2 = await rpc.request("initialize", {
318
+ clientInfo: { name: "pushary_native_input", version: "1" },
319
+ capabilities: { experimentalApi: true }
320
+ });
321
+ if (!nativeRecord(v2) || v2.codexHome !== home || typeof v2.userAgent !== "string" || !v2.userAgent.includes(`/${NATIVE_INPUT.codexVersion} `) || v2.platformOs !== "macos")
322
+ throw new Error("unqualified native host");
323
+ rpc.notify("initialized");
324
+ return { rpc, profile: home };
325
+ } catch (e) {
326
+ rpc.close();
327
+ throw e;
328
+ }
329
+ }
330
+ const path = join(home, NATIVE_INPUT.hermesSocket);
331
+ if (!ownerControlledDirectory(home) || !privateNode(path, "socket"))
332
+ throw new Error("native control unavailable");
333
+ const v = await controlRequest(
334
+ path,
335
+ { protocol: 1, verb: "identify", id: 1 },
336
+ this.timeout
337
+ );
338
+ if (!nativeRecord(v)) throw new Error("invalid host");
339
+ const d = nativeRecord(v.result) ? v.result : v;
340
+ if (d.runtime_protocol !== 1 || d.state !== "ready" || !Array.isArray(d.served_profiles) || !nativeIdentifier(d.instance_id) || typeof d.api_origin !== "string")
341
+ throw new Error("unqualified native host");
342
+ const profiles = d.served_profiles.filter(
343
+ (p) => nativeRecord(p) && p.home === home
344
+ );
345
+ const profile = profiles[0];
346
+ if (profiles.length !== 1 || !nativeRecord(profile) || typeof profile.profile_id !== "string")
347
+ throw new Error("native profile mismatch");
348
+ const hermesReceiptMode = qualifyHermesReceiptMode(
349
+ d.code_sha,
350
+ d.capabilities
351
+ );
352
+ if (!hermesReceiptMode) throw new Error("native admission unavailable");
353
+ const url = new URL(d.api_origin);
354
+ if (url.protocol !== "http:" || url.hostname !== "127.0.0.1" || url.username || url.password || url.pathname !== "/")
355
+ throw new Error("nonlocal native host");
356
+ const t = await controlRequest(
357
+ path,
358
+ {
359
+ protocol: 1,
360
+ id: 1,
361
+ verb: "session-ticket",
362
+ params: {
363
+ profile_id: profile.profile_id,
364
+ instance_id: d.instance_id,
365
+ purpose: "interactive"
366
+ }
367
+ },
368
+ this.timeout
369
+ );
370
+ const ticket = nativeRecord(t) && nativeRecord(t.result) ? t.result : t;
371
+ if (!nativeRecord(ticket) || ticket.profile_id !== profile.profile_id || ticket.instance_id !== d.instance_id || ticket.runtime_protocol !== 1 || typeof ticket.ticket !== "string")
372
+ throw new Error("native ticket mismatch");
373
+ return {
374
+ rpc: await Rpc.connect(
375
+ `ws://${url.host}/api/ws`,
376
+ [
377
+ NATIVE_INPUT.hermesWebSocketProtocol,
378
+ NATIVE_INPUT.hermesTicketPrefix + ticket.ticket
379
+ ],
380
+ this.timeout
381
+ ),
382
+ profile: profile.profile_id,
383
+ hermesReceiptMode
384
+ };
385
+ }
386
+ async list(host) {
387
+ const { rpc } = host;
388
+ if (this.options.agent === "codex" /* codex */) {
389
+ const v2 = await rpc.request("thread/loaded/list", {
390
+ limit: NATIVE_INPUT.maxSessions
391
+ });
392
+ if (!nativeRecord(v2) || !Array.isArray(v2.data) || !v2.data.every(nativeIdentifier))
393
+ throw new Error("invalid loaded sessions");
394
+ const sessions = [];
395
+ for (const id of v2.data) {
396
+ const t = await this.thread(rpc, id, false);
397
+ if (t && typeof t.cwd === "string")
398
+ sessions.push({
399
+ sessionId: id,
400
+ cwd: t.cwd,
401
+ agent: this.options.agent
402
+ });
403
+ }
404
+ return sessions;
405
+ }
406
+ const v = await rpc.request("session.list", {
407
+ limit: NATIVE_INPUT.maxSessions
408
+ });
409
+ if (!nativeRecord(v) || v.scope !== "live" || !Array.isArray(v.sessions))
410
+ throw new Error("invalid live sessions");
411
+ return v.sessions.filter(
412
+ (row) => nativeRecord(row) && nativeIdentifier(row.session_id) && (row.source === "cli" || row.source === "local")
413
+ ).map((row) => ({
414
+ sessionId: String(row.session_id),
415
+ cwd: this.options.home,
416
+ agent: this.options.agent
417
+ }));
418
+ }
419
+ async sessions() {
420
+ let host;
421
+ try {
422
+ host = await this.connect();
423
+ return await this.list(host);
424
+ } catch (error) {
425
+ this.options.report?.(
426
+ error instanceof Error ? error.message : "native discovery failed"
427
+ );
428
+ return [];
429
+ } finally {
430
+ host?.rpc.close();
431
+ }
432
+ }
433
+ async thread(rpc, id, includeTurns = false) {
434
+ const v = await rpc.request("thread/read", { threadId: id, includeTurns });
435
+ return nativeRecord(v) && nativeRecord(v.thread) && v.thread.id === id ? v.thread : null;
436
+ }
437
+ async hasReceipt(rpc, sessionId, commandId) {
438
+ for (const method of [
439
+ "thread/queue/list" /* queueList */,
440
+ "thread/items/list" /* itemsList */
441
+ ]) {
442
+ let cursor;
443
+ for (let page = 0; page < NATIVE_INPUT.receiptMaxPages; page++) {
444
+ const result = await rpc.request(method, {
445
+ threadId: sessionId,
446
+ ...method === "thread/items/list" /* itemsList */ ? { sortDirection: "desc" } : {},
447
+ limit: NATIVE_INPUT.receiptPageSize,
448
+ ...cursor ? { cursor } : {}
449
+ });
450
+ if (!nativeRecord(result) || !Array.isArray(result.data))
451
+ throw new Error("invalid native receipt page");
452
+ if (method === "thread/queue/list" /* queueList */) {
453
+ if (result.data.some((row) => isCodexQueuedReceipt(row, commandId)))
454
+ return true;
455
+ } else {
456
+ const items = result.data.flatMap(
457
+ (row) => nativeRecord(row) && nativeRecord(row.item) ? [row.item] : []
458
+ );
459
+ if (isCodexInputReceipt(
460
+ { id: sessionId, turns: [{ items }] },
461
+ sessionId,
462
+ commandId
463
+ ))
464
+ return true;
465
+ }
466
+ if (typeof result.nextCursor !== "string" || !result.nextCursor || result.nextCursor === cursor)
467
+ break;
468
+ cursor = result.nextCursor;
469
+ }
470
+ }
471
+ return false;
472
+ }
473
+ async hasHermesReceipt(host, intent, snapshot) {
474
+ if (host.hermesReceiptMode === "submission" /* submission */) {
475
+ const receipt2 = parseHermesAdmission(
476
+ await host.rpc.request("prompt.receipt" /* receipt */, {
477
+ session_id: intent.sessionId,
478
+ submission_id: intent.commandId
479
+ }),
480
+ host.profile,
481
+ intent.sessionId
482
+ );
483
+ return receipt2 !== null && receipt2.status !== "unknown" /* unknown */;
484
+ }
485
+ let admissionId = hermesPendingAdmissionId(
486
+ snapshot,
487
+ host.profile,
488
+ intent.sessionId,
489
+ intent.commandId
490
+ );
491
+ if (!admissionId && intent.hermesCursor) {
492
+ const replay = await host.rpc.request("session.events.since" /* eventsSince */, {
493
+ session_id: intent.sessionId,
494
+ ...intent.hermesCursor
495
+ });
496
+ admissionId = hermesReplayAdmissionId(
497
+ replay,
498
+ intent.hermesCursor,
499
+ intent.sessionId,
500
+ intent.commandId
501
+ );
502
+ }
503
+ if (!admissionId) return false;
504
+ const receipt = parseHermesAdmission(
505
+ await host.rpc.request("prompt.receipt" /* receipt */, {
506
+ session_id: intent.sessionId,
507
+ admission_id: admissionId
508
+ }),
509
+ host.profile,
510
+ intent.sessionId
511
+ );
512
+ return receipt?.admissionId === admissionId && receipt.status !== "unknown" /* unknown */;
513
+ }
514
+ journalPath(sessionId, commandId) {
515
+ return join(
516
+ this.options.journalDir,
517
+ createHash("sha256").update(`${sessionId}\0${commandId}`).digest("hex") + ".json"
518
+ );
519
+ }
520
+ readIntent(sessionId, commandId) {
521
+ try {
522
+ const file = this.journalPath(sessionId, commandId);
523
+ const node = lstatSync(file);
524
+ if (!node.isFile() || node.uid !== process.getuid?.() || node.size > 4096 || (node.mode & 63) !== 0)
525
+ throw new Error("unsafe intent");
526
+ const v = JSON.parse(readFileSync(file, "utf8"));
527
+ if (!nativeRecord(v) || v.version !== NATIVE_INPUT.journalVersion || v.agent !== this.options.agent || v.sessionId !== sessionId || v.commandId !== commandId || v.home !== resolve(this.options.home) || typeof v.submittedAtMs !== "number" || !Number.isSafeInteger(v.submittedAtMs) || typeof v.accepted !== "boolean" || v.hermesCursor !== void 0 && !parseHermesReplayCursor(v.hermesCursor))
528
+ throw new Error("invalid intent");
529
+ const hermesCursor = parseHermesReplayCursor(v.hermesCursor);
530
+ return {
531
+ version: v.version,
532
+ agent: v.agent,
533
+ sessionId,
534
+ commandId,
535
+ home: v.home,
536
+ submittedAtMs: v.submittedAtMs,
537
+ accepted: v.accepted,
538
+ settled: v.settled === true,
539
+ ...hermesCursor ? { hermesCursor } : {}
540
+ };
541
+ } catch (e) {
542
+ if (e instanceof Error && "code" in e && e.code === "ENOENT") return null;
543
+ throw e;
544
+ }
545
+ }
546
+ pendingSettlements() {
547
+ try {
548
+ return readdirSync(this.options.journalDir).filter((file) => /^[0-9a-f]{64}\.json$/.test(file)).flatMap((file) => {
549
+ try {
550
+ const raw = JSON.parse(
551
+ readFileSync(join(this.options.journalDir, file), "utf8")
552
+ );
553
+ if (!nativeRecord(raw) || !nativeIdentifier(raw.sessionId) || !nativeIdentifier(raw.commandId))
554
+ return [];
555
+ const intent = this.readIntent(raw.sessionId, raw.commandId);
556
+ return intent?.accepted && !intent.settled ? [{ sessionId: intent.sessionId, commandId: intent.commandId }] : [];
557
+ } catch {
558
+ return [];
559
+ }
560
+ });
561
+ } catch {
562
+ return [];
563
+ }
564
+ }
565
+ confirmSettlement(sessionId, commandId) {
566
+ const intent = this.readIntent(sessionId, commandId);
567
+ if (intent?.accepted)
568
+ writeJsonAtomicDurable(
569
+ this.journalPath(sessionId, commandId),
570
+ { ...intent, settled: true },
571
+ 384
572
+ );
573
+ }
574
+ writeIntent(sessionId, commandId, hermesCursor) {
575
+ mkdirSync(this.options.journalDir, { recursive: true, mode: 448 });
576
+ const fd = openSync(this.journalPath(sessionId, commandId), "wx", 384);
577
+ try {
578
+ writeSync(
579
+ fd,
580
+ JSON.stringify({
581
+ version: NATIVE_INPUT.journalVersion,
582
+ agent: this.options.agent,
583
+ sessionId,
584
+ commandId,
585
+ home: resolve(this.options.home),
586
+ submittedAtMs: Date.now(),
587
+ accepted: false,
588
+ ...hermesCursor ? { hermesCursor } : {}
589
+ })
590
+ );
591
+ fsyncSync(fd);
592
+ } finally {
593
+ closeSync(fd);
594
+ }
595
+ syncDirectory(this.options.journalDir);
596
+ }
597
+ accept(intent) {
598
+ writeJsonAtomicDurable(
599
+ this.journalPath(intent.sessionId, intent.commandId),
600
+ { ...intent, accepted: true },
601
+ 384
602
+ );
603
+ return "accepted" /* accepted */;
604
+ }
605
+ async deliver(sessionId, command, signal) {
606
+ let intent;
607
+ try {
608
+ intent = this.readIntent(sessionId, command.id);
609
+ } catch {
610
+ return "outcome_unknown" /* outcomeUnknown */;
611
+ }
612
+ if (intent?.accepted) return "accepted" /* accepted */;
613
+ if (!intent && command.expiresAtMs <= Date.now())
614
+ return "expired" /* expired */;
615
+ let host;
616
+ let hermesCursor;
617
+ try {
618
+ host = await this.connect();
619
+ const { rpc } = host;
620
+ if (!(await this.list(host)).some((s) => s.sessionId === sessionId))
621
+ return intent ? "outcome_unknown" /* outcomeUnknown */ : "not_sent" /* notSent */;
622
+ if (this.options.agent === "codex" /* codex */) {
623
+ if (intent) {
624
+ if (await this.hasReceipt(rpc, sessionId, command.id))
625
+ return this.accept(intent);
626
+ } else {
627
+ const queue = await rpc.request("thread/queue/list" /* queueList */, {
628
+ threadId: sessionId,
629
+ limit: 1
630
+ });
631
+ if (!nativeRecord(queue) || !Array.isArray(queue.data))
632
+ throw new Error("native queue unavailable");
633
+ }
634
+ } else if (host.hermesReceiptMode === "submission" /* submission */) {
635
+ if (intent && await this.hasHermesReceipt(host, intent))
636
+ return this.accept(intent);
637
+ if (!intent) {
638
+ const snapshot = await rpc.request("session.resume" /* resume */, {
639
+ session_id: sessionId
640
+ });
641
+ if (!nativeRecord(snapshot) || snapshot.session_id !== sessionId)
642
+ throw new Error("native session mismatch");
643
+ }
644
+ } else {
645
+ const snapshot = await rpc.request("session.resume" /* resume */, {
646
+ session_id: sessionId
647
+ });
648
+ if (!nativeRecord(snapshot) || snapshot.session_id !== sessionId)
649
+ throw new Error("native session mismatch");
650
+ hermesCursor = parseHermesReplayCursor(snapshot) ?? void 0;
651
+ if (intent && await this.hasHermesReceipt(host, intent, snapshot))
652
+ return this.accept(intent);
653
+ if (!intent && !hermesCursor)
654
+ throw new Error("native replay cursor unavailable");
655
+ }
656
+ const action = nativeInputAction({
657
+ attempted: !!intent,
658
+ accepted: false,
659
+ nowMs: Date.now(),
660
+ expiresAtMs: Math.min(
661
+ command.expiresAtMs,
662
+ intent ? intent.submittedAtMs + NATIVE_INPUT.receiptWindowMs : Infinity
663
+ )
664
+ });
665
+ if (action === "expire" /* expire */) return "expired" /* expired */;
666
+ if (action === "outcome_unknown" /* outcomeUnknown */)
667
+ return "outcome_unknown" /* outcomeUnknown */;
668
+ if (action === "observe" /* observe */)
669
+ return "unconfirmed" /* unconfirmed */;
670
+ if (signal?.aborted) return "not_sent" /* notSent */;
671
+ if (!intent) {
672
+ this.writeIntent(sessionId, command.id, hermesCursor);
673
+ intent = this.readIntent(sessionId, command.id);
674
+ }
675
+ if (!intent) return "outcome_unknown" /* outcomeUnknown */;
676
+ if (this.options.agent === "hermes" /* hermes */) {
677
+ const receipt = parseHermesAdmission(
678
+ await rpc.request("prompt.submit" /* submit */, {
679
+ session_id: sessionId,
680
+ submission_id: command.id,
681
+ text: command.text
682
+ }),
683
+ host.profile,
684
+ sessionId
685
+ );
686
+ return receipt && receipt.status !== "unknown" /* unknown */ ? this.accept(intent) : "outcome_unknown" /* outcomeUnknown */;
687
+ }
688
+ const response = await rpc.request("thread/queue/add" /* queueAdd */, {
689
+ threadId: sessionId,
690
+ clientUserMessageId: command.id,
691
+ input: [{ type: "text", text: command.text, text_elements: [] }]
692
+ });
693
+ return nativeRecord(response) && isCodexQueuedReceipt(response.queuedSubmission, command.id) || await this.hasReceipt(rpc, sessionId, command.id) ? this.accept(intent) : "unconfirmed" /* unconfirmed */;
694
+ } catch (error) {
695
+ this.options.report?.(
696
+ error instanceof Error ? error.message : "native delivery failed"
697
+ );
698
+ return intent ? Date.now() >= Math.min(
699
+ command.expiresAtMs,
700
+ intent.submittedAtMs + NATIVE_INPUT.receiptWindowMs
701
+ ) ? "outcome_unknown" /* outcomeUnknown */ : "unconfirmed" /* unconfirmed */ : "not_sent" /* notSent */;
702
+ } finally {
703
+ host?.rpc.close();
704
+ }
705
+ }
706
+ };
707
+
708
+ // src/hook-ownership-file.ts
709
+ import { readFileSync as readFileSync2 } from "fs";
710
+ var readHookOwner = (file) => {
711
+ try {
712
+ const value = JSON.parse(readFileSync2(file, "utf8"));
713
+ return nativeRecord(value) ? hookOwnerOf(value) : null;
714
+ } catch (error) {
715
+ return error instanceof Error && "code" in error && error.code === "ENOENT" ? "none" : null;
716
+ }
717
+ };
718
+
719
+ // src/native-input-receiver.ts
720
+ import { basename } from "path";
721
+ var startNativeInputReceiver = (apiKey, machineId, options) => {
722
+ const client = options.client;
723
+ const baseUrl = options.baseUrl ?? getBaseUrl();
724
+ const abort = new AbortController();
725
+ const busy = /* @__PURE__ */ new Set();
726
+ const heartbeats = /* @__PURE__ */ new Map();
727
+ const settlements = /* @__PURE__ */ new Map();
728
+ let scanning = false;
729
+ let cursor = 0;
730
+ const post = async (path, body) => {
731
+ const response = await fetch(`${baseUrl}${path}`, {
732
+ method: "POST",
733
+ headers: {
734
+ "Content-Type": "application/json",
735
+ Authorization: `Bearer ${apiKey}`
736
+ },
737
+ body: JSON.stringify(body),
738
+ signal: AbortSignal.any([
739
+ abort.signal,
740
+ AbortSignal.timeout(CLAUDE_INBOX_RECEIVER.requestTimeoutMs)
741
+ ])
742
+ });
743
+ if (!response.ok) throw new Error(`native inbox HTTP ${response.status}`);
744
+ return response.json();
745
+ };
746
+ const settle = async (commandId) => {
747
+ const pending = settlements.get(commandId);
748
+ if (!pending) return false;
749
+ try {
750
+ const receipt = await post("/api/agent/command/ack", {
751
+ ...pending,
752
+ commandId,
753
+ machineId
754
+ });
755
+ if (!isCommandAcknowledgementReceipt(receipt))
756
+ throw new Error("native acknowledgement missing receipt");
757
+ client.confirmSettlement(pending.sessionId, commandId);
758
+ settlements.delete(commandId);
759
+ return true;
760
+ } catch (error) {
761
+ if (!abort.signal.aborted)
762
+ options.report?.(
763
+ error instanceof Error ? error.message : "native receipt recovery failed"
764
+ );
765
+ return false;
766
+ }
767
+ };
768
+ const receive = async (sessionId) => {
769
+ busy.add(sessionId);
770
+ try {
771
+ if ([...settlements.values()].some(
772
+ (receipt) => receipt.sessionId === sessionId
773
+ ))
774
+ return;
775
+ const raw = await post("/api/agent/command/native", {
776
+ operation: "claim" /* claim */,
777
+ sessionId,
778
+ machineId
779
+ });
780
+ const command = parseClaudeInboxClaim(raw);
781
+ if (!command || abort.signal.aborted) return;
782
+ const outcome = await client.deliver(sessionId, command, abort.signal);
783
+ const settlement = CLAUDE_INBOX_SETTLEMENTS[outcome];
784
+ if (abort.signal.aborted || !settlement) return;
785
+ settlements.set(command.id, { sessionId, ...settlement });
786
+ await settle(command.id);
787
+ } catch (error) {
788
+ if (!abort.signal.aborted)
789
+ options.report?.(
790
+ error instanceof Error ? error.message : "native inbox delivery failed"
791
+ );
792
+ } finally {
793
+ busy.delete(sessionId);
794
+ }
795
+ };
796
+ const tick = async () => {
797
+ if (scanning || abort.signal.aborted) return;
798
+ scanning = true;
799
+ try {
800
+ for (const receipt of client.pendingSettlements()) {
801
+ settlements.set(receipt.commandId, {
802
+ sessionId: receipt.sessionId,
803
+ state: "delivered"
804
+ });
805
+ }
806
+ for (const commandId of [...settlements.keys()]) await settle(commandId);
807
+ const sessions = (await client.sessions()).filter(options.ownsSession).slice(0, CLAUDE_INBOX_MAX_SESSIONS);
808
+ if (!sessions.length) return;
809
+ for (const session of sessions) {
810
+ if (Date.now() - (heartbeats.get(session.sessionId) ?? 0) < CLAUDE_INBOX_RECEIVER.heartbeatMs)
811
+ continue;
812
+ await post("/api/agent/event", {
813
+ event: "heartbeat",
814
+ agentType: options.agentType ?? "claude_code",
815
+ agentName: `${options.agentName ?? "Claude Code"} - ${basename(session.cwd)}`,
816
+ sessionId: session.sessionId,
817
+ machineId,
818
+ cwd: session.cwd,
819
+ sentAt: (/* @__PURE__ */ new Date()).toISOString(),
820
+ canDrainCommand: false
821
+ });
822
+ heartbeats.set(session.sessionId, Date.now());
823
+ }
824
+ const raw = await post("/api/agent/command/native", {
825
+ operation: "poll" /* poll */,
826
+ machineId,
827
+ sessionIds: sessions.map((session) => session.sessionId)
828
+ });
829
+ const pending = parseClaudeInboxPoll(raw);
830
+ if (!pending) throw new Error("native inbox protocol mismatch");
831
+ const rotated = [...pending.slice(cursor), ...pending.slice(0, cursor)];
832
+ cursor = pending.length ? (cursor + CLAUDE_INBOX_RECEIVER.maxConcurrent) % pending.length : 0;
833
+ for (const sessionId of rotated) {
834
+ if (abort.signal.aborted || busy.size >= CLAUDE_INBOX_RECEIVER.maxConcurrent)
835
+ break;
836
+ if (!busy.has(sessionId) && sessions.some((session) => session.sessionId === sessionId))
837
+ void receive(sessionId);
838
+ }
839
+ } catch (error) {
840
+ const message = error instanceof Error ? error.message : "native inbox polling failed";
841
+ if (!abort.signal.aborted) options.report?.(message);
842
+ } finally {
843
+ scanning = false;
844
+ }
845
+ };
846
+ const timer = setInterval(() => {
847
+ void tick();
848
+ }, options.pollMs ?? CLAUDE_INBOX_RECEIVER.pollMs);
849
+ timer.unref();
850
+ void tick();
851
+ return () => {
852
+ abort.abort();
853
+ clearInterval(timer);
854
+ };
855
+ };
856
+
857
+ // src/native-host-receiver.ts
858
+ var ownsHermes = (home) => {
859
+ try {
860
+ return parseHermesInstallMarker(
861
+ JSON.parse(
862
+ readFileSync3(
863
+ join2(home, "plugins", "pushary", HERMES_INSTALL_MARKER_FILE),
864
+ "utf8"
865
+ )
866
+ )
867
+ )?.installedBy === "cli";
868
+ } catch {
869
+ return false;
870
+ }
871
+ };
872
+ var startNativeHostReceivers = (apiKey, machineId, report) => {
873
+ const homes = {
874
+ ["codex" /* codex */]: codexHome(),
875
+ ["hermes" /* hermes */]: defaultHermesHome()
876
+ };
877
+ const stops = Object.values(NativeInputAgent).map((agent) => {
878
+ const home = homes[agent];
879
+ return startNativeInputReceiver(apiKey, machineId, {
880
+ client: new NativeHostClient({
881
+ agent,
882
+ home,
883
+ report,
884
+ journalDir: join2(homedir(), ".pushary", NATIVE_INPUT.journalDirectory)
885
+ }),
886
+ agentType: agent,
887
+ agentName: agent === "codex" /* codex */ ? "Codex" : "Hermes",
888
+ report,
889
+ ownsSession: () => agent === "codex" /* codex */ ? readHookOwner(join2(home, "hooks.json")) === "cli" : ownsHermes(home)
890
+ });
891
+ });
892
+ return () => {
893
+ for (const stop of stops) stop();
894
+ };
895
+ };
896
+
897
+ // src/hook-command-receipts.ts
898
+ var reconcileCommandReceipts = async (apiKey, machineId, agentType, baseUrl, signal) => {
899
+ const namespace = `${apiKey}:${machineId}:${agentType}`;
900
+ for (const { sessionId, command } of pendingCommandReceipts(namespace)) {
901
+ signal?.throwIfAborted();
902
+ const state = command.delivered ? "delivered" : command.attempted ? "uncertain" : "failed";
903
+ const response = await fetch(`${baseUrl}/api/agent/command/ack`, {
904
+ method: "POST",
905
+ headers: {
906
+ "Content-Type": "application/json",
907
+ Authorization: `Bearer ${apiKey}`
908
+ },
909
+ body: JSON.stringify({
910
+ commandId: command.id,
911
+ sessionId,
912
+ machineId,
913
+ state,
914
+ failureCode: command.attempted ? "delivery_unconfirmed" : "expired"
915
+ }),
916
+ signal: AbortSignal.any([
917
+ AbortSignal.timeout(COMMAND_RECOVERY.acknowledgementTimeoutMs),
918
+ ...signal ? [signal] : []
919
+ ])
920
+ });
921
+ if (response.ok) removePendingCommand(sessionId, command.id, namespace);
922
+ }
923
+ };
924
+
925
+ // src/claude-inbox-receiver.ts
926
+ import { join as join4, basename as basename2 } from "path";
927
+ import { homedir as homedir2 } from "os";
928
+
929
+ // src/claude-inbox.ts
930
+ import { createHash as createHash2, randomUUID } from "crypto";
931
+ import { execFileSync } from "child_process";
932
+ import {
933
+ closeSync as closeSync2,
934
+ fsyncSync as fsyncSync2,
935
+ lstatSync as lstatSync2,
936
+ mkdirSync as mkdirSync2,
937
+ openSync as openSync2,
938
+ readFileSync as readFileSync4,
939
+ readdirSync as readdirSync2,
940
+ readSync,
941
+ statSync,
942
+ writeFileSync,
943
+ unlinkSync
944
+ } from "fs";
945
+ import { dirname as dirname2, isAbsolute, join as join3, resolve as resolve2 } from "path";
946
+ import { Socket } from "net";
947
+ import { setTimeout as delay } from "timers/promises";
948
+ var object = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
949
+ var digest = (value) => createHash2("sha256").update(value).digest("hex");
950
+ var readObject = (file) => {
951
+ const stat = lstatSync2(file);
952
+ if (!stat.isFile() || stat.size > CLAUDE_INBOX.maxRecordBytes || stat.uid !== process.getuid?.())
953
+ throw new Error("Invalid inbox record");
954
+ return JSON.parse(readFileSync4(file, "utf8"));
955
+ };
956
+ var readKey = (file) => {
957
+ const value = readObject(file);
958
+ return object(value) && typeof value.peerToken === "string" && /^[0-9a-f]{32}$/.test(value.peerToken) && typeof value.procStart === "string" ? { peerToken: value.peerToken, procStart: value.procStart } : null;
959
+ };
960
+ var processStart = (pid) => {
961
+ if (process.platform !== "darwin") return null;
962
+ try {
963
+ return execFileSync("/bin/ps", ["-p", String(pid), "-o", "lstart="], {
964
+ encoding: "utf8",
965
+ env: { ...process.env, TZ: "UTC", LC_ALL: "C" },
966
+ timeout: CLAUDE_INBOX.connectTimeoutMs
967
+ }).trim();
968
+ } catch {
969
+ return null;
970
+ }
971
+ };
972
+ var transcriptPath = (config, session) => join3(
973
+ config,
974
+ "projects",
975
+ resolve2(session.cwd).replace(/[^a-zA-Z0-9-]/g, "-"),
976
+ `${session.sessionId}.jsonl`
977
+ );
978
+ var transcriptSize = (file) => {
979
+ try {
980
+ return statSync(file).size;
981
+ } catch {
982
+ return 0;
983
+ }
984
+ };
985
+ var receiptPresent = (attempt) => {
986
+ let fd;
987
+ try {
988
+ fd = openSync2(attempt.transcript, "r");
989
+ const buffer = Buffer.alloc(CLAUDE_INBOX.maxTranscriptReadBytes);
990
+ const count = readSync(fd, buffer, 0, buffer.length, attempt.offset);
991
+ for (const line of buffer.subarray(0, count).toString("utf8").split("\n")) {
992
+ try {
993
+ if (isClaudeInboxReceipt(
994
+ JSON.parse(line),
995
+ attempt.sessionId,
996
+ attempt.commandId
997
+ ))
998
+ return true;
999
+ } catch {
1000
+ }
1001
+ }
1002
+ return false;
1003
+ } catch {
1004
+ return false;
1005
+ } finally {
1006
+ if (fd !== void 0) closeSync2(fd);
1007
+ }
1008
+ };
1009
+ var connectInbox = (path) => new Promise((resolveConnection) => {
1010
+ const socket = new Socket();
1011
+ const timer = setTimeout(failed, CLAUDE_INBOX.connectTimeoutMs);
1012
+ function failed() {
1013
+ clearTimeout(timer);
1014
+ socket.destroy();
1015
+ resolveConnection(null);
1016
+ }
1017
+ socket.once("error", failed);
1018
+ socket.connect(path, () => {
1019
+ clearTimeout(timer);
1020
+ socket.removeListener("error", failed);
1021
+ socket.on("error", () => {
1022
+ });
1023
+ resolveConnection(socket);
1024
+ });
1025
+ });
1026
+ var createAttempt = (file, attempt) => {
1027
+ mkdirSync2(dirname2(file), { recursive: true, mode: 448 });
1028
+ const fd = openSync2(file, "wx", 384);
1029
+ try {
1030
+ writeFileSync(fd, JSON.stringify(attempt));
1031
+ fsyncSync2(fd);
1032
+ } finally {
1033
+ closeSync2(fd);
1034
+ }
1035
+ const directory = openSync2(dirname2(file), "r");
1036
+ try {
1037
+ fsyncSync2(directory);
1038
+ } finally {
1039
+ closeSync2(directory);
1040
+ }
1041
+ return true;
1042
+ };
1043
+ var ClaudeInboxClient = class {
1044
+ options;
1045
+ constructor(options) {
1046
+ this.options = options;
1047
+ }
1048
+ sessions() {
1049
+ try {
1050
+ const sessions = readdirSync2(
1051
+ join3(this.options.configDir, "sessions")
1052
+ ).filter((file) => /^\d+\.json$/.test(file));
1053
+ return sessions.flatMap((file) => {
1054
+ try {
1055
+ const session = parseClaudeInboxSession(
1056
+ readObject(join3(this.options.configDir, "sessions", file))
1057
+ );
1058
+ if (!session || file !== `${session.pid}.json` || !isAbsolute(session.cwd) || !isAbsolute(session.messagingSocketPath))
1059
+ return [];
1060
+ if ((this.options.processStart ?? processStart)(session.pid) !== session.procStart)
1061
+ return [];
1062
+ const socket = lstatSync2(session.messagingSocketPath);
1063
+ if (!socket.isSocket() || socket.uid !== process.getuid?.()) return [];
1064
+ return this.key(session) ? [session] : [];
1065
+ } catch {
1066
+ return [];
1067
+ }
1068
+ });
1069
+ } catch {
1070
+ return [];
1071
+ }
1072
+ }
1073
+ key(session) {
1074
+ const canonicalSocket = session.messagingSocketPath.replace(
1075
+ /^\/private(?=\/(?:tmp|var|etc)\/)/,
1076
+ ""
1077
+ );
1078
+ const key = readKey(
1079
+ join3(
1080
+ this.options.configDir,
1081
+ "sessions",
1082
+ `${session.pid}.${digest(canonicalSocket)}.key`
1083
+ )
1084
+ );
1085
+ return key?.procStart === session.procStart ? key : null;
1086
+ }
1087
+ attempts(version) {
1088
+ const directory = join3(this.options.journalDir, version);
1089
+ mkdirSync2(directory, { recursive: true, mode: 448 });
1090
+ const files = readdirSync2(directory).filter((file) => /^\d{13}-[0-9a-f-]{36}\.json$/.test(file)).sort().reverse();
1091
+ const attempts = files.slice(0, CLAUDE_WAKE.retainedAttempts).map((name) => {
1092
+ const file = join3(directory, name);
1093
+ const value = readObject(file);
1094
+ if (!object(value) || value.version !== CLAUDE_INBOX.journalVersion || typeof value.sessionId !== "string" || typeof value.commandId !== "string" || typeof value.transcript !== "string" || !resolve2(value.transcript).startsWith(
1095
+ resolve2(this.options.configDir, "projects") + "/"
1096
+ ) || typeof value.offset !== "number" || !Number.isSafeInteger(value.offset) || value.offset < 0 || typeof value.accepted !== "boolean" || typeof value.submittedAtMs !== "number" || !Number.isSafeInteger(value.submittedAtMs))
1097
+ throw new Error("Invalid wake receipt");
1098
+ const attempt = {
1099
+ version: value.version,
1100
+ sessionId: value.sessionId,
1101
+ commandId: value.commandId,
1102
+ transcript: value.transcript,
1103
+ offset: value.offset,
1104
+ accepted: value.accepted,
1105
+ submittedAtMs: value.submittedAtMs
1106
+ };
1107
+ if (!attempt.accepted && receiptPresent(attempt)) {
1108
+ const accepted = { ...attempt, accepted: true };
1109
+ writeJsonAtomicDurable(file, accepted, 384);
1110
+ return { file, attempt: accepted };
1111
+ }
1112
+ return { file, attempt };
1113
+ });
1114
+ for (const name of files.slice(CLAUDE_WAKE.retainedAttempts))
1115
+ unlinkSync(join3(directory, name));
1116
+ return attempts;
1117
+ }
1118
+ async wake(sessionId, signal) {
1119
+ const matches = this.sessions().filter(
1120
+ (candidate) => candidate.sessionId === sessionId
1121
+ );
1122
+ const session = matches.length === 1 ? matches[0] : null;
1123
+ if (!session || signal?.aborted) return "wake_not_sent" /* notSent */;
1124
+ try {
1125
+ const attempts = this.attempts(session.version);
1126
+ const decision = claudeWakeDecision({
1127
+ sessionId,
1128
+ idle: session.status === "idle",
1129
+ nowMs: Date.now(),
1130
+ attempts: attempts.map((entry) => entry.attempt)
1131
+ });
1132
+ if (decision) return decision;
1133
+ const key = this.key(session);
1134
+ if (!key) return "wake_not_sent" /* notSent */;
1135
+ const socket = await connectInbox(session.messagingSocketPath);
1136
+ if (!socket) return "wake_not_sent" /* notSent */;
1137
+ if (signal?.aborted || (this.options.processStart ?? processStart)(session.pid) !== session.procStart) {
1138
+ socket.destroy();
1139
+ return "wake_not_sent" /* notSent */;
1140
+ }
1141
+ const commandId = randomUUID();
1142
+ const transcript = transcriptPath(this.options.configDir, session);
1143
+ const attempt = {
1144
+ version: CLAUDE_INBOX.journalVersion,
1145
+ sessionId,
1146
+ commandId,
1147
+ transcript,
1148
+ offset: transcriptSize(transcript),
1149
+ accepted: false,
1150
+ submittedAtMs: Date.now()
1151
+ };
1152
+ const file = join3(
1153
+ this.options.journalDir,
1154
+ session.version,
1155
+ `${attempt.submittedAtMs}-${commandId}.json`
1156
+ );
1157
+ try {
1158
+ createAttempt(file, attempt);
1159
+ } catch {
1160
+ socket.destroy();
1161
+ return "wake_not_sent" /* notSent */;
1162
+ }
1163
+ const frame = {
1164
+ type: "user",
1165
+ session_id: sessionId,
1166
+ uuid: commandId,
1167
+ msg_id: commandId,
1168
+ from: CLAUDE_INBOX.sender,
1169
+ priority: "next",
1170
+ message: { role: "user", content: CLAUDE_WAKE.message }
1171
+ };
1172
+ socket.end(
1173
+ JSON.stringify({ type: "auth", token: key.peerToken }) + "\n" + JSON.stringify(frame) + "\n"
1174
+ );
1175
+ socket.setTimeout(CLAUDE_INBOX.connectTimeoutMs, () => socket.destroy());
1176
+ const outcome = await this.observe(
1177
+ file,
1178
+ attempt,
1179
+ attempt.submittedAtMs + CLAUDE_WAKE.receiptWindowMs
1180
+ );
1181
+ return outcome === "accepted" /* accepted */ ? "wake_accepted" /* accepted */ : "wake_unconfirmed" /* unconfirmed */;
1182
+ } catch {
1183
+ return "wake_not_sent" /* notSent */;
1184
+ }
1185
+ }
1186
+ async observe(file, attempt, expiresAtMs) {
1187
+ const deliveryDeadline = Math.min(
1188
+ expiresAtMs,
1189
+ attempt.submittedAtMs + CLAUDE_INBOX.receiptWindowMs
1190
+ );
1191
+ if (claudeInboxAction({
1192
+ attempted: true,
1193
+ accepted: attempt.accepted,
1194
+ expiresAtMs: deliveryDeadline,
1195
+ nowMs: Date.now()
1196
+ }) === "acknowledge" /* acknowledge */)
1197
+ return "accepted" /* accepted */;
1198
+ const deadline = Math.min(
1199
+ deliveryDeadline,
1200
+ Date.now() + (this.options.receiptWindowMs ?? CLAUDE_INBOX.receiptWindowMs)
1201
+ );
1202
+ do {
1203
+ if (receiptPresent(attempt)) {
1204
+ try {
1205
+ writeJsonAtomicDurable(file, { ...attempt, accepted: true }, 384);
1206
+ } catch {
1207
+ return "unconfirmed" /* unconfirmed */;
1208
+ }
1209
+ return "accepted" /* accepted */;
1210
+ }
1211
+ if (Date.now() >= deadline) break;
1212
+ await delay(this.options.receiptPollMs ?? CLAUDE_INBOX.receiptPollMs);
1213
+ } while (Date.now() <= deadline);
1214
+ return deliveryDeadline <= Date.now() ? "outcome_unknown" /* outcomeUnknown */ : "unconfirmed" /* unconfirmed */;
1215
+ }
1216
+ };
1217
+
1218
+ // src/claude-inbox-receiver.ts
1219
+ var ownsSession = (session) => {
1220
+ const files = [
1221
+ join4(claudeConfigDir(), "settings.json"),
1222
+ join4(session.cwd, ".claude", "settings.json"),
1223
+ join4(session.cwd, ".claude", "settings.local.json")
1224
+ ];
1225
+ const owners = files.map(readHookOwner);
1226
+ return owners.includes("cli") && !owners.includes("app") && !owners.includes(null);
1227
+ };
1228
+ var startClaudeInboxReceiver = (apiKey, machineId, options = {}) => {
1229
+ const client = options.client ?? new ClaudeInboxClient({
1230
+ configDir: claudeConfigDir(),
1231
+ journalDir: join4(homedir2(), ".pushary", CLAUDE_WAKE.directory)
1232
+ });
1233
+ const baseUrl = options.baseUrl ?? getBaseUrl();
1234
+ const abort = new AbortController();
1235
+ let scanning = false;
1236
+ let cursor = 0;
1237
+ const heartbeats = /* @__PURE__ */ new Map();
1238
+ const post = async (body, path = CLAUDE_WAKE_API) => {
1239
+ const response = await fetch(`${baseUrl}${path}`, {
1240
+ method: "POST",
1241
+ headers: {
1242
+ "Content-Type": "application/json",
1243
+ Authorization: `Bearer ${apiKey}`
1244
+ },
1245
+ body: JSON.stringify(body),
1246
+ signal: AbortSignal.any([
1247
+ abort.signal,
1248
+ AbortSignal.timeout(CLAUDE_INBOX_RECEIVER.requestTimeoutMs)
1249
+ ])
1250
+ });
1251
+ if (!response.ok) throw new Error(`Claude wake HTTP ${response.status}`);
1252
+ return response.json();
1253
+ };
1254
+ const report = async (sessionId, waitingOn) => {
1255
+ await post({
1256
+ operation: "report" /* report */,
1257
+ sessionId,
1258
+ machineId,
1259
+ waitingOn
1260
+ });
1261
+ };
1262
+ const tick = async () => {
1263
+ if (scanning || abort.signal.aborted) return;
1264
+ scanning = true;
1265
+ try {
1266
+ await reconcileCommandReceipts(
1267
+ apiKey,
1268
+ machineId,
1269
+ "claude_code",
1270
+ baseUrl,
1271
+ abort.signal
1272
+ );
1273
+ const sessions = client.sessions().filter(options.ownsSession ?? ownsSession).slice(0, CLAUDE_INBOX_MAX_SESSIONS);
1274
+ if (!sessions.length) return;
1275
+ for (const session of sessions) {
1276
+ if (Date.now() - (heartbeats.get(session.sessionId) ?? 0) < CLAUDE_INBOX_RECEIVER.heartbeatMs)
1277
+ continue;
1278
+ await post(
1279
+ {
1280
+ event: "heartbeat",
1281
+ agentType: "claude_code",
1282
+ agentName: `Claude Code - ${basename2(session.cwd)}`,
1283
+ sessionId: session.sessionId,
1284
+ machineId,
1285
+ cwd: session.cwd,
1286
+ sentAt: (/* @__PURE__ */ new Date()).toISOString(),
1287
+ canDrainCommand: false,
1288
+ skipCommandDrain: true
1289
+ },
1290
+ "/api/agent/event"
1291
+ );
1292
+ heartbeats.set(session.sessionId, Date.now());
1293
+ }
1294
+ const raw = await post({
1295
+ operation: "poll" /* poll */,
1296
+ machineId,
1297
+ sessionIds: sessions.map((session) => session.sessionId)
1298
+ });
1299
+ const pending = parseClaudeInboxPoll(raw);
1300
+ if (!pending) throw new Error("Claude wake protocol mismatch");
1301
+ const rotated = [...pending.slice(cursor), ...pending.slice(0, cursor)];
1302
+ const batch = rotated.slice(0, CLAUDE_INBOX_RECEIVER.maxConcurrent);
1303
+ cursor = pending.length ? (cursor + batch.length) % pending.length : 0;
1304
+ await Promise.all(
1305
+ batch.map(async (sessionId) => {
1306
+ if (!sessions.some((session) => session.sessionId === sessionId) || abort.signal.aborted)
1307
+ return;
1308
+ await report(sessionId, "waking" /* waking */);
1309
+ const outcome = await client.wake(sessionId, abort.signal);
1310
+ await report(sessionId, claudeWakeWaitingOn(outcome));
1311
+ })
1312
+ );
1313
+ } catch (error) {
1314
+ if (!abort.signal.aborted)
1315
+ options.report?.(
1316
+ error instanceof Error ? error.message : "Claude wake failed"
1317
+ );
1318
+ } finally {
1319
+ scanning = false;
1320
+ }
1321
+ };
1322
+ const timer = setInterval(() => {
1323
+ void tick();
1324
+ }, options.pollMs ?? CLAUDE_INBOX_RECEIVER.pollMs);
1325
+ timer.unref();
1326
+ void tick();
1327
+ return () => {
1328
+ abort.abort();
1329
+ clearInterval(timer);
1330
+ };
1331
+ };
1332
+
1333
+ // src/spawn-daemon.ts
1334
+ import { execFileSync as execFileSync2, spawn } from "child_process";
1335
+ import { existsSync, statSync as statSync2 } from "fs";
1336
+ import { homedir as homedir3 } from "os";
1337
+ import { basename as basename3, dirname as dirname3, join as join5 } from "path";
113
1338
 
114
1339
  // src/stop-requests.ts
115
1340
  var STOP_CAPABILITY = "stop";
@@ -292,13 +1517,13 @@ var resolveSpawnDirectory = (requested, options = {}) => {
292
1517
  const fallbackDir = options.fallbackDir ?? process.cwd();
293
1518
  const value = requested?.trim();
294
1519
  if (!value) return { ok: true, cwd: fallbackDir };
295
- const homeDir = options.homeDir ?? homedir();
296
- const cwd = value === "~" ? homeDir : value.startsWith("~/") || value.startsWith("~\\") ? join(homeDir, value.slice(2)) : value;
1520
+ const homeDir = options.homeDir ?? homedir3();
1521
+ const cwd = value === "~" ? homeDir : value.startsWith("~/") || value.startsWith("~\\") ? join5(homeDir, value.slice(2)) : value;
297
1522
  if (!(options.exists ?? existsSync)(cwd)) {
298
1523
  return { ok: false, message: `Directory does not exist: ${value}` };
299
1524
  }
300
1525
  try {
301
- if (!(options.isDirectory ?? ((path) => statSync(path).isDirectory()))(cwd)) {
1526
+ if (!(options.isDirectory ?? ((path) => statSync2(path).isDirectory()))(cwd)) {
302
1527
  return { ok: false, message: `Not a directory: ${value}` };
303
1528
  }
304
1529
  } catch {
@@ -390,9 +1615,9 @@ var runSpawnDaemon = async () => {
390
1615
  describedProviders = description;
391
1616
  });
392
1617
  describedProviders = describeLaunchableProviders(inventory.current.capabilities);
393
- const siblingEntry = process.argv[1] ? join(dirname(process.argv[1]), "pushary.js") : "";
394
- const codexBridgeEntry = process.argv[1] ? join(dirname(process.argv[1]), "pushary-codex-bridge.js") : "pushary-codex-bridge";
395
- const geminiBridgeEntry = process.argv[1] ? join(dirname(process.argv[1]), "pushary-gemini-bridge.js") : "pushary-gemini-bridge";
1618
+ const siblingEntry = process.argv[1] ? join5(dirname3(process.argv[1]), "pushary.js") : "";
1619
+ const codexBridgeEntry = process.argv[1] ? join5(dirname3(process.argv[1]), "pushary-codex-bridge.js") : "pushary-codex-bridge";
1620
+ const geminiBridgeEntry = process.argv[1] ? join5(dirname3(process.argv[1]), "pushary-gemini-bridge.js") : "pushary-gemini-bridge";
396
1621
  const spawnDirect = siblingEntry.length > 0 && existsSync(siblingEntry);
397
1622
  const existing = readDaemonStatus();
398
1623
  let handoff;
@@ -492,7 +1717,7 @@ var runSpawnDaemon = async () => {
492
1717
  if (!plan) {
493
1718
  return Promise.reject(new Error(FLAG_SHAPED_PROMPT_MESSAGE));
494
1719
  }
495
- return new Promise((resolve, reject) => {
1720
+ return new Promise((resolve3, reject) => {
496
1721
  try {
497
1722
  const child = plan.direct ? spawn(plan.command, plan.args, { cwd, detached: true, stdio: "ignore", env }) : spawnClaude(plan.command, plan.args, { cwd, detached: true, stdio: "ignore", env });
498
1723
  let started = false;
@@ -521,8 +1746,8 @@ var runSpawnDaemon = async () => {
521
1746
  });
522
1747
  }
523
1748
  child.unref();
524
- log(`spawn ${req.id}: launched a phone-requested ${plan.agent} session in ${basename(cwd)} (pid ${child.pid ?? "unknown"})`);
525
- resolve(typeof child.pid === "number" ? child.pid : null);
1749
+ log(`spawn ${req.id}: launched a phone-requested ${plan.agent} session in ${basename3(cwd)} (pid ${child.pid ?? "unknown"})`);
1750
+ resolve3(typeof child.pid === "number" ? child.pid : null);
526
1751
  });
527
1752
  } catch (err) {
528
1753
  reject(err);
@@ -530,7 +1755,7 @@ var runSpawnDaemon = async () => {
530
1755
  });
531
1756
  };
532
1757
  const kill = process.platform === "win32" ? (pid) => {
533
- execFileSync("taskkill", windowsTaskkillArgs(pid), { stdio: "ignore" });
1758
+ execFileSync2("taskkill", windowsTaskkillArgs(pid), { stdio: "ignore" });
534
1759
  } : process.kill.bind(process);
535
1760
  const stopDeps = {
536
1761
  kill: (pid, signal) => {
@@ -542,7 +1767,7 @@ var runSpawnDaemon = async () => {
542
1767
  return launched || ownsLiveProcess(request);
543
1768
  },
544
1769
  selfPid: process.pid,
545
- sleep: (ms) => new Promise((resolve) => setTimeout(resolve, ms)),
1770
+ sleep: (ms) => new Promise((resolve3) => setTimeout(resolve3, ms)),
546
1771
  isAlive: (pid) => {
547
1772
  try {
548
1773
  process.kill(pid, 0);
@@ -600,7 +1825,7 @@ var runSpawnDaemon = async () => {
600
1825
  completedStops.delete(acknowledgement.stopId);
601
1826
  break;
602
1827
  }
603
- if (attempt < 2) await new Promise((resolve) => setTimeout(resolve, 200 * (attempt + 1)));
1828
+ if (attempt < 2) await new Promise((resolve3) => setTimeout(resolve3, 200 * (attempt + 1)));
604
1829
  }
605
1830
  }
606
1831
  async function apply() {
@@ -743,8 +1968,12 @@ var runSpawnDaemon = async () => {
743
1968
  if (!stopped) timer = setTimeout(tick, jitter(nextDelay()));
744
1969
  };
745
1970
  const stopTranscripts = startRegisteredTranscripts(apiKey, machineId);
1971
+ const stopNativeHosts = startNativeHostReceivers(apiKey, machineId, log);
1972
+ const stopClaudeInbox = startClaudeInboxReceiver(apiKey, machineId, { report: log });
746
1973
  const teardown = () => {
747
1974
  stopTranscripts();
1975
+ stopClaudeInbox();
1976
+ stopNativeHosts();
748
1977
  if (stopped) return;
749
1978
  stopped = true;
750
1979
  if (timer) clearTimeout(timer);
@@ -767,7 +1996,7 @@ var runSpawnDaemon = async () => {
767
1996
  return;
768
1997
  }
769
1998
  const result = await sendHeartbeat(apiKey, {
770
- repoKey: basename(process.cwd()),
1999
+ repoKey: basename3(process.cwd()),
771
2000
  capabilities: [
772
2001
  ...launchAdvertisement(inventory.current.capabilities),
773
2002
  STOP_CAPABILITY,
@@ -799,24 +2028,24 @@ var runSpawnDaemon = async () => {
799
2028
  log(hasSpawnProvider(inventory.current.capabilities) ? `daemon online (pid ${process.pid}): this machine (${machineId}) can now be sent a new session from your phone. Ctrl-C to stop.` : `daemon online (pid ${process.pid}): no supported Claude, Codex, or Gemini CLI was found, so phone-start is unavailable. Ctrl-C to stop.`);
800
2029
  timer = setTimeout(tick, 0);
801
2030
  const signals = daemonSignals();
802
- await new Promise((resolve) => {
2031
+ await new Promise((resolve3) => {
803
2032
  const onSignal = () => finish();
804
2033
  for (const signal of signals) process.on(signal, onSignal);
805
2034
  finish = () => {
806
2035
  teardown();
807
2036
  for (const signal of signals) process.off(signal, onSignal);
808
- resolve();
2037
+ resolve3();
809
2038
  };
810
2039
  });
811
2040
  return 0;
812
2041
  };
813
2042
 
814
2043
  // src/daemon-sessions.ts
815
- import { execFileSync as execFileSync2 } from "child_process";
2044
+ import { execFileSync as execFileSync3 } from "child_process";
816
2045
  var readResidentSetKb = (pid) => {
817
2046
  if (process.platform === "win32") return null;
818
2047
  try {
819
- const output = execFileSync2("ps", ["-o", "rss=", "-p", String(pid)], {
2048
+ const output = execFileSync3("ps", ["-o", "rss=", "-p", String(pid)], {
820
2049
  encoding: "utf8",
821
2050
  stdio: ["ignore", "pipe", "ignore"]
822
2051
  });