@pushary/agent-hooks 1.9.42 → 1.9.44

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 (111) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/README.md +1 -0
  3. package/data/SKILL.md +7 -1
  4. package/data/cowork/SKILL.md +3 -1
  5. package/data/cursor-plugin/.cursor-plugin/plugin.json +1 -1
  6. package/data/cursor-plugin/skills/pushary/SKILL.md +6 -0
  7. package/data/vscode-plugin/skills/pushary/SKILL.md +6 -0
  8. package/dist/bin/pushary-bell-hook.js +27 -26
  9. package/dist/bin/pushary-bell.js +11 -11
  10. package/dist/bin/pushary-claude.js +30 -28
  11. package/dist/bin/pushary-clean.js +39 -38
  12. package/dist/bin/pushary-codex-bridge.js +38 -28
  13. package/dist/bin/pushary-codex-hook.js +35 -33
  14. package/dist/bin/pushary-codex-session.d.ts +1 -0
  15. package/dist/bin/pushary-codex-session.js +253 -0
  16. package/dist/bin/pushary-codex.js +13 -12
  17. package/dist/bin/pushary-connect.js +28 -27
  18. package/dist/bin/pushary-cowork.js +6 -6
  19. package/dist/bin/pushary-daemon.js +48 -47
  20. package/dist/bin/pushary-disconnect.js +27 -26
  21. package/dist/bin/pushary-doctor.js +85 -82
  22. package/dist/bin/pushary-elicitation-hook.js +27 -26
  23. package/dist/bin/pushary-gemini-bridge.js +22 -22
  24. package/dist/bin/pushary-gemini-hook.js +32 -30
  25. package/dist/bin/pushary-hook.js +33 -32
  26. package/dist/bin/pushary-login.js +16 -16
  27. package/dist/bin/pushary-logout.js +28 -27
  28. package/dist/bin/pushary-mcp.js +9 -9
  29. package/dist/bin/pushary-mode.js +14 -14
  30. package/dist/bin/pushary-notification-hook.js +26 -25
  31. package/dist/bin/pushary-opencode-hook.js +31 -29
  32. package/dist/bin/pushary-permission-denied-hook.js +31 -30
  33. package/dist/bin/pushary-permission-hook.js +30 -29
  34. package/dist/bin/pushary-post-hook.js +26 -25
  35. package/dist/bin/pushary-prompt-hook.js +26 -25
  36. package/dist/bin/pushary-session-end-hook.js +26 -25
  37. package/dist/bin/pushary-session-start-hook.js +31 -30
  38. package/dist/bin/pushary-setup.js +165 -112
  39. package/dist/bin/pushary-stats.js +5 -5
  40. package/dist/bin/pushary-status.js +35 -34
  41. package/dist/bin/pushary-stop-hook.js +26 -25
  42. package/dist/bin/pushary-stopfailure-hook.js +26 -25
  43. package/dist/bin/pushary-suggestions.js +14 -14
  44. package/dist/bin/pushary-transcript-register.js +18 -18
  45. package/dist/bin/pushary-transcripts.js +7 -7
  46. package/dist/bin/pushary-upgrade.js +41 -40
  47. package/dist/bin/pushary-wait.js +14 -14
  48. package/dist/bin/pushary.js +7 -6
  49. package/dist/{chunk-ZX3PFROQ.js → chunk-2NVXFWQY.js} +1 -1
  50. package/dist/{chunk-7L6YSHZO.js → chunk-3IRC3XFG.js} +2 -2
  51. package/dist/{chunk-IXNVIWNN.js → chunk-42CVAOC5.js} +2 -2
  52. package/dist/{chunk-EBZK2SJ2.js → chunk-4JHEZ2PV.js} +12 -5
  53. package/dist/{chunk-JBK47Y2M.js → chunk-4WBAHVBK.js} +11 -11
  54. package/dist/{chunk-AW4TDBUJ.js → chunk-53UZN7JW.js} +3 -3
  55. package/dist/{chunk-I7EZMNKI.js → chunk-5ZSBQHCM.js} +12 -173
  56. package/dist/{chunk-KYFWYPHN.js → chunk-6EV3B5CD.js} +1 -1
  57. package/dist/{chunk-X7TNLYNZ.js → chunk-75M7NFFC.js} +1 -1
  58. package/dist/{chunk-AUDM6HU6.js → chunk-7UM4NCUI.js} +2 -2
  59. package/dist/{chunk-5INZFKHL.js → chunk-ALY7ZBNB.js} +1 -1
  60. package/dist/{chunk-CGRVIUKE.js → chunk-BKK3JCTD.js} +80 -164
  61. package/dist/{chunk-K2SSNAUW.js → chunk-CAJCCQ7N.js} +1 -1
  62. package/dist/{chunk-B7PZBWBU.js → chunk-CESLE4UN.js} +10 -10
  63. package/dist/{chunk-MI67QQEQ.js → chunk-CEZTYTBN.js} +13 -11
  64. package/dist/{chunk-6PABNQVH.js → chunk-DD6IBKEJ.js} +4 -4
  65. package/dist/{chunk-Z4RM54LU.js → chunk-E76QCZIA.js} +2 -2
  66. package/dist/{chunk-RBUCXUT7.js → chunk-FI2F5KZJ.js} +4 -4
  67. package/dist/{chunk-IVUP2EE4.js → chunk-FKDZU6YX.js} +4 -4
  68. package/dist/{chunk-EA5NWDK7.js → chunk-FYNMFSIZ.js} +29 -0
  69. package/dist/{chunk-M2YE3OEC.js → chunk-GBCR2FH2.js} +3 -3
  70. package/dist/chunk-GOEIW2VH.js +266 -0
  71. package/dist/{chunk-JQX2L3ES.js → chunk-GRAPJKDK.js} +1 -1
  72. package/dist/{chunk-DPISXMLL.js → chunk-HV4TEKNP.js} +4 -4
  73. package/dist/{chunk-FMYXPJYS.js → chunk-IWHDNMM3.js} +5 -5
  74. package/dist/{chunk-ORLTWJ2O.js → chunk-JNU3LV47.js} +355 -138
  75. package/dist/{chunk-ZJY4KON3.js → chunk-KILK6SWZ.js} +7 -7
  76. package/dist/{chunk-J7OBJZ5S.js → chunk-KYLY6UMN.js} +1 -1
  77. package/dist/{chunk-T3Q4B6WV.js → chunk-L2ZM7LXN.js} +2 -2
  78. package/dist/{chunk-PJ3C6IID.js → chunk-LEHBWBCG.js} +4 -4
  79. package/dist/{chunk-ODPULZZG.js → chunk-M2L7625S.js} +2 -2
  80. package/dist/{chunk-BDSIE2MT.js → chunk-MIBCRQWK.js} +2 -2
  81. package/dist/{chunk-MAB6CGL5.js → chunk-MRQJCIFO.js} +1 -1
  82. package/dist/{chunk-F43LAKAK.js → chunk-OR6KJOHE.js} +2 -2
  83. package/dist/{chunk-Y3HTWB5S.js → chunk-OSMNLNPF.js} +1 -1
  84. package/dist/{chunk-CHD6IUNM.js → chunk-OWQOMGBF.js} +1 -1
  85. package/dist/{chunk-RTM5GVRZ.js → chunk-OZ7MLVDM.js} +15 -15
  86. package/dist/{chunk-4WM7IVVQ.js → chunk-PGHN5D4N.js} +2 -2
  87. package/dist/{chunk-OLLWYLQX.js → chunk-PQAFH4DF.js} +3 -11
  88. package/dist/{chunk-E47JEBUR.js → chunk-RGBTKOCC.js} +45 -8
  89. package/dist/{chunk-4UCTKLHY.js → chunk-S2ALW446.js} +3 -3
  90. package/dist/{chunk-3U3FU6ZX.js → chunk-SI2NC6UU.js} +3 -3
  91. package/dist/chunk-SVCT5D7K.js +196 -0
  92. package/dist/{chunk-ZVMFRUBD.js → chunk-SWDPCDLT.js} +4 -4
  93. package/dist/{chunk-DIJ532S7.js → chunk-T3KMJI4W.js} +2 -1
  94. package/dist/{chunk-NRSTNZYJ.js → chunk-T4CKP47U.js} +5 -5
  95. package/dist/{chunk-M7VKFAAK.js → chunk-TJZUQRMH.js} +3 -3
  96. package/dist/{chunk-ZV56INA6.js → chunk-TKTS3SIM.js} +3 -3
  97. package/dist/{chunk-RETRVO47.js → chunk-TWATOPHK.js} +2 -2
  98. package/dist/{chunk-WZNRDDC4.js → chunk-UC4PNLMK.js} +6 -6
  99. package/dist/{chunk-ASXBYX2N.js → chunk-UJXLVH66.js} +1 -1
  100. package/dist/{chunk-Q3QRD67J.js → chunk-UY5E2UUW.js} +5 -5
  101. package/dist/{chunk-FP2ZJK3T.js → chunk-UZVF6MP2.js} +5 -5
  102. package/dist/{chunk-STETTN4K.js → chunk-W2TA4EVZ.js} +5 -5
  103. package/dist/{chunk-QRNNIDGY.js → chunk-WBD7HZEZ.js} +1 -1
  104. package/dist/{chunk-QPT4PTUG.js → chunk-WMO2XVG4.js} +1 -1
  105. package/dist/{chunk-BZIXDIIT.js → chunk-XPQDBTW4.js} +1 -1
  106. package/dist/{global-upgrade-YEAGJG4G.js → global-upgrade-QSSDYZCZ.js} +6 -6
  107. package/dist/{paths-HQAICWAF.js → paths-55DDFLY4.js} +2 -2
  108. package/dist/{reapply-XBEB2B5A.js → reapply-MEU66ALO.js} +23 -22
  109. package/dist/src/index.js +20 -19
  110. package/dist/{upgrade-lock-GIO5QZ7E.js → upgrade-lock-YLXAU4AD.js} +3 -3
  111. package/package.json +5 -2
@@ -12,21 +12,21 @@ import {
12
12
  terminalColumns,
13
13
  useColor
14
14
  } from "./chunk-FO3MDD26.js";
15
- import {
16
- callMcpTool
17
- } from "./chunk-AW4TDBUJ.js";
18
15
  import {
19
16
  reportedInstallSource
20
- } from "./chunk-ODPULZZG.js";
17
+ } from "./chunk-M2L7625S.js";
18
+ import {
19
+ callMcpTool
20
+ } from "./chunk-53UZN7JW.js";
21
21
  import {
22
22
  NPX_LATEST_COMMAND
23
- } from "./chunk-DIJ532S7.js";
23
+ } from "./chunk-T3KMJI4W.js";
24
24
  import {
25
25
  NO_PAIRING_COMPUTER,
26
26
  PAIR_TTL_SECONDS,
27
27
  classifyConfirmResponse,
28
28
  isAnswerSource
29
- } from "./chunk-ORLTWJ2O.js";
29
+ } from "./chunk-JNU3LV47.js";
30
30
 
31
31
  // src/reach.ts
32
32
  var reachVerdict = (channels) => {
@@ -972,7 +972,7 @@ var watchSkip = (listener) => {
972
972
  });
973
973
  return { skipped: () => skipped, woken, stop };
974
974
  };
975
- var PHONE_WAIT_REPAIR_LINE = `Open the Pushary app, sign in with the same account, and allow notifications. Then run ${NPX_LATEST_COMMAND} doctor.`;
975
+ var PHONE_WAIT_REPAIR_LINE = `Open the Pushary app, sign in with the same account, and allow notifications. Then run ${NPX_LATEST_COMMAND} doctor --roundtrip. Your agent configuration is saved; no reinstall is needed.`;
976
976
  var phoneWaitRepair = (verdict) => verdict.kind === "nobody" ? [PHONE_WAIT_REPAIR_LINE] : describeAppRepair(verdict) ?? [];
977
977
  var pollForYourPhone = async (apiKey, firstSeen, options) => {
978
978
  const clock = options.clock ?? SYSTEM_PAIR_CLOCK;
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  HOOK_HOLD_BUDGET_ENV,
3
3
  hookHoldBudgetProof
4
- } from "./chunk-ORLTWJ2O.js";
4
+ } from "./chunk-JNU3LV47.js";
5
5
 
6
6
  // src/hold-budget.ts
7
7
  var takeHookHoldBudget = (args = process.argv.slice(2), env = process.env) => {
@@ -10,10 +10,10 @@ import {
10
10
  resolvePackageVersionFromBinary,
11
11
  resolvePathBinary,
12
12
  resolvePathBinaryAsync
13
- } from "./chunk-EBZK2SJ2.js";
13
+ } from "./chunk-4JHEZ2PV.js";
14
14
  import {
15
15
  MANAGED_PROVIDERS
16
- } from "./chunk-ORLTWJ2O.js";
16
+ } from "./chunk-JNU3LV47.js";
17
17
 
18
18
  // src/setup/codex-install.ts
19
19
  var NATIVE_BINARY_MIN_BYTES = 20 * 1024 * 1024;
@@ -1,18 +1,18 @@
1
1
  import {
2
2
  macAppBridgeAvailable
3
- } from "./chunk-FP2ZJK3T.js";
3
+ } from "./chunk-UZVF6MP2.js";
4
4
  import {
5
5
  keyPrefix
6
- } from "./chunk-M7VKFAAK.js";
6
+ } from "./chunk-TJZUQRMH.js";
7
7
  import {
8
8
  bridgeAgentKey
9
- } from "./chunk-EBZK2SJ2.js";
9
+ } from "./chunk-4JHEZ2PV.js";
10
10
  import {
11
11
  readJsonSafe
12
12
  } from "./chunk-AQFEP65J.js";
13
13
  import {
14
14
  isValidApiKey
15
- } from "./chunk-ORLTWJ2O.js";
15
+ } from "./chunk-JNU3LV47.js";
16
16
 
17
17
  // src/shared-mcp-key.ts
18
18
  var asRecord = (value) => value !== null && typeof value === "object" && !Array.isArray(value) ? value : void 0;
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  configFilePath
3
- } from "./chunk-CHD6IUNM.js";
3
+ } from "./chunk-OWQOMGBF.js";
4
4
  import {
5
5
  normalizeInstallSource
6
- } from "./chunk-ORLTWJ2O.js";
6
+ } from "./chunk-JNU3LV47.js";
7
7
 
8
8
  // src/install-source.ts
9
9
  import { readFileSync } from "fs";
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  CODEX_HOOK_STATUS
3
- } from "./chunk-Z4RM54LU.js";
3
+ } from "./chunk-E76QCZIA.js";
4
4
  import {
5
5
  NPX_LATEST_COMMAND
6
- } from "./chunk-DIJ532S7.js";
6
+ } from "./chunk-T3KMJI4W.js";
7
7
 
8
8
  // src/setup/codex-trust.ts
9
9
  var CODEX_TRUST_TELEMETRY = {
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  getBaseUrl
3
- } from "./chunk-CHD6IUNM.js";
3
+ } from "./chunk-OWQOMGBF.js";
4
4
 
5
5
  // src/transcript-api.ts
6
6
  var fetchTranscriptRecipients = async (apiKey, baseUrl = getBaseUrl()) => {
@@ -3,10 +3,10 @@ import {
3
3
  } from "./chunk-KZERVKTD.js";
4
4
  import {
5
5
  renderCommandHelp
6
- } from "./chunk-DIJ532S7.js";
6
+ } from "./chunk-T3KMJI4W.js";
7
7
  import {
8
8
  getPackageVersion
9
- } from "./chunk-ORLTWJ2O.js";
9
+ } from "./chunk-JNU3LV47.js";
10
10
 
11
11
  // src/cli/usage.ts
12
12
  var exitOnHelpFlag = (command) => {
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  guardBinary
3
- } from "./chunk-EBZK2SJ2.js";
3
+ } from "./chunk-4JHEZ2PV.js";
4
4
 
5
5
  // src/bell/install.ts
6
6
  import { join } from "path";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  NPX_LATEST_COMMAND
3
- } from "./chunk-DIJ532S7.js";
3
+ } from "./chunk-T3KMJI4W.js";
4
4
 
5
5
  // src/config.ts
6
6
  import { readFileSync } from "fs";
@@ -6,38 +6,38 @@ import {
6
6
  } from "./chunk-DWQYCOPI.js";
7
7
  import {
8
8
  ensureDaemonRunning
9
- } from "./chunk-STETTN4K.js";
9
+ } from "./chunk-W2TA4EVZ.js";
10
+ import {
11
+ hookOwnerOf
12
+ } from "./chunk-T4CKP47U.js";
13
+ import {
14
+ transcriptEnvironmentPreference,
15
+ transcriptsEnabled
16
+ } from "./chunk-WBD7HZEZ.js";
17
+ import {
18
+ readTranscriptPreference
19
+ } from "./chunk-TJZUQRMH.js";
10
20
  import {
11
21
  buildTranscriptRecords,
12
22
  decodePublicKey,
13
23
  generateSessionKey,
14
24
  wrapSessionKey
15
- } from "./chunk-X7TNLYNZ.js";
25
+ } from "./chunk-75M7NFFC.js";
16
26
  import {
17
27
  fetchTranscriptRecipients,
18
28
  uploadTranscriptRecords
19
- } from "./chunk-MAB6CGL5.js";
20
- import {
21
- transcriptEnvironmentPreference,
22
- transcriptsEnabled
23
- } from "./chunk-QRNNIDGY.js";
24
- import {
25
- hookOwnerOf
26
- } from "./chunk-NRSTNZYJ.js";
27
- import {
28
- readTranscriptPreference
29
- } from "./chunk-M7VKFAAK.js";
29
+ } from "./chunk-MRQJCIFO.js";
30
30
  import {
31
31
  codexHome,
32
32
  pusharyDir
33
- } from "./chunk-EBZK2SJ2.js";
33
+ } from "./chunk-4JHEZ2PV.js";
34
34
  import {
35
35
  writeJsonAtomic
36
36
  } from "./chunk-AQFEP65J.js";
37
37
  import {
38
38
  getApiKey,
39
39
  getBaseUrl
40
- } from "./chunk-CHD6IUNM.js";
40
+ } from "./chunk-OWQOMGBF.js";
41
41
 
42
42
  // src/transcript-registration.ts
43
43
  import { createHash } from "crypto";
@@ -1,12 +1,12 @@
1
1
  import {
2
2
  pusharyDir
3
- } from "./chunk-EBZK2SJ2.js";
3
+ } from "./chunk-4JHEZ2PV.js";
4
4
  import {
5
5
  readJsonSafe
6
6
  } from "./chunk-AQFEP65J.js";
7
7
  import {
8
8
  isRecord
9
- } from "./chunk-ORLTWJ2O.js";
9
+ } from "./chunk-JNU3LV47.js";
10
10
 
11
11
  // src/process-ownership.ts
12
12
  import { createHash, randomUUID } from "crypto";
@@ -3,11 +3,11 @@ import {
3
3
  decodePublicKey,
4
4
  generateSessionKey,
5
5
  wrapSessionKey
6
- } from "./chunk-X7TNLYNZ.js";
6
+ } from "./chunk-75M7NFFC.js";
7
7
  import {
8
8
  fetchTranscriptRecipients,
9
9
  uploadTranscriptRecords
10
- } from "./chunk-MAB6CGL5.js";
10
+ } from "./chunk-MRQJCIFO.js";
11
11
 
12
12
  // src/live-session.ts
13
13
  var MAX_TRANSCRIPT_QUEUE = 100;
@@ -24,19 +24,11 @@ var liveSessionSleep = (ms, signal) => new Promise((resolve) => {
24
24
  signal.addEventListener("abort", done, { once: true });
25
25
  });
26
26
  var postLiveSession = (options, path, body) => {
27
- const controller = new AbortController();
28
- const abort = () => controller.abort();
29
- const timeout = setTimeout(abort, options.requestTimeoutMs ?? 1e4);
30
- if (options.signal.aborted) abort();
31
- else options.signal.addEventListener("abort", abort, { once: true });
32
27
  return fetch(`${options.baseUrl}${path}`, {
33
28
  method: "POST",
34
29
  headers: { Authorization: `Bearer ${options.apiKey}`, "Content-Type": "application/json" },
35
30
  body: JSON.stringify(body),
36
- signal: controller.signal
37
- }).finally(() => {
38
- clearTimeout(timeout);
39
- options.signal.removeEventListener("abort", abort);
31
+ signal: AbortSignal.any([options.signal, AbortSignal.timeout(options.requestTimeoutMs ?? 1e4)])
40
32
  });
41
33
  };
42
34
  var postLiveSessionUntilAccepted = async (options, path, body) => {
@@ -1,13 +1,14 @@
1
1
  import {
2
2
  MCP_BRIDGE_BINARY
3
- } from "./chunk-RETRVO47.js";
3
+ } from "./chunk-TWATOPHK.js";
4
4
  import {
5
5
  KEY_FILE_MODE
6
- } from "./chunk-M7VKFAAK.js";
6
+ } from "./chunk-TJZUQRMH.js";
7
7
  import {
8
+ NpmInstallError,
8
9
  NpmMissingError,
9
10
  NpmPermissionError
10
- } from "./chunk-EBZK2SJ2.js";
11
+ } from "./chunk-4JHEZ2PV.js";
11
12
  import {
12
13
  readJsonSafe,
13
14
  writeFileAtomic,
@@ -15,7 +16,10 @@ import {
15
16
  } from "./chunk-AQFEP65J.js";
16
17
  import {
17
18
  NPX_LATEST_COMMAND
18
- } from "./chunk-DIJ532S7.js";
19
+ } from "./chunk-T3KMJI4W.js";
20
+ import {
21
+ readSetupAgentFailures
22
+ } from "./chunk-JNU3LV47.js";
19
23
 
20
24
  // src/instruction-file.ts
21
25
  import { existsSync, readFileSync, writeFileSync, mkdirSync, rmSync } from "fs";
@@ -35,6 +39,7 @@ You have Pushary MCP tools that reach the user on their phone. Use them proactiv
35
39
  - When meaningful work finishes while the user is away, or they requested an alert, call \`send_notification\` so the user knows it is done. Skip trivial completions and duplicate reports to someone already reading. Always pass \`context\` with a \`type\` of "task_complete", "error" or "info": it is what tells Pushary this is a task update, so the user's own setting for where updates land can route it.
36
40
  - Confirm notifications can offer lock-screen actions; arbitrary choices and text open the app. The Mac notch handles personal account questions, not Partner customer enrollment.
37
41
  - Use \`cancel_question\` to retract a pending question once it is no longer needed.
42
+ - When the user asks to be reminded later, call \`schedule_reminder\` with \`body\` and either \`inMinutes\` or \`at\` with an explicit UTC offset. Confirm the returned time, never guess their timezone, and do not use \`ask_user\` as a reminder. Pushary saves it and delivers after this agent exits. Call with no arguments to list pending reminders or \`cancelReminderId\` to cancel one before dispatch starts.
38
43
  - Respect the user's Pushary delivery mode: it decides where an approval is surfaced (phone, terminal, or awareness only), not whether it is required. In Terminal mode do not expect a phone answer; the approval waits in the terminal. If the Pushary hook already gated an action, a single approval is enough, do not ask again.
39
44
 
40
45
  Pass \`agentName\` as "${label} - <project folder name>" so the user knows which session is asking. If a question times out, follow \`handoffAction\` when present, otherwise \`nextAction\`: keep waiting with the same correlationId while pending. A poll ending is not question expiry. Cancel only for an explicit handoff or when the answer is no longer needed. If cancellation returns \`handoffAction: "stop"\`, stop. Otherwise, if cancellation returns false, poll once for 1 second and honor the answer that won the race.`;
@@ -103,7 +108,8 @@ var FX_TOOL_DISCOVERY_NOTE = 'In fx, these tools are named mcp_pushary_ask_user,
103
108
  var FX_AGENT_LABEL = "fx";
104
109
  var fxInstructionsBody = () => `${renderAgentInstructions(FX_AGENT_LABEL)}
105
110
 
106
- ${FX_TOOL_DISCOVERY_NOTE}`;
111
+ ${FX_TOOL_DISCOVERY_NOTE}
112
+ For a reminder later, select mcp_pushary_schedule_reminder with mcp_select_tool, then call it. Pushary schedules delivery on its server; fx can exit.`;
107
113
  var ALLOW_ACTION = "allow";
108
114
  var PERMISSION_KEY = "permission";
109
115
  var WORKSPACES_KEY = "workspaces";
@@ -459,6 +465,23 @@ var fxIgnoresInstructionsFile = (path, fxDir) => {
459
465
  }
460
466
  };
461
467
 
468
+ // src/setup/config-write.ts
469
+ var AgentConfigWriteError = class extends Error {
470
+ constructor(error) {
471
+ super(error instanceof Error ? error.message : String(error), { cause: error });
472
+ this.name = "AgentConfigWriteError";
473
+ }
474
+ };
475
+ var writeSetupConfigFile = (path, content, mode = 384) => {
476
+ try {
477
+ writeFileAtomic(path, content, mode);
478
+ } catch (error) {
479
+ throw new AgentConfigWriteError(error);
480
+ }
481
+ };
482
+ var writeSetupConfigJson = (path, value, mode) => writeSetupConfigFile(path, `${JSON.stringify(value, null, 2)}
483
+ `, mode);
484
+
462
485
  // src/setup/agent-failure.ts
463
486
  var AgentConfigError = class extends Error {
464
487
  kind;
@@ -483,7 +506,8 @@ var describedAgentFailure = (agent, kind) => ({
483
506
  agent,
484
507
  kind,
485
508
  detail: AGENT_FAILURE_DETAILS[kind],
486
- remedy: DESCRIBED_FAILURE_REMEDIES[kind]
509
+ remedy: DESCRIBED_FAILURE_REMEDIES[kind],
510
+ stage: kind === "not-wired" ? "hook-validation" : "account-check"
487
511
  });
488
512
  var NPM_PERMISSION_REMEDIES = {
489
513
  "global-prefix": "fix-npm-prefix",
@@ -494,11 +518,22 @@ var remedyForError = (error) => {
494
518
  if (error instanceof NpmPermissionError) return NPM_PERMISSION_REMEDIES[error.kind];
495
519
  return "retry-agent";
496
520
  };
521
+ var setupFailureDiagnostic = (error) => {
522
+ if (error instanceof AgentConfigError) return { stage: "config-read" };
523
+ if (error instanceof AgentConfigWriteError) return { stage: "config-write", diagnosticCode: "config-write-failed" };
524
+ if (error instanceof NpmMissingError) return { stage: "runtime-install", diagnosticCode: "npm-missing" };
525
+ if (error instanceof NpmPermissionError) {
526
+ return { stage: "runtime-install", diagnosticCode: error.kind === "global-prefix" ? "npm-global-prefix-denied" : "npm-file-in-use" };
527
+ }
528
+ if (error instanceof NpmInstallError) return { stage: "runtime-install", diagnosticCode: "npm-install-failed" };
529
+ return { stage: "unknown" };
530
+ };
497
531
  var agentFailureFromError = (agent, error, describeError) => ({
498
532
  agent,
499
533
  kind: error instanceof AgentConfigError ? error.kind : "installer-error",
500
534
  detail: describeError(error),
501
- remedy: remedyForError(error)
535
+ remedy: remedyForError(error),
536
+ ...setupFailureDiagnostic(error)
502
537
  });
503
538
  var retryAgentCommand = (agent) => `${NPX_LATEST_COMMAND} setup --agents ${agent}`;
504
539
  var takeOverHooksCommand = (agent) => `${retryAgentCommand(agent)} --take-over-hooks`;
@@ -511,7 +546,7 @@ var describeMacAccountMismatch = (mismatch) => [
511
546
  var REMAINING_AGENTS_CONTINUE = "Other agents will continue. Re-run setup to retry.";
512
547
  var afterAgentFailureNote = (agentsStillToSetUp) => agentsStillToSetUp > 0 ? REMAINING_AGENTS_CONTINUE : null;
513
548
  var failedAgentsContext = (configuredAgentNames) => configuredAgentNames.length > 0 ? `(configured: ${configuredAgentNames.join(", ")})` : null;
514
- var agentFailureWire = (failures) => failures.map(({ agent, kind }) => ({ agent, kind }));
549
+ var agentFailureWire = (failures) => readSetupAgentFailures(failures);
515
550
 
516
551
  // src/fx-lifecycle.ts
517
552
  import { existsSync as existsSync3 } from "fs";
@@ -642,6 +677,8 @@ export {
642
677
  withFxPusharyServer,
643
678
  FX_QUESTION_TOOL,
644
679
  fxIgnoresInstructionsFile,
680
+ writeSetupConfigFile,
681
+ writeSetupConfigJson,
645
682
  AgentConfigError,
646
683
  configFailureKind,
647
684
  describedAgentFailure,
@@ -1,16 +1,16 @@
1
1
  import {
2
2
  readExistingMachineId
3
- } from "./chunk-QPT4PTUG.js";
3
+ } from "./chunk-WMO2XVG4.js";
4
4
  import {
5
5
  NetworkError
6
6
  } from "./chunk-7QLSKOSU.js";
7
7
  import {
8
8
  getBaseUrl
9
- } from "./chunk-CHD6IUNM.js";
9
+ } from "./chunk-OWQOMGBF.js";
10
10
  import {
11
11
  clientContextHeaders,
12
12
  getPackageVersion
13
- } from "./chunk-ORLTWJ2O.js";
13
+ } from "./chunk-JNU3LV47.js";
14
14
 
15
15
  // src/cli/net.ts
16
16
  import { createRequire } from "module";
@@ -5,16 +5,16 @@ import {
5
5
  import {
6
6
  NOTIFICATION_MATCHED_TYPES,
7
7
  POSTTOOLUSE_MIRRORED_TOOLS
8
- } from "./chunk-K2SSNAUW.js";
8
+ } from "./chunk-CAJCCQ7N.js";
9
9
  import {
10
10
  isProtectedByScope,
11
11
  registerHookEntry
12
- } from "./chunk-EBZK2SJ2.js";
12
+ } from "./chunk-4JHEZ2PV.js";
13
13
  import {
14
14
  CLAUDE_PRETOOLUSE_MATCHER,
15
15
  HOOK_BUDGETS,
16
16
  HOOK_LOCATOR_BINARY
17
- } from "./chunk-ORLTWJ2O.js";
17
+ } from "./chunk-JNU3LV47.js";
18
18
 
19
19
  // src/gemini-config.ts
20
20
  var GEMINI_HOOK_BINARY = "pushary-gemini-hook";
@@ -0,0 +1,196 @@
1
+ import {
2
+ liveSessionSleep,
3
+ postLiveSession
4
+ } from "./chunk-PQAFH4DF.js";
5
+ import {
6
+ isWaitForAnswerResponse,
7
+ repoKeyFor,
8
+ writeDegraded
9
+ } from "./chunk-GOEIW2VH.js";
10
+ import {
11
+ callMcpTool
12
+ } from "./chunk-53UZN7JW.js";
13
+ import {
14
+ CODEX_NATIVE_APPROVAL_CAPABILITY,
15
+ CODEX_NATIVE_APPROVAL_METHODS,
16
+ canonicalJsonText,
17
+ codexNativeApprovalResponse,
18
+ parseCodexNativeApproval
19
+ } from "./chunk-JNU3LV47.js";
20
+
21
+ // src/codex-native-approvals.ts
22
+ import { createHash, randomUUID } from "crypto";
23
+ var record = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
24
+ var validId = (id) => typeof id === "string" && id.length > 0 && id.length <= 200 || typeof id === "number" && Number.isSafeInteger(id);
25
+ var PATCH_LIMIT = 32;
26
+ var WAIT_LIMIT_MS = 3e5;
27
+ var CodexNativeApprovals = class {
28
+ constructor(options) {
29
+ this.options = options;
30
+ }
31
+ generation = randomUUID();
32
+ pending = /* @__PURE__ */ new Map();
33
+ retired = /* @__PURE__ */ new Set();
34
+ patches = /* @__PURE__ */ new Map();
35
+ tasks = /* @__PURE__ */ new Set();
36
+ closed = false;
37
+ handle(message, threadId) {
38
+ if (this.closed || !message.method) return false;
39
+ const p = message.params ?? {};
40
+ if (message.id === void 0) {
41
+ this.observe(message.method, p);
42
+ return false;
43
+ }
44
+ if (!validId(message.id) || !CODEX_NATIVE_APPROVAL_METHODS.some((method) => method === message.method)) return false;
45
+ if (this.retired.has(message.id)) return true;
46
+ const changes = this.patches.get(this.patchKey(p));
47
+ const approval = parseCodexNativeApproval({ method: message.method, params: p, ...changes ? { changes } : {} });
48
+ if (!approval || approval.threadId !== threadId) return false;
49
+ const repoKey = repoKeyFor(approval.cwd ?? this.options.cwd);
50
+ const subjectHash = createHash("sha256").update(canonicalJsonText({ request: approval.request, cwd: this.options.cwd, repoKey })).digest("hex");
51
+ const existing = this.pending.get(message.id);
52
+ if (existing) {
53
+ if (existing.owner.subjectHash !== subjectHash) this.retire(message.id, existing, false);
54
+ return true;
55
+ }
56
+ if (this.pending.size >= 8) return false;
57
+ const owner = {
58
+ capability: CODEX_NATIVE_APPROVAL_CAPABILITY,
59
+ machineId: this.options.machineId,
60
+ generation: this.generation,
61
+ threadId: approval.threadId,
62
+ requestId: message.id,
63
+ subjectHash,
64
+ method: approval.request.method
65
+ };
66
+ const pending = { owner, approval, repoKey, controller: new AbortController(), replied: false, creating: true, correlationId: randomUUID() };
67
+ this.pending.set(message.id, pending);
68
+ const task = this.answer(pending).finally(() => {
69
+ this.tasks.delete(task);
70
+ });
71
+ this.tasks.add(task);
72
+ return true;
73
+ }
74
+ async close() {
75
+ this.closed = true;
76
+ for (const [id, pending] of this.pending) this.retire(id, pending, false);
77
+ await Promise.allSettled(this.tasks);
78
+ }
79
+ patchKey(params) {
80
+ return JSON.stringify([params.threadId, params.turnId, params.itemId]);
81
+ }
82
+ observe(method, params) {
83
+ if (method === "item/started" && record(params.item) && params.item.type === "fileChange" && Array.isArray(params.item.changes)) {
84
+ if (this.patches.size >= PATCH_LIMIT) this.patches.delete(this.patches.keys().next().value ?? "");
85
+ this.patches.set(this.patchKey({ ...params, itemId: params.item.id }), params.item.changes);
86
+ }
87
+ for (const [id, pending] of this.pending) {
88
+ const a = pending.approval;
89
+ const resolved = method === "serverRequest/resolved" && params.requestId === id;
90
+ const sameThread = params.threadId === a.threadId;
91
+ const closed = sameThread && (method === "thread/closed" || method === "thread/archived" || method === "turn/completed" && record(params.turn) && params.turn.id === a.turnId || method === "item/completed" && params.turnId === a.turnId && record(params.item) && params.item.id === a.itemId);
92
+ const patchChanged = method === "item/fileChange/patchUpdated" && sameThread && params.turnId === a.turnId && params.itemId === a.itemId;
93
+ if (resolved || closed || patchChanged) this.retire(id, pending, resolved || closed);
94
+ }
95
+ if (method === "item/fileChange/patchUpdated") this.patches.delete(this.patchKey(params));
96
+ if (method === "turn/completed" || method === "thread/closed") this.patches.clear();
97
+ }
98
+ retire(id, pending, nativeClosed) {
99
+ pending.controller.abort();
100
+ this.pending.delete(id);
101
+ this.retired.add(id);
102
+ if (!nativeClosed && !this.closed && !pending.replied && !this.options.shared) {
103
+ this.options.reply(id, codexNativeApprovalResponse(pending.approval, "Deny"));
104
+ pending.replied = true;
105
+ }
106
+ const task = this.cleanup(pending, nativeClosed).finally(() => {
107
+ this.tasks.delete(task);
108
+ });
109
+ this.tasks.add(task);
110
+ }
111
+ async post(pending, operation, signal) {
112
+ const boundedSignal = AbortSignal.any([signal, AbortSignal.timeout(this.options.requestTimeoutMs ?? 1e4)]);
113
+ const response = await postLiveSession({ ...this.options, signal: boundedSignal }, "/api/agent/native-approval", {
114
+ operation,
115
+ owner: pending.owner,
116
+ request: pending.approval.request,
117
+ correlationId: pending.correlationId,
118
+ claimId: pending.claimId,
119
+ localSurfaceAvailable: this.options.shared,
120
+ cwd: this.options.cwd,
121
+ repoKey: pending.repoKey
122
+ });
123
+ const body = await response.json();
124
+ if (!response.ok || !record(body)) throw new Error("Native approval service unavailable");
125
+ return body;
126
+ }
127
+ async cleanup(pending, nativeClosed) {
128
+ if (pending.creating || !pending.correlationId) return;
129
+ try {
130
+ const signal = AbortSignal.timeout(3e3);
131
+ while (!signal.aborted) {
132
+ const result = await this.post(pending, nativeClosed ? "request_closed" : "unavailable", signal);
133
+ if (result.recorded === true) return;
134
+ await liveSessionSleep(100, signal);
135
+ }
136
+ throw new Error("Native approval cleanup not recorded");
137
+ } catch {
138
+ writeDegraded("questions", { reason: "native_approval_cleanup_unconfirmed", fallback: "expiry", event: pending.owner.method });
139
+ }
140
+ }
141
+ async answer(pending) {
142
+ const signal = AbortSignal.any([this.options.signal, pending.controller.signal, AbortSignal.timeout(WAIT_LIMIT_MS)]);
143
+ try {
144
+ let created;
145
+ try {
146
+ created = await this.post(pending, "create", AbortSignal.timeout(this.options.requestTimeoutMs ?? 1e4));
147
+ pending.correlationId = typeof created.correlationId === "string" ? created.correlationId : void 0;
148
+ } finally {
149
+ pending.creating = false;
150
+ }
151
+ if (signal.aborted) return;
152
+ if (created.kind === "deny") {
153
+ this.options.reply(pending.owner.requestId, codexNativeApprovalResponse(pending.approval, "Deny"));
154
+ pending.replied = true;
155
+ return;
156
+ }
157
+ if (created.kind !== "ask" || !pending.correlationId) throw new Error("Native approval unavailable");
158
+ let answer = null;
159
+ while (!signal.aborted) {
160
+ const result = await callMcpTool(
161
+ this.options.apiKey,
162
+ "wait_for_answer",
163
+ { correlationId: pending.correlationId, timeoutMs: 25e3 },
164
+ { baseUrl: this.options.baseUrl, signal, timeoutMs: 27e3, maxRetries: 1 }
165
+ );
166
+ if (!isWaitForAnswerResponse(result)) throw new Error("Invalid native approval answer");
167
+ if (result.answered) {
168
+ answer = typeof result.value === "string" ? result.value : null;
169
+ break;
170
+ }
171
+ if (!("status" in result) || result.status !== "pending") throw new Error("Native approval no longer pending");
172
+ await liveSessionSleep(100, signal);
173
+ }
174
+ if (signal.aborted || !answer || !pending.approval.options.includes(answer)) throw new Error("No supported approval answer");
175
+ const claim = await this.post(pending, "claim", signal);
176
+ if (typeof claim.claimId !== "string" || !claim.claimId) throw new Error("Native approval dispatch refused");
177
+ pending.claimId = claim.claimId;
178
+ if (signal.aborted || this.pending.get(pending.owner.requestId) !== pending) return;
179
+ this.options.reply(pending.owner.requestId, codexNativeApprovalResponse(pending.approval, answer));
180
+ pending.replied = true;
181
+ await this.post(pending, "reply_queued", AbortSignal.timeout(3e3));
182
+ } catch {
183
+ if (!pending.controller.signal.aborted && !this.options.signal.aborted && !this.closed && !this.options.shared && !pending.replied) {
184
+ this.options.reply(pending.owner.requestId, codexNativeApprovalResponse(pending.approval, "Deny"));
185
+ pending.replied = true;
186
+ }
187
+ if (!pending.controller.signal.aborted && !this.options.signal.aborted) writeDegraded("questions", { reason: "native_approval_unavailable", fallback: this.options.shared ? "native client" : "refused", event: pending.owner.method });
188
+ } finally {
189
+ if (!pending.claimId || !pending.replied || signal.aborted) await this.cleanup(pending, false);
190
+ }
191
+ }
192
+ };
193
+
194
+ export {
195
+ CodexNativeApprovals
196
+ };
@@ -2,25 +2,25 @@ import {
2
2
  SIGNED_OUT_ON_MAC_MESSAGE,
3
3
  describeSignedOutKey,
4
4
  readSignedOutDiagnosis
5
- } from "./chunk-IXNVIWNN.js";
5
+ } from "./chunk-42CVAOC5.js";
6
6
  import {
7
7
  createIo
8
8
  } from "./chunk-FO3MDD26.js";
9
9
  import {
10
10
  readKeySource
11
- } from "./chunk-M7VKFAAK.js";
11
+ } from "./chunk-TJZUQRMH.js";
12
12
  import {
13
13
  absoluteUrl,
14
14
  checkApiKey,
15
15
  describeKeyCheck,
16
16
  keyCheckFromResponse
17
- } from "./chunk-ZV56INA6.js";
17
+ } from "./chunk-TKTS3SIM.js";
18
18
  import {
19
19
  EXIT
20
20
  } from "./chunk-KZERVKTD.js";
21
21
  import {
22
22
  getBaseUrl
23
- } from "./chunk-CHD6IUNM.js";
23
+ } from "./chunk-OWQOMGBF.js";
24
24
 
25
25
  // src/cli/report-key.ts
26
26
  var { dim, yellow } = createIo();
@@ -2,7 +2,7 @@ import {
2
2
  DISCONNECT_AGENT_IDS,
3
3
  SETUP_AGENT_CHOICES,
4
4
  isApprovalMode
5
- } from "./chunk-ORLTWJ2O.js";
5
+ } from "./chunk-JNU3LV47.js";
6
6
 
7
7
  // src/cli/mode-request.ts
8
8
  var parseDuration = (value) => {
@@ -36,6 +36,7 @@ var describeMode = (mode) => {
36
36
  var npxLatestCommandFor = (platform) => `${platform === "win32" ? "npx.cmd" : "npx"} pushary@latest`;
37
37
  var NPX_LATEST_COMMAND = npxLatestCommandFor(process.platform);
38
38
  var COMMAND_LIST = [
39
+ { name: "codex", module: "pushary-codex-session", summary: "Start a managed Codex terminal session with Pushary native approvals", detail: ["Use --cwd <directory> to choose a project. Existing desktop chats are not attached."] },
39
40
  {
40
41
  name: "cowork",
41
42
  module: "pushary-cowork",