@pushary/agent-hooks 1.9.57 → 1.9.59

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 (119) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/dist/{api-RQAY3A5V.js → api-UVJGNZIF.js} +10 -10
  3. package/dist/bin/pushary-bell-hook.js +27 -27
  4. package/dist/bin/pushary-bell.js +10 -10
  5. package/dist/bin/pushary-claude.js +26 -26
  6. package/dist/bin/pushary-clean.js +49 -46
  7. package/dist/bin/pushary-codex-bridge.js +21 -21
  8. package/dist/bin/pushary-codex-hook.js +32 -32
  9. package/dist/bin/pushary-codex-session.js +10 -8
  10. package/dist/bin/pushary-codex.js +14 -14
  11. package/dist/bin/pushary-connect.js +27 -26
  12. package/dist/bin/pushary-cowork.js +5 -5
  13. package/dist/bin/pushary-daemon.js +37 -37
  14. package/dist/bin/pushary-disconnect.js +26 -26
  15. package/dist/bin/pushary-doctor.js +41 -39
  16. package/dist/bin/pushary-elicitation-hook.js +27 -27
  17. package/dist/bin/pushary-gemini-bridge.js +15 -15
  18. package/dist/bin/pushary-gemini-hook.js +29 -29
  19. package/dist/bin/pushary-hook.js +31 -31
  20. package/dist/bin/pushary-login.js +13 -13
  21. package/dist/bin/pushary-logout.js +26 -26
  22. package/dist/bin/pushary-mcp.js +11 -11
  23. package/dist/bin/pushary-mode.js +13 -13
  24. package/dist/bin/pushary-notification-hook.js +26 -26
  25. package/dist/bin/pushary-opencode-hook.js +28 -28
  26. package/dist/bin/pushary-permission-denied-hook.js +31 -31
  27. package/dist/bin/pushary-permission-hook.js +30 -30
  28. package/dist/bin/pushary-post-hook.js +26 -26
  29. package/dist/bin/pushary-prompt-hook.js +26 -26
  30. package/dist/bin/pushary-session-end-hook.js +26 -26
  31. package/dist/bin/pushary-session-start-hook.js +33 -33
  32. package/dist/bin/pushary-setup.js +65 -268
  33. package/dist/bin/pushary-stats.js +5 -5
  34. package/dist/bin/pushary-status.js +32 -30
  35. package/dist/bin/pushary-stop-hook.js +26 -26
  36. package/dist/bin/pushary-stopfailure-hook.js +26 -26
  37. package/dist/bin/pushary-suggestions.js +13 -13
  38. package/dist/bin/pushary-transcript-register.js +18 -18
  39. package/dist/bin/pushary-transcripts.js +7 -7
  40. package/dist/bin/pushary-upgrade.js +107 -58
  41. package/dist/bin/pushary-wait.js +13 -13
  42. package/dist/bin/pushary.js +7 -7
  43. package/dist/chunk-2FVP7WRP.js +378 -0
  44. package/dist/{chunk-W6363YYZ.js → chunk-2WMZDKPM.js} +8 -8
  45. package/dist/{chunk-N3NSWWNU.js → chunk-3N2ZTHZZ.js} +1 -1
  46. package/dist/{chunk-JCMSISCC.js → chunk-3NROUNIS.js} +2 -2
  47. package/dist/{chunk-7RZO6VWD.js → chunk-3RKIJM26.js} +2 -2
  48. package/dist/{chunk-Z7Q36I4K.js → chunk-4QF2YLYK.js} +11 -7
  49. package/dist/{chunk-LVDC3RUE.js → chunk-4XXZUFWN.js} +1 -1
  50. package/dist/{chunk-BAJ3A3BU.js → chunk-5BNXJTOZ.js} +5 -2
  51. package/dist/{chunk-A54W5A74.js → chunk-5EFNBL6V.js} +3 -3
  52. package/dist/{chunk-4YTFYILS.js → chunk-5JWCWALO.js} +9 -9
  53. package/dist/{chunk-3IBUZQKM.js → chunk-5O6D4J2K.js} +2 -2
  54. package/dist/{chunk-DDQRYX2L.js → chunk-5PP4455M.js} +3 -3
  55. package/dist/{chunk-NQDEMRAS.js → chunk-6ONSTCDJ.js} +5 -5
  56. package/dist/{chunk-ECJJKDP7.js → chunk-75UYYIFJ.js} +1 -1
  57. package/dist/{chunk-GZL3KEPC.js → chunk-762CDAJJ.js} +140 -136
  58. package/dist/{chunk-YKYVDHIY.js → chunk-AS4Z6KS4.js} +1 -1
  59. package/dist/{chunk-4YNNAF53.js → chunk-AY32RSUC.js} +2 -2
  60. package/dist/{chunk-I43PY5G3.js → chunk-BKVX53QR.js} +1 -1
  61. package/dist/{chunk-5SFM6XB4.js → chunk-BMPZ5HVJ.js} +2 -2
  62. package/dist/{chunk-7OQ4AJC7.js → chunk-BRUHVTFB.js} +2 -7
  63. package/dist/{chunk-OZSP4RIQ.js → chunk-CPRA7IEK.js} +174 -41
  64. package/dist/{chunk-C3IWX6KU.js → chunk-D6CXQIXO.js} +1 -1
  65. package/dist/{chunk-T45PAPOG.js → chunk-DK35M4TH.js} +5 -5
  66. package/dist/{chunk-K45CFSYV.js → chunk-DS7X36Z3.js} +4 -4
  67. package/dist/{chunk-IBSRULIY.js → chunk-EBQTWH5O.js} +10 -10
  68. package/dist/{chunk-OLNBYAPI.js → chunk-EHCIQA5D.js} +4 -4
  69. package/dist/{chunk-2NIRYGTK.js → chunk-IBXHVYN2.js} +2 -2
  70. package/dist/{chunk-HCIB4YNB.js → chunk-JAKBCKEB.js} +3 -3
  71. package/dist/{chunk-PZDMZ7LM.js → chunk-JBCMXSBO.js} +5 -5
  72. package/dist/{chunk-IRZ5FVWG.js → chunk-KAJDKYUM.js} +3 -3
  73. package/dist/{chunk-AN6TVB2H.js → chunk-KHDD74MK.js} +5 -5
  74. package/dist/{chunk-ZZB4J6CX.js → chunk-KQHY6L3X.js} +4 -4
  75. package/dist/{chunk-UPFFO4F6.js → chunk-KQWN7C5C.js} +4 -4
  76. package/dist/{chunk-D2EETYAL.js → chunk-KW3DED2A.js} +14 -369
  77. package/dist/{chunk-TWLWDIFB.js → chunk-KZCYHWDF.js} +4 -2
  78. package/dist/{chunk-RFAF3TOX.js → chunk-L3NVHPBU.js} +94 -15
  79. package/dist/{chunk-LVZYIUSR.js → chunk-LAEW5QQD.js} +1 -1
  80. package/dist/{chunk-SQ5G6A6K.js → chunk-M46WDK64.js} +1 -1
  81. package/dist/{chunk-LVWXRXT7.js → chunk-M6MOQBR5.js} +1 -1
  82. package/dist/{chunk-FBKVCODG.js → chunk-MA37WNVS.js} +3 -3
  83. package/dist/{chunk-XTZHUIGJ.js → chunk-MMLIQDTG.js} +1 -1
  84. package/dist/{chunk-CB565EVO.js → chunk-MSU3VKGJ.js} +2 -2
  85. package/dist/{chunk-ZU6L34DQ.js → chunk-MYU5D5VH.js} +4 -4
  86. package/dist/{chunk-WXZN5CS3.js → chunk-OZIXBN3O.js} +2 -2
  87. package/dist/{chunk-JSQOHMNJ.js → chunk-PVANKDWM.js} +1 -1
  88. package/dist/{chunk-JYXIARGW.js → chunk-RAMQBSVZ.js} +1 -1
  89. package/dist/{chunk-S5LX25XE.js → chunk-RQDP2SJY.js} +1 -1
  90. package/dist/{chunk-DS6OR5OS.js → chunk-SIU6OWA6.js} +6 -6
  91. package/dist/{chunk-QLDTBZBP.js → chunk-T65RIQK2.js} +6 -6
  92. package/dist/{chunk-2PJENKRJ.js → chunk-TCH5UCPR.js} +77 -30
  93. package/dist/{chunk-QQYUWTYS.js → chunk-TDOKDCFH.js} +3 -3
  94. package/dist/chunk-UFJS4RR7.js +266 -0
  95. package/dist/{chunk-AEXACKDR.js → chunk-US3IT6ZC.js} +3 -3
  96. package/dist/{chunk-4QD7VE4E.js → chunk-UTJ5465F.js} +2 -2
  97. package/dist/{chunk-M7NX25ZA.js → chunk-UVZK5LIK.js} +1 -1
  98. package/dist/{chunk-IUOUNRLH.js → chunk-V3PDUDPB.js} +2 -2
  99. package/dist/{chunk-K5SDLOFS.js → chunk-VAJHDTJX.js} +11 -11
  100. package/dist/{chunk-QTCFEKLJ.js → chunk-W47WL6JY.js} +1 -1
  101. package/dist/{chunk-6QM54CSJ.js → chunk-WW24SG27.js} +5 -5
  102. package/dist/{chunk-3TJLPABQ.js → chunk-WWAVHLGS.js} +3 -3
  103. package/dist/{chunk-CUEUANIU.js → chunk-X4ES7QBT.js} +5 -5
  104. package/dist/{chunk-XXNT4FGA.js → chunk-XGP5VSQG.js} +1 -1
  105. package/dist/{chunk-LW7IWGSV.js → chunk-YQVKG5MC.js} +5 -5
  106. package/dist/{codex-app-cards-switch-SVGTZ2UN.js → codex-app-cards-switch-N77RWKCO.js} +21 -19
  107. package/dist/{codex-desktop-launch-SH7OUEEC.js → codex-desktop-launch-KIX7OMTL.js} +3 -1
  108. package/dist/{codex-session-launch-IQ57OL3N.js → codex-session-launch-XV7SIIWP.js} +18 -18
  109. package/dist/{config-SYLUYZQE.js → config-HH4DYYNX.js} +3 -3
  110. package/dist/global-upgrade-YWIVJIUR.js +34 -0
  111. package/dist/{identity-6KRUO2YO.js → identity-4J7Y2MW3.js} +4 -4
  112. package/dist/{live-capabilities-RKUHAWT6.js → live-capabilities-LHZBCI5U.js} +4 -4
  113. package/dist/{paths-MUPQZNLG.js → paths-ES3V7BWQ.js} +5 -3
  114. package/dist/{reapply-YZXZCWCI.js → reapply-KA5SIYWX.js} +26 -26
  115. package/dist/src/index.js +21 -21
  116. package/dist/{upgrade-lock-JXP27UKI.js → upgrade-lock-TJPKU5HL.js} +4 -6
  117. package/dist/{usage-OUV76AIY.js → usage-7ALXBJVJ.js} +3 -3
  118. package/package.json +1 -1
  119. package/dist/global-upgrade-SRCWQE7N.js +0 -26
@@ -4,38 +4,38 @@ import {
4
4
  } from "../chunk-CVWG2N3Y.js";
5
5
  import {
6
6
  exitOnFailedSettingsRequest
7
- } from "../chunk-K45CFSYV.js";
8
- import "../chunk-4YNNAF53.js";
7
+ } from "../chunk-DS7X36Z3.js";
8
+ import "../chunk-AY32RSUC.js";
9
9
  import {
10
10
  createIo
11
11
  } from "../chunk-FO3MDD26.js";
12
12
  import {
13
13
  exitOnHelpFlag
14
- } from "../chunk-M7NX25ZA.js";
14
+ } from "../chunk-UVZK5LIK.js";
15
15
  import {
16
16
  rejectUnknownFlags
17
17
  } from "../chunk-GMXKITVA.js";
18
- import "../chunk-IRZ5FVWG.js";
19
- import "../chunk-FBKVCODG.js";
20
- import "../chunk-HCIB4YNB.js";
21
- import "../chunk-SQ5G6A6K.js";
18
+ import "../chunk-KAJDKYUM.js";
19
+ import "../chunk-MA37WNVS.js";
20
+ import "../chunk-JAKBCKEB.js";
21
+ import "../chunk-M46WDK64.js";
22
22
  import {
23
23
  UsageError
24
24
  } from "../chunk-7QLSKOSU.js";
25
25
  import {
26
26
  EXIT
27
27
  } from "../chunk-KZERVKTD.js";
28
- import "../chunk-7RZO6VWD.js";
29
- import "../chunk-TWLWDIFB.js";
30
- import "../chunk-AEXACKDR.js";
28
+ import "../chunk-3RKIJM26.js";
29
+ import "../chunk-KZCYHWDF.js";
30
+ import "../chunk-US3IT6ZC.js";
31
31
  import "../chunk-QENTGE75.js";
32
32
  import {
33
33
  getApiKey,
34
34
  getBaseUrl
35
- } from "../chunk-LVDC3RUE.js";
36
- import "../chunk-5SFM6XB4.js";
35
+ } from "../chunk-4XXZUFWN.js";
36
+ import "../chunk-BMPZ5HVJ.js";
37
37
  import "../chunk-QTOLNRET.js";
38
- import "../chunk-GZL3KEPC.js";
38
+ import "../chunk-762CDAJJ.js";
39
39
 
40
40
  // bin/pushary-wait.ts
41
41
  exitOnHelpFlag("wait");
@@ -1,25 +1,25 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  installNetworkDefaults
4
- } from "../chunk-HCIB4YNB.js";
5
- import "../chunk-SQ5G6A6K.js";
4
+ } from "../chunk-JAKBCKEB.js";
5
+ import "../chunk-M46WDK64.js";
6
6
  import "../chunk-7QLSKOSU.js";
7
7
  import {
8
8
  EXIT
9
9
  } from "../chunk-KZERVKTD.js";
10
- import "../chunk-TWLWDIFB.js";
11
- import "../chunk-AEXACKDR.js";
10
+ import "../chunk-KZCYHWDF.js";
11
+ import "../chunk-US3IT6ZC.js";
12
12
  import "../chunk-QENTGE75.js";
13
- import "../chunk-LVDC3RUE.js";
13
+ import "../chunk-4XXZUFWN.js";
14
14
  import {
15
15
  LISTED_COMMANDS,
16
16
  renderHelp,
17
17
  resolveCommandName
18
- } from "../chunk-5SFM6XB4.js";
18
+ } from "../chunk-BMPZ5HVJ.js";
19
19
  import {
20
20
  getPackageVersion
21
21
  } from "../chunk-QTOLNRET.js";
22
- import "../chunk-GZL3KEPC.js";
22
+ import "../chunk-762CDAJJ.js";
23
23
 
24
24
  // src/cli/runtime.ts
25
25
  var supportsRuntime = (version) => {
@@ -0,0 +1,378 @@
1
+ import {
2
+ callMcpTool
3
+ } from "./chunk-5EFNBL6V.js";
4
+ import {
5
+ NPX_LATEST_COMMAND
6
+ } from "./chunk-BMPZ5HVJ.js";
7
+ import {
8
+ classifyConfirmResponse,
9
+ isAnswerSource
10
+ } from "./chunk-762CDAJJ.js";
11
+
12
+ // src/reach.ts
13
+ var reachVerdict = (channels) => {
14
+ const { yours, site } = channels;
15
+ if (yours === null || site === null) {
16
+ return channels.phoneReachable || channels.webActive > 0 ? { kind: "yours" } : { kind: "nobody" };
17
+ }
18
+ if (yours.phoneReachable) return { kind: "yours" };
19
+ const solo = site.otherMembers === 0;
20
+ if (solo) {
21
+ return site.phoneReachable || site.webActive > 0 ? { kind: "yours" } : { kind: "nobody" };
22
+ }
23
+ if (site.appDevices === 0 && site.webActive === 0) return { kind: "nobody" };
24
+ if (site.webActive > 0 && !yours.webAttributable) {
25
+ return { kind: "unattributable", otherMembers: site.otherMembers };
26
+ }
27
+ return { kind: "others", otherMembers: site.otherMembers };
28
+ };
29
+ var reachesYou = (channels) => reachVerdict(channels).kind === "yours";
30
+ var plural = (count, one, many = `${one}s`) => `${count} ${count === 1 ? one : many}`;
31
+ var describeReach = (channels) => {
32
+ const verdict = reachVerdict(channels);
33
+ if (verdict.kind === "nobody") return "nothing can receive an approval";
34
+ const parts = [];
35
+ const own = channels.yours;
36
+ if (verdict.kind === "yours" && own !== null && own.appDevices > 0) {
37
+ const platforms = own.appPlatforms.length > 0 ? ` (${own.appPlatforms.join(", ")})` : "";
38
+ parts.push(`${plural(own.appDevices, "app device")}${platforms}`);
39
+ } else if (channels.appDevices > 0) {
40
+ const platforms = channels.appPlatforms.length > 0 ? ` (${channels.appPlatforms.join(", ")})` : "";
41
+ parts.push(`${plural(channels.appDevices, "app device")}${platforms}`);
42
+ }
43
+ if (channels.webPhone > 0) parts.push(`${plural(channels.webPhone, "browser")} on a phone`);
44
+ if (channels.webDesktop > 0) parts.push(`${plural(channels.webDesktop, "browser")} on a desktop`);
45
+ const summary = parts.length > 0 ? parts.join(", ") : "a device";
46
+ if (verdict.kind === "others") return `${summary}, none of them yours`;
47
+ if (verdict.kind === "unattributable") return `${summary}, and no way to tell whose`;
48
+ return summary;
49
+ };
50
+ var describeAppRepair = (verdict) => {
51
+ switch (verdict.kind) {
52
+ case "yours":
53
+ return null;
54
+ case "nobody":
55
+ return [
56
+ "Open the Pushary app, sign in, and allow notifications.",
57
+ `If this terminal was never paired with your phone, run: ${NPX_LATEST_COMMAND} setup --connect app`
58
+ ];
59
+ case "others":
60
+ return [
61
+ `Reachable devices belong to other members of this workspace (${verdict.otherMembers}).`,
62
+ "Sign in on the Pushary app with your own account so approvals can reach you."
63
+ ];
64
+ case "unattributable":
65
+ return [
66
+ "Something is reachable here, but not identifiably yours.",
67
+ "Sign in on the Pushary app so your approvals are routed to your device."
68
+ ];
69
+ }
70
+ };
71
+ var describeJustPairedRepair = (verdict) => {
72
+ if (verdict.kind === "nobody") {
73
+ return [
74
+ "The app asks for the notification permission after it grants the pairing.",
75
+ "Open the Pushary app and allow notifications. That is the whole remaining step."
76
+ ];
77
+ }
78
+ return describeAppRepair(verdict) ?? [];
79
+ };
80
+
81
+ // src/setup/verify.ts
82
+ var plural2 = (count, one) => `${count} ${one}${count === 1 ? "" : "s"}`;
83
+ var SETUP_AGENT_NAME = "Pushary setup";
84
+ var ownAppPlatforms = (channels) => channels.yours?.appPlatforms ?? channels.appPlatforms;
85
+ var describeRecipients = (push, channels) => {
86
+ const parts = [];
87
+ if (push.app > 0) {
88
+ const platforms = ownAppPlatforms(channels);
89
+ const suffix = platforms.length > 0 ? ` (${platforms.join(", ")})` : "";
90
+ parts.push(`${plural2(push.app, "app device")}${suffix}`);
91
+ }
92
+ if (push.web > 0) parts.push(plural2(push.web, "browser"));
93
+ return parts.length > 0 ? parts.join(" and ") : plural2(push.total, "device");
94
+ };
95
+ var classifyDelivery = (channels, push) => {
96
+ if (push.total <= 0) return { kind: "no-recipients" };
97
+ const verdict = reachVerdict(channels);
98
+ if (verdict.kind === "yours" && (push.app > 0 || channels.yours === null || channels.site?.otherMembers === 0)) {
99
+ return { kind: "delivered", recipients: push.total, deviceLabel: describeRecipients(push, channels) };
100
+ }
101
+ if (verdict.kind === "unattributable") {
102
+ return { kind: "unattributable", recipients: push.total, otherMembers: verdict.otherMembers };
103
+ }
104
+ if (verdict.kind === "others") {
105
+ return { kind: "others-only", recipients: push.total, otherMembers: verdict.otherMembers };
106
+ }
107
+ if (verdict.kind === "yours") {
108
+ const otherMembers = channels.site?.otherMembers ?? 0;
109
+ return { kind: "unattributable", recipients: push.total, otherMembers };
110
+ }
111
+ return { kind: "delivered", recipients: push.total, deviceLabel: describeRecipients(push, channels) };
112
+ };
113
+ var sendTestNotification = async (apiKey) => {
114
+ const result = await callMcpTool(apiKey, "send_notification", {
115
+ title: "Pushary is connected",
116
+ body: "This setup notification reached you. Agent hooks still need a real-agent check.",
117
+ agentName: SETUP_AGENT_NAME
118
+ }, { timeoutMs: 2e4 });
119
+ const toCount = (value) => typeof value === "number" && Number.isFinite(value) ? value : 0;
120
+ const app = toCount(result.delivery?.mobile?.recipients);
121
+ const web = toCount(result.delivery?.web?.recipients);
122
+ return { app, web, total: typeof result.sent === "number" ? result.sent : app + web };
123
+ };
124
+ var verifyDelivery = async (apiKey, channels, deps = {}) => {
125
+ const send = deps.send ?? sendTestNotification;
126
+ try {
127
+ return classifyDelivery(channels, await send(apiKey));
128
+ } catch (err) {
129
+ return { kind: "error", detail: err instanceof Error ? err.message : "send failed" };
130
+ }
131
+ };
132
+ var describeVerifyOutcome = (outcome) => {
133
+ switch (outcome.kind) {
134
+ case "delivered":
135
+ return { ok: true, message: `Delivered to ${outcome.deviceLabel}. Push works.`, next: [] };
136
+ case "no-recipients":
137
+ return {
138
+ ok: false,
139
+ message: "Nothing to deliver to. Agents are configured; approvals will fall back to the terminal.",
140
+ next: [
141
+ "Run pushary pair to pair your phone.",
142
+ "Then pushary doctor --roundtrip to test a synthetic question and answer."
143
+ ]
144
+ };
145
+ case "others-only":
146
+ return {
147
+ ok: false,
148
+ message: `Delivered to ${plural2(outcome.recipients, "device")} on this site, none of them yours.`,
149
+ next: [
150
+ `Approvals for your agent would wake one of your ${plural2(outcome.otherMembers, "teammate")}.`,
151
+ "Run pushary pair to pair your own phone."
152
+ ]
153
+ };
154
+ case "unattributable":
155
+ return {
156
+ ok: false,
157
+ message: `Delivered to ${plural2(outcome.recipients, "device")} on this site, and no way to tell whose.`,
158
+ next: [
159
+ "Browser subscriptions carry no owner, and this site has other members.",
160
+ "Install the Pushary app and pair it with pushary pair, or run pushary doctor --roundtrip to prove it reaches you."
161
+ ]
162
+ };
163
+ case "error":
164
+ return {
165
+ ok: false,
166
+ message: `Could not send the test notification (${outcome.detail}).`,
167
+ next: ["Agents are configured. Run pushary doctor once the connection is back."]
168
+ };
169
+ }
170
+ };
171
+ var ROUND_TRIP_QUESTION = "Pushary delivery test. Answer either way to test returning an answer. This does not test your agent hooks.";
172
+ var ROUND_TRIP_TIMEOUT_MS = 55e3;
173
+ var reportNoInterrupt = async () => {
174
+ };
175
+ var defaultAsk = (apiKey, agentName) => (
176
+ // wait:false so the poll is ours: the blocking form returns on the SITE's
177
+ // policy timeout, which a user may have set to seconds, and a setup check
178
+ // reporting "unanswered" after three seconds would be a lie about the product.
179
+ callMcpTool(apiKey, "ask_user", {
180
+ question: ROUND_TRIP_QUESTION,
181
+ type: "confirm",
182
+ agentName,
183
+ wait: false
184
+ }, { timeoutMs: 15e3 })
185
+ );
186
+ var defaultWait = (apiKey, correlationId) => callMcpTool(apiKey, "wait_for_answer", {
187
+ correlationId,
188
+ timeoutMs: ROUND_TRIP_TIMEOUT_MS
189
+ }, { timeoutMs: ROUND_TRIP_TIMEOUT_MS + 5e3 });
190
+ var defaultCancel = async (apiKey, correlationId) => {
191
+ await callMcpTool(apiKey, "cancel_question", { correlationId }, { timeoutMs: 1e4 }).catch(() => {
192
+ });
193
+ };
194
+ var answeredRoundTrip = (answer, correlationId, latencyMs) => {
195
+ if (answer.value === void 0 || classifyConfirmResponse(answer.value) === null) {
196
+ return { kind: "error", detail: "the server reported an answer without a valid Yes, No or defer value" };
197
+ }
198
+ return {
199
+ kind: "answered",
200
+ value: answer.value,
201
+ latencyMs,
202
+ correlationId,
203
+ ...isAnswerSource(answer.answerSource) ? { answerSource: answer.answerSource } : {}
204
+ };
205
+ };
206
+ var verifyRoundTrip = async (apiKey, agentName = SETUP_AGENT_NAME, deps = {}) => {
207
+ const ask = deps.ask ?? defaultAsk;
208
+ const wait = deps.wait ?? defaultWait;
209
+ const cancel = deps.cancel ?? defaultCancel;
210
+ const interrupted = deps.interrupted ?? reportNoInterrupt;
211
+ const now = deps.now ?? Date.now;
212
+ const started = now();
213
+ let asked;
214
+ try {
215
+ asked = await ask(apiKey, agentName);
216
+ } catch (err) {
217
+ return { kind: "error", detail: err instanceof Error ? err.message : "ask failed" };
218
+ }
219
+ let shutdown;
220
+ const stop = (code) => {
221
+ if (shutdown) return;
222
+ shutdown = (async () => {
223
+ let timer;
224
+ try {
225
+ await Promise.race([
226
+ Promise.all([
227
+ cancel(apiKey, asked.correlationId).catch(() => {
228
+ }),
229
+ interrupted(code).catch(() => {
230
+ })
231
+ ]),
232
+ new Promise((resolve) => {
233
+ timer = setTimeout(resolve, 4e3);
234
+ })
235
+ ]);
236
+ } finally {
237
+ clearTimeout(timer);
238
+ process.stderr.write("Delivery test interrupted. Cleanup was attempted; an unresolved question expires automatically.\n");
239
+ process.exit(code);
240
+ }
241
+ })();
242
+ };
243
+ const onInterrupt = () => stop(130);
244
+ const onTerminate = () => stop(143);
245
+ if (asked.correlationId) {
246
+ process.on("SIGINT", onInterrupt);
247
+ process.on("SIGTERM", onTerminate);
248
+ }
249
+ let answerObserved = false;
250
+ try {
251
+ if (asked.noDevices || asked.warning) return { kind: "no-recipients" };
252
+ if (asked.mode === "notify_only" || asked.status === "notified") return { kind: "notify-only" };
253
+ if (!asked.correlationId) return { kind: "error", detail: "no correlationId returned" };
254
+ const answer = asked.answered === true ? asked : await wait(apiKey, asked.correlationId);
255
+ if (answer.answered === true) {
256
+ const outcome = answeredRoundTrip(answer, asked.correlationId, now() - started);
257
+ answerObserved = outcome.kind === "answered";
258
+ return outcome;
259
+ }
260
+ return { kind: "unanswered" };
261
+ } catch (err) {
262
+ return { kind: "error", detail: err instanceof Error ? err.message : "wait failed" };
263
+ } finally {
264
+ if (shutdown) await shutdown;
265
+ process.off("SIGINT", onInterrupt);
266
+ process.off("SIGTERM", onTerminate);
267
+ if (!answerObserved && asked.correlationId) await cancel(apiKey, asked.correlationId).catch(() => {
268
+ });
269
+ }
270
+ };
271
+ var describeAnswerSource = (source) => {
272
+ switch (source) {
273
+ case "mobile_app":
274
+ case "mobile_background":
275
+ return "Answered on your phone.";
276
+ case "mac_app":
277
+ return "Answered on your Mac.";
278
+ case "sandbox":
279
+ return "This was a simulated sandbox answer.";
280
+ default:
281
+ return source ? `Answer source: ${source}. This was a synthetic test.` : "The answering surface was not reported. This was a synthetic test.";
282
+ }
283
+ };
284
+ var NOTIFICATION_ANSWER_STEPS = /* @__PURE__ */ new Map([
285
+ ["ios", "Press and hold the notification, then tap Approve."],
286
+ ["android", "Expand the notification, then tap Approve."]
287
+ ]);
288
+ var EITHER_PHONE_ANSWER_STEP = "Press and hold the notification on iPhone, or expand it on Android, then tap Approve.";
289
+ var notificationAnswerStep = (appPlatforms) => {
290
+ const platforms = [...new Set(appPlatforms)];
291
+ const [onlyPlatform] = platforms;
292
+ if (onlyPlatform === void 0) return null;
293
+ if (platforms.length > 1) return EITHER_PHONE_ANSWER_STEP;
294
+ return NOTIFICATION_ANSWER_STEPS.get(onlyPlatform) ?? EITHER_PHONE_ANSWER_STEP;
295
+ };
296
+ var WAITING_FOR_TAP = "Waiting for you to tap the notification";
297
+ var WAITING_FOR_ANSWER = "Waiting for your answer";
298
+ var describeAnswerTestPrompt = (appPlatforms) => {
299
+ const step = notificationAnswerStep(appPlatforms);
300
+ return { step, waiting: step === null ? WAITING_FOR_TAP : WAITING_FOR_ANSWER };
301
+ };
302
+ var CHECK_THE_NOTIFICATION_OPENED = "Check the notification actually opened something you could tap, then run pushary doctor --roundtrip.";
303
+ var unansweredNextStep = (appPlatforms) => {
304
+ const step = notificationAnswerStep(appPlatforms);
305
+ return step === null ? CHECK_THE_NOTIFICATION_OPENED : `To send another one, run pushary doctor --roundtrip. ${step}`;
306
+ };
307
+ var describeRoundTripOutcome = (outcome, appPlatforms = []) => {
308
+ switch (outcome.kind) {
309
+ case "answered":
310
+ return {
311
+ ok: true,
312
+ message: `Answered "${outcome.value}" in ${(outcome.latencyMs / 1e3).toFixed(1)}s. ${describeAnswerSource(outcome.answerSource)}`,
313
+ next: []
314
+ };
315
+ case "unanswered":
316
+ return {
317
+ ok: false,
318
+ message: "The question was sent and nothing came back.",
319
+ next: [
320
+ "A push that arrives and cannot be answered is the most common way this breaks, so this is worth resolving now.",
321
+ unansweredNextStep(appPlatforms)
322
+ ]
323
+ };
324
+ case "notify-only":
325
+ return {
326
+ ok: true,
327
+ message: "Sent. This site is in notify-only mode, so questions never wait for an answer.",
328
+ next: ["Switch with pushary mode push_first if you want approvals to hold for your phone."]
329
+ };
330
+ case "no-recipients":
331
+ return {
332
+ ok: false,
333
+ message: "Nothing to ask. No phone or browser is connected to this site.",
334
+ next: [
335
+ "Run pushary pair to pair your phone.",
336
+ "Or pushary pair --app if you already have the Pushary app."
337
+ ]
338
+ };
339
+ case "error":
340
+ return {
341
+ ok: false,
342
+ message: `Could not complete the round trip (${outcome.detail}).`,
343
+ next: ["Agents are configured. Run pushary doctor --roundtrip once the connection is back."]
344
+ };
345
+ }
346
+ };
347
+ var describeRoundTrip = (outcome, appPlatforms = []) => {
348
+ const report = describeRoundTripOutcome(outcome, appPlatforms);
349
+ return { ...report, next: [...report.next, "Agent hook activation remains unverified. Restart your agent and check one real approval."] };
350
+ };
351
+ var roundTripEvidence = (outcome) => ({
352
+ kind: outcome?.kind ?? "skipped",
353
+ ...outcome?.kind === "answered" ? {
354
+ correlationId: outcome.correlationId ?? null,
355
+ answerSource: outcome.answerSource ?? null,
356
+ value: outcome.value,
357
+ latencyMs: outcome.latencyMs
358
+ } : {},
359
+ hookActivation: "unverified"
360
+ });
361
+
362
+ export {
363
+ reachVerdict,
364
+ reachesYou,
365
+ describeReach,
366
+ describeAppRepair,
367
+ describeJustPairedRepair,
368
+ SETUP_AGENT_NAME,
369
+ ownAppPlatforms,
370
+ verifyDelivery,
371
+ describeVerifyOutcome,
372
+ verifyRoundTrip,
373
+ notificationAnswerStep,
374
+ describeAnswerTestPrompt,
375
+ describeRoundTripOutcome,
376
+ describeRoundTrip,
377
+ roundTripEvidence
378
+ };
@@ -6,38 +6,38 @@ import {
6
6
  } from "./chunk-QW3FRUFB.js";
7
7
  import {
8
8
  ensureDaemonRunning
9
- } from "./chunk-Z7Q36I4K.js";
9
+ } from "./chunk-4QF2YLYK.js";
10
10
  import {
11
11
  hookOwnerOf
12
- } from "./chunk-CUEUANIU.js";
12
+ } from "./chunk-X4ES7QBT.js";
13
13
  import {
14
14
  buildTranscriptRecords,
15
15
  decodePublicKey,
16
16
  generateSessionKey,
17
17
  wrapSessionKey
18
- } from "./chunk-JSQOHMNJ.js";
18
+ } from "./chunk-PVANKDWM.js";
19
19
  import {
20
20
  fetchTranscriptRecipients,
21
21
  uploadTranscriptRecords
22
- } from "./chunk-N3NSWWNU.js";
22
+ } from "./chunk-3N2ZTHZZ.js";
23
23
  import {
24
24
  transcriptEnvironmentPreference,
25
25
  transcriptsEnabled
26
- } from "./chunk-C3IWX6KU.js";
26
+ } from "./chunk-D6CXQIXO.js";
27
27
  import {
28
28
  readTranscriptPreference
29
- } from "./chunk-IRZ5FVWG.js";
29
+ } from "./chunk-KAJDKYUM.js";
30
30
  import {
31
31
  codexHome,
32
32
  pusharyDir
33
- } from "./chunk-TWLWDIFB.js";
33
+ } from "./chunk-KZCYHWDF.js";
34
34
  import {
35
35
  writeJsonAtomic
36
36
  } from "./chunk-QENTGE75.js";
37
37
  import {
38
38
  getApiKey,
39
39
  getBaseUrl
40
- } from "./chunk-LVDC3RUE.js";
40
+ } from "./chunk-4XXZUFWN.js";
41
41
 
42
42
  // src/transcript-registration.ts
43
43
  import { createHash } from "crypto";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  getBaseUrl
3
- } from "./chunk-LVDC3RUE.js";
3
+ } from "./chunk-4XXZUFWN.js";
4
4
 
5
5
  // src/transcript-api.ts
6
6
  var fetchTranscriptRecipients = async (apiKey, baseUrl = getBaseUrl()) => {
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  parseSseMessages
3
- } from "./chunk-A54W5A74.js";
3
+ } from "./chunk-5EFNBL6V.js";
4
4
  import {
5
5
  NPX_LATEST_COMMAND
6
- } from "./chunk-5SFM6XB4.js";
6
+ } from "./chunk-BMPZ5HVJ.js";
7
7
 
8
8
  // src/mcp-stdio-bridge.ts
9
9
  var MCP_BRIDGE_BINARY = "pushary-mcp";
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  configFilePath
3
- } from "./chunk-LVDC3RUE.js";
3
+ } from "./chunk-4XXZUFWN.js";
4
4
  import {
5
5
  normalizeInstallSource
6
- } from "./chunk-GZL3KEPC.js";
6
+ } from "./chunk-762CDAJJ.js";
7
7
 
8
8
  // src/install-source.ts
9
9
  import { readFileSync } from "fs";
@@ -4,16 +4,16 @@ import {
4
4
  } from "./chunk-XHKBHWLX.js";
5
5
  import {
6
6
  readControlMode
7
- } from "./chunk-IRZ5FVWG.js";
7
+ } from "./chunk-KAJDKYUM.js";
8
8
  import {
9
9
  getMachineId
10
- } from "./chunk-SQ5G6A6K.js";
10
+ } from "./chunk-M46WDK64.js";
11
11
  import {
12
12
  pusharyDir
13
- } from "./chunk-TWLWDIFB.js";
13
+ } from "./chunk-KZCYHWDF.js";
14
14
  import {
15
15
  resolveGlobalBinary
16
- } from "./chunk-AEXACKDR.js";
16
+ } from "./chunk-US3IT6ZC.js";
17
17
  import {
18
18
  readJsonSafe,
19
19
  writeFileAtomic,
@@ -22,7 +22,7 @@ import {
22
22
  } from "./chunk-QENTGE75.js";
23
23
  import {
24
24
  getApiKey
25
- } from "./chunk-LVDC3RUE.js";
25
+ } from "./chunk-4XXZUFWN.js";
26
26
  import {
27
27
  CLI_PACKAGE_NAMES,
28
28
  cmdShimTarget,
@@ -32,7 +32,7 @@ import {
32
32
  } from "./chunk-QTOLNRET.js";
33
33
  import {
34
34
  isRecord
35
- } from "./chunk-GZL3KEPC.js";
35
+ } from "./chunk-762CDAJJ.js";
36
36
 
37
37
  // src/daemon-lifecycle.ts
38
38
  import { dirname, join, win32 as win32Path } from "path";
@@ -40,7 +40,9 @@ import { existsSync, mkdirSync, readFileSync, realpathSync, renameSync, unlinkSy
40
40
  import { createHash, randomUUID } from "crypto";
41
41
  import { homedir, userInfo } from "os";
42
42
  import { execFileSync } from "child_process";
43
+ var daemonIsServing = (status) => status.kind === "running" || status.kind === "version-mismatch" || status.kind === "configuration-mismatch";
43
44
  var daemonNeedsHandoff = (status) => status.kind === "version-mismatch" || status.kind === "configuration-mismatch" || status.kind === "unresponsive" && status.processAlive === true;
45
+ var daemonProcessIsThere = (status) => daemonIsServing(status) || daemonNeedsHandoff(status);
44
46
  var DAEMON_REMOTE_PROOF_MAX_AGE_MS = 18e4;
45
47
  var DAEMON_REMOTE_PROOF_CLOCK_SKEW_MS = 3e4;
46
48
  var DAEMON_STARTUP_TIMEOUT_MS = 5e3;
@@ -573,7 +575,7 @@ var acquireDaemonLease = (options = {}) => {
573
575
  return { kind: "unavailable", detail: err instanceof Error ? err.message : String(err) };
574
576
  }
575
577
  const status = readDaemonStatus(options);
576
- if (status.kind === "running" || status.kind === "version-mismatch" || status.kind === "configuration-mismatch") {
578
+ if (daemonIsServing(status)) {
577
579
  return { kind: "already-running", status };
578
580
  }
579
581
  const lock = readJsonSafe(lockPath(dir));
@@ -1121,6 +1123,8 @@ var waitForDaemonRemoteProof = async (options = {}) => {
1121
1123
  };
1122
1124
 
1123
1125
  export {
1126
+ daemonIsServing,
1127
+ daemonProcessIsThere,
1124
1128
  DAEMON_COLD_START_TIMEOUT_MS,
1125
1129
  daemonHasRemoteProof,
1126
1130
  daemonServiceManagerAvailable,
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  NPX_LATEST_COMMAND
3
- } from "./chunk-5SFM6XB4.js";
3
+ } from "./chunk-BMPZ5HVJ.js";
4
4
 
5
5
  // src/config.ts
6
6
  import { readFileSync } from "fs";
@@ -3,7 +3,7 @@ import {
3
3
  } from "./chunk-POFBE6ZY.js";
4
4
  import {
5
5
  autoUpdateState
6
- } from "./chunk-TWLWDIFB.js";
6
+ } from "./chunk-KZCYHWDF.js";
7
7
  import {
8
8
  readJsonSafe,
9
9
  writeJsonAtomic
@@ -17,7 +17,7 @@ import {
17
17
  } from "./chunk-QTOLNRET.js";
18
18
  import {
19
19
  isAutoUpdateResult
20
- } from "./chunk-GZL3KEPC.js";
20
+ } from "./chunk-762CDAJJ.js";
21
21
 
22
22
  // src/auto-update.ts
23
23
  import { spawn } from "child_process";
@@ -155,6 +155,7 @@ var describeAutoUpdateCheck = (state) => {
155
155
  var UNFINISHED_AUTO_UPDATE_RESULTS = ["failed", "finish_pending", "finish_blocked"];
156
156
  var isUnfinishedAutoUpdate = (state) => state.lastResult !== void 0 && UNFINISHED_AUTO_UPDATE_RESULTS.includes(state.lastResult);
157
157
  var isRetryableFinishRecord = (state) => state.lastResult === "finish_pending" || state.lastResult === "updated" && Boolean(state.detail);
158
+ var holdsUnfinishedFinish = (state) => state.lastResult === "finish_pending" || state.lastResult === "finish_blocked";
158
159
  var hasRetryableUpgradeFinish = (state, installed) => state.to === installed && isRetryableFinishRecord(state);
159
160
  var hasSettledUpgradeFinish = (state, installed) => state.to === installed && state.lastResult === "finish_blocked";
160
161
  var hasPendingUpgradeFinish = (state, installed) => hasRetryableUpgradeFinish(state, installed) || hasSettledUpgradeFinish(state, installed);
@@ -217,9 +218,11 @@ export {
217
218
  parseAutoUpdateFlag,
218
219
  describeAutoUpdateCheck,
219
220
  isUnfinishedAutoUpdate,
221
+ holdsUnfinishedFinish,
220
222
  hasRetryableUpgradeFinish,
221
223
  hasSettledUpgradeFinish,
222
224
  hasPendingUpgradeFinish,
225
+ pendingAutoUpdateAnnouncement,
223
226
  describeAutoUpdateAnnouncement,
224
227
  takeAutoUpdateAnnouncement,
225
228
  readAutoUpdateState,
@@ -1,12 +1,12 @@
1
1
  import {
2
2
  keyCheckFromResponse
3
- } from "./chunk-FBKVCODG.js";
3
+ } from "./chunk-MA37WNVS.js";
4
4
  import {
5
5
  installNetworkDefaults
6
- } from "./chunk-HCIB4YNB.js";
6
+ } from "./chunk-JAKBCKEB.js";
7
7
  import {
8
8
  getBaseUrl
9
- } from "./chunk-LVDC3RUE.js";
9
+ } from "./chunk-4XXZUFWN.js";
10
10
 
11
11
  // src/retry.ts
12
12
  var isRetryable = (err) => {