pushary 1.9.82 → 1.9.84

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 (138) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/data/SKILL.md +132 -465
  3. package/data/cowork/SKILL.md +8 -8
  4. package/data/cursor-plugin/.cursor-plugin/plugin.json +1 -1
  5. package/data/cursor-plugin/CHANGELOG.md +8 -0
  6. package/data/cursor-plugin/commands/notify-when-done.md +1 -1
  7. package/data/cursor-plugin/rules/pushary.mdc +5 -4
  8. package/data/cursor-plugin/skills/pushary/SKILL.md +131 -464
  9. package/data/vscode-plugin/.claude-plugin/plugin.json +1 -1
  10. package/data/vscode-plugin/CHANGELOG.md +8 -0
  11. package/data/vscode-plugin/commands/notify-when-done.md +1 -1
  12. package/data/vscode-plugin/skills/pushary/SKILL.md +131 -464
  13. package/dist/{api-TZO5ACOS.js → api-4WEMR2G3.js} +10 -10
  14. package/dist/bin/pushary-bell-hook.js +26 -26
  15. package/dist/bin/pushary-bell.js +10 -10
  16. package/dist/bin/pushary-claude.js +32 -31
  17. package/dist/bin/pushary-clean.js +37 -33
  18. package/dist/bin/pushary-codex-bridge.js +21 -21
  19. package/dist/bin/pushary-codex-hook.js +44 -43
  20. package/dist/bin/pushary-codex-session.js +8 -8
  21. package/dist/bin/pushary-codex.js +14 -13
  22. package/dist/bin/pushary-connect.js +27 -26
  23. package/dist/bin/pushary-cowork.js +5 -5
  24. package/dist/bin/pushary-daemon.js +70 -43
  25. package/dist/bin/pushary-disconnect.js +26 -25
  26. package/dist/bin/pushary-display-hook.js +17 -17
  27. package/dist/bin/pushary-doctor.js +42 -41
  28. package/dist/bin/pushary-elicitation-hook.js +26 -26
  29. package/dist/bin/pushary-gemini-bridge.js +15 -15
  30. package/dist/bin/pushary-gemini-hook.js +35 -34
  31. package/dist/bin/pushary-hook.js +30 -30
  32. package/dist/bin/pushary-login.js +20 -20
  33. package/dist/bin/pushary-logout.js +25 -24
  34. package/dist/bin/pushary-mcp.js +11 -11
  35. package/dist/bin/pushary-mode.js +15 -15
  36. package/dist/bin/pushary-notification-hook.js +25 -25
  37. package/dist/bin/pushary-opencode-hook.js +35 -34
  38. package/dist/bin/pushary-permission-denied-hook.js +30 -30
  39. package/dist/bin/pushary-permission-hook.js +30 -30
  40. package/dist/bin/pushary-post-hook.js +25 -25
  41. package/dist/bin/pushary-prompt-hook.js +25 -25
  42. package/dist/bin/pushary-session-end-hook.js +27 -27
  43. package/dist/bin/pushary-session-start-hook.js +35 -35
  44. package/dist/bin/pushary-setup.js +48 -47
  45. package/dist/bin/pushary-stats.js +5 -5
  46. package/dist/bin/pushary-status.js +32 -31
  47. package/dist/bin/pushary-stop-hook.js +25 -25
  48. package/dist/bin/pushary-stopfailure-hook.js +25 -25
  49. package/dist/bin/pushary-suggestions.js +15 -15
  50. package/dist/bin/pushary-transcript-register.js +21 -20
  51. package/dist/bin/pushary-transcripts.js +30 -20
  52. package/dist/bin/pushary-upgrade.js +40 -39
  53. package/dist/bin/pushary-wait.js +15 -15
  54. package/dist/bin/pushary.js +7 -7
  55. package/dist/{chunk-3BFCJZBU.js → chunk-254B34JU.js} +1 -1
  56. package/dist/{chunk-NEWGYFKC.js → chunk-2VZY5XQ5.js} +7 -7
  57. package/dist/chunk-3MWQZKIO.js +843 -0
  58. package/dist/{chunk-RL2KE6HG.js → chunk-42DNYOC4.js} +23 -21
  59. package/dist/{chunk-DS5BR4K6.js → chunk-4J3NGYHG.js} +2 -2
  60. package/dist/{chunk-2TEX3APZ.js → chunk-4OP5GWHQ.js} +5 -5
  61. package/dist/{chunk-UYTPCZ4M.js → chunk-4QGV3II7.js} +4 -4
  62. package/dist/{chunk-MHUKYYYR.js → chunk-4TMKTLQX.js} +44 -1
  63. package/dist/{chunk-O6T3CLWT.js → chunk-5Z43EFX2.js} +2 -2
  64. package/dist/{chunk-2BJZFA7X.js → chunk-64XK7DLE.js} +3 -3
  65. package/dist/{chunk-IXVMMDIG.js → chunk-6HBWJXU6.js} +3 -3
  66. package/dist/{chunk-W3VRQEL3.js → chunk-7GYDMVHV.js} +2 -2
  67. package/dist/{chunk-TO77OYLM.js → chunk-7ZYVX2KN.js} +1 -1
  68. package/dist/{chunk-LGWTITVK.js → chunk-BGMYSRMY.js} +1 -1
  69. package/dist/{chunk-C6FQ4YST.js → chunk-BS5MTP34.js} +2 -2
  70. package/dist/{chunk-LNSCANYY.js → chunk-BUS7MXEE.js} +3 -3
  71. package/dist/{chunk-FJZSJZCT.js → chunk-CIICMXVH.js} +3 -3
  72. package/dist/{chunk-GUM3WG4B.js → chunk-CSOVOAX5.js} +6 -6
  73. package/dist/{chunk-AM3H6HWG.js → chunk-CZ7C5AJT.js} +1 -1
  74. package/dist/{chunk-3V6G2GBJ.js → chunk-DNBBWBYA.js} +5 -5
  75. package/dist/{chunk-72CIQCBA.js → chunk-EKWDNEPN.js} +3 -3
  76. package/dist/{chunk-6224ENW3.js → chunk-EX33ZFJ6.js} +3 -3
  77. package/dist/{chunk-6355HHSR.js → chunk-FNC4SPOD.js} +2 -2
  78. package/dist/{chunk-CXZAKS7S.js → chunk-FRPR4OTH.js} +4 -4
  79. package/dist/{chunk-PJ26BODT.js → chunk-FSSA76HW.js} +1 -1
  80. package/dist/{chunk-RRMRP62I.js → chunk-G2GOVKBU.js} +6 -6
  81. package/dist/{chunk-TTTK44RK.js → chunk-GGGOFJBL.js} +1 -1
  82. package/dist/{chunk-OXTRF7CP.js → chunk-GWHZUHIK.js} +1 -1
  83. package/dist/{chunk-TKFDPYXS.js → chunk-H6IJOGDH.js} +4 -4
  84. package/dist/{chunk-JXE4UQFJ.js → chunk-HARHFDZM.js} +1 -1
  85. package/dist/{chunk-AXE2ZOKS.js → chunk-HLAW7HUT.js} +2 -2
  86. package/dist/{chunk-G5BYSS6C.js → chunk-HVZJ2EEI.js} +6 -6
  87. package/dist/{chunk-OIW3JAWH.js → chunk-HWT42Z4O.js} +5 -5
  88. package/dist/{chunk-WG67W43Y.js → chunk-JHWOM55B.js} +3 -1
  89. package/dist/{chunk-JNCD2ZG3.js → chunk-JJCMQRBF.js} +3 -3
  90. package/dist/{chunk-FHPSVTS5.js → chunk-JLJWVKMJ.js} +54 -835
  91. package/dist/{chunk-ONUAAXAW.js → chunk-JSZT4Q23.js} +1 -1
  92. package/dist/{chunk-UB7VUSSX.js → chunk-LUSWC2MV.js} +3 -3
  93. package/dist/{chunk-M5XNFUXN.js → chunk-OGTZ7JR7.js} +1 -1
  94. package/dist/{chunk-QJJJENT2.js → chunk-OKWHHDV5.js} +5 -5
  95. package/dist/{chunk-5LLZSX7I.js → chunk-OSEGZLCO.js} +3 -3
  96. package/dist/{chunk-UOUUTDWF.js → chunk-PHBZK4SC.js} +3 -3
  97. package/dist/{chunk-JTYZEP4Q.js → chunk-POKCUYIQ.js} +1 -1
  98. package/dist/{chunk-D3QGBMO3.js → chunk-QF6OJUWQ.js} +1 -1
  99. package/dist/{chunk-APSYBSMG.js → chunk-QG5Y5A36.js} +10 -10
  100. package/dist/{chunk-J4TQYGHU.js → chunk-QSDZE2B7.js} +5 -5
  101. package/dist/{chunk-4S5XVFDV.js → chunk-RPNT4D3K.js} +1 -1
  102. package/dist/{chunk-6L2U3TP3.js → chunk-SAPRO7FI.js} +6 -6
  103. package/dist/{chunk-ESRKYHWE.js → chunk-SBK7WGSE.js} +2 -2
  104. package/dist/{chunk-L77YWBMK.js → chunk-TD5PW2VI.js} +6 -6
  105. package/dist/{chunk-WLOVGZ7R.js → chunk-TFJQAPIA.js} +2 -2
  106. package/dist/{chunk-I4RNLK5G.js → chunk-TNPKYKXU.js} +166 -24
  107. package/dist/{chunk-CV56MBIZ.js → chunk-TSCFJJIB.js} +6 -6
  108. package/dist/{chunk-ER4UCECJ.js → chunk-TZDEHPYZ.js} +1 -1
  109. package/dist/{chunk-NPBPH7KI.js → chunk-UGELL4RB.js} +2 -2
  110. package/dist/{chunk-6WOG2SCC.js → chunk-UIDWJ24H.js} +6 -13
  111. package/dist/{chunk-N3C3D4AV.js → chunk-UWL6VVE3.js} +4 -4
  112. package/dist/{chunk-YAMNJXCD.js → chunk-V4JZHAOY.js} +17 -11
  113. package/dist/{chunk-4TR4G7A4.js → chunk-VD24OIWR.js} +1 -1
  114. package/dist/{chunk-5TMGEQAC.js → chunk-VJZGR4N3.js} +7 -5
  115. package/dist/{chunk-GUI2NYCX.js → chunk-VVWL6AB4.js} +5 -5
  116. package/dist/{chunk-OXKVR2FP.js → chunk-W2IRYDG5.js} +4 -4
  117. package/dist/{chunk-3UBYG4G7.js → chunk-WRKRZJCW.js} +10 -8
  118. package/dist/{chunk-DJILSCSU.js → chunk-YQCCJ6U5.js} +3 -3
  119. package/dist/{chunk-IEAABGST.js → chunk-YU5ACKJP.js} +4 -4
  120. package/dist/{chunk-DX45XQ36.js → chunk-Z75D2NJN.js} +3 -3
  121. package/dist/{chunk-S2DLTWFG.js → chunk-ZHL6F2KC.js} +3 -3
  122. package/dist/{chunk-X2P3O4PX.js → chunk-ZNFAF74M.js} +1 -1
  123. package/dist/{chunk-WUS2LFTM.js → chunk-ZUZ7NTWO.js} +181 -75
  124. package/dist/{chunk-Z5HLACZT.js → chunk-ZWKF7WE4.js} +4 -4
  125. package/dist/{codex-app-cards-switch-3FAIEEB2.js → codex-app-cards-switch-26N5XSSZ.js} +11 -11
  126. package/dist/{codex-desktop-launch-LHMHRVGF.js → codex-desktop-launch-UU6CPB2I.js} +1 -1
  127. package/dist/{codex-session-launch-MXUBSTL2.js → codex-session-launch-66LBR4MG.js} +18 -18
  128. package/dist/{config-MZDRETL2.js → config-ESAAVJWO.js} +3 -3
  129. package/dist/{global-upgrade-ZDENZA42.js → global-upgrade-QJONTWQI.js} +10 -10
  130. package/dist/{identity-YZRDJOGY.js → identity-IZVPSC7B.js} +4 -4
  131. package/dist/{live-capabilities-KSQDWVT6.js → live-capabilities-OEUPZX67.js} +5 -5
  132. package/dist/{paths-DYZZBVYI.js → paths-GDWS3B46.js} +3 -3
  133. package/dist/{reapply-L3AI5KJH.js → reapply-P2VOZGWQ.js} +28 -27
  134. package/dist/src/index.js +29 -29
  135. package/dist/{upgrade-lock-X7F2OFYB.js → upgrade-lock-3QABNXBZ.js} +4 -4
  136. package/dist/{usage-TKZALYXF.js → usage-KDRZPUZS.js} +3 -3
  137. package/package.json +1 -1
  138. package/dist/chunk-VHLRXIMC.js +0 -74
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  NPX_LATEST_COMMAND
3
- } from "./chunk-TTTK44RK.js";
3
+ } from "./chunk-GGGOFJBL.js";
4
4
 
5
5
  // src/config.ts
6
6
  import { readFileSync } from "fs";
@@ -1,12 +1,12 @@
1
1
  import {
2
2
  findNativeClaudeInstall
3
- } from "./chunk-TO77OYLM.js";
3
+ } from "./chunk-7ZYVX2KN.js";
4
4
  import {
5
5
  parseVersion
6
- } from "./chunk-2BJZFA7X.js";
6
+ } from "./chunk-64XK7DLE.js";
7
7
  import {
8
8
  isRecord
9
- } from "./chunk-WG67W43Y.js";
9
+ } from "./chunk-JHWOM55B.js";
10
10
 
11
11
  // src/setup/agent-probe.ts
12
12
  import { execSync } from "child_process";
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  localRepoKey,
3
3
  normalizeRepoRemote
4
- } from "./chunk-WG67W43Y.js";
4
+ } from "./chunk-JHWOM55B.js";
5
5
 
6
6
  // src/repo.ts
7
7
  import { existsSync, readFileSync, statSync } from "fs";
@@ -1,20 +1,20 @@
1
1
  import {
2
2
  openCodeConfigDirFrom
3
- } from "./chunk-CV56MBIZ.js";
3
+ } from "./chunk-TSCFJJIB.js";
4
4
  import {
5
5
  nativeClaudeInstallPath
6
- } from "./chunk-TO77OYLM.js";
6
+ } from "./chunk-7ZYVX2KN.js";
7
7
  import {
8
8
  grokHome
9
- } from "./chunk-5TMGEQAC.js";
9
+ } from "./chunk-VJZGR4N3.js";
10
10
  import {
11
11
  claudeConfigDir,
12
12
  clineDataDirectory,
13
13
  codexHomeFrom
14
- } from "./chunk-UOUUTDWF.js";
14
+ } from "./chunk-PHBZK4SC.js";
15
15
  import {
16
16
  HOOK_COMPATIBLE_PROFILES
17
- } from "./chunk-WG67W43Y.js";
17
+ } from "./chunk-JHWOM55B.js";
18
18
 
19
19
  // src/vscode-config.ts
20
20
  import { homedir } from "os";
@@ -9,20 +9,20 @@ import {
9
9
  installedCodexApp,
10
10
  readCodexAppRuntime,
11
11
  shellQuoted
12
- } from "./chunk-6355HHSR.js";
12
+ } from "./chunk-FNC4SPOD.js";
13
13
  import {
14
14
  writeHuman
15
15
  } from "./chunk-QCGNGEGE.js";
16
16
  import {
17
17
  xml
18
- } from "./chunk-6L2U3TP3.js";
18
+ } from "./chunk-SAPRO7FI.js";
19
19
  import {
20
20
  CODEX_APP_LAUNCH_AT_LOGIN_VERB,
21
21
  CODEX_APP_LAUNCH_VARIABLE
22
22
  } from "./chunk-E46NE7FB.js";
23
23
  import {
24
24
  writeFileAtomic
25
- } from "./chunk-2BJZFA7X.js";
25
+ } from "./chunk-64XK7DLE.js";
26
26
 
27
27
  // src/codex-app-cards-switch.ts
28
28
  import { execFileSync as execFileSync2 } from "child_process";
@@ -4,7 +4,7 @@ import {
4
4
  quoteIfNeeded,
5
5
  shellDoubleQuoted,
6
6
  writeFileAtomic
7
- } from "./chunk-2BJZFA7X.js";
7
+ } from "./chunk-64XK7DLE.js";
8
8
  import {
9
9
  cmdShimScriptOf
10
10
  } from "./chunk-ZVU6WMMU.js";
@@ -15,7 +15,7 @@ import {
15
15
  HOOK_LOCATOR_BINARY,
16
16
  PRETOOLUSE_GATED_TOOLS,
17
17
  withHookHoldBudget
18
- } from "./chunk-WG67W43Y.js";
18
+ } from "./chunk-JHWOM55B.js";
19
19
 
20
20
  // src/setup/paths.ts
21
21
  import { createHash as createHash2 } from "crypto";
@@ -795,7 +795,7 @@ You have Pushary MCP tools that reach the user on their phone. Use them proactiv
795
795
  - Ask once, on one channel. If you also have a built-in way to prompt the user, use that or \`ask_user\` for a given question, never both. \`ask_user\` is the one that reaches the user when they are away from this machine, so prefer it. Raising both strands the user in whichever prompt you are not waiting on.
796
796
  - Honor authorization already granted in this session. Ask before a risky or irreversible action outside that authorization, or when an enforced host policy requires it. Do not ask again for an already authorized step, and never bypass an enforced gate. When a Pushary hook result says the user approved a specific command after a denial, rerunning that exact command is the approved path and not a bypass of that gate.
797
797
  - Call \`propose_scope\` only for an unresolved or requested file boundary. A ratified scope narrows what may proceed; it does not bypass other approval rules. Do not add a plan approval merely because work spans several files, and do not propose a new scope mid-run just to widen it. Let the installed gate request any scope expansion before the edit executes. Only file paths are checked, and only on tool calls that carry one, so put a boundary that is not a path (recipients, channels, spend, systems you will not open) in \`promises\` and never in \`allowedPaths\`. Read the \`enforces\` field that comes back: an empty array means nothing in that contract is checked automatically, and you must say so rather than report that a scope is in force. \`hookSeen: false\` means no hook has reported that sessionId, so the gate will never find the contract: fix the session id rather than proceeding as if a scope were in force.
798
- - Before a task of more than two steps, break it into steps and find the forks. Settle by yourself every fork that a file, a command or a tool call can answer, then ask the rest as one numbered round with your recommended answer against each. A question in the terminal is free; a push is not. When the user is at the terminal, ask the whole round there. When the user is away, send only the fork that stops the run, and decide the rest on your recommendation.
798
+ - Before a task of more than two steps, break it into steps and find the forks. Settle by yourself every fork that a file, a command or a tool call can answer, then ask the rest as one numbered round with your recommended answer against each. A question in the terminal is free; a push is not. When the user is at the terminal, ask the whole round there. When the user is away, send only the fork that stops the run. Decide another fork on your recommendation only if it is reversible, spends nothing and stays inside the task, and list it in your final message; ask the rest once the first answer is back.
799
799
  - When you are blocked, stuck, or hit an error you cannot resolve on your own, call \`send_notification\` so the user knows, and call \`ask_user\` if you need a decision to continue.
800
800
  - 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.
801
801
  - 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.
@@ -7,7 +7,7 @@ import {
7
7
  recordSealerFor,
8
8
  uploadTranscriptRecords,
9
9
  wrapSessionKey
10
- } from "./chunk-W3VRQEL3.js";
10
+ } from "./chunk-7GYDMVHV.js";
11
11
 
12
12
  // src/live-session.ts
13
13
  var MAX_TRANSCRIPT_QUEUE = 100;
@@ -3,7 +3,7 @@ import {
3
3
  } from "./chunk-KZERVKTD.js";
4
4
  import {
5
5
  renderCommandHelp
6
- } from "./chunk-TTTK44RK.js";
6
+ } from "./chunk-GGGOFJBL.js";
7
7
  import {
8
8
  getPackageVersion
9
9
  } from "./chunk-ZVU6WMMU.js";
@@ -1,13 +1,13 @@
1
1
  import {
2
2
  ensureCodexTrustsPusharyHooks
3
- } from "./chunk-O6T3CLWT.js";
3
+ } from "./chunk-5Z43EFX2.js";
4
4
  import {
5
5
  hermesReceiptPaths,
6
6
  hermesSetupPredatesThePluginFolder,
7
7
  installHermesPlugin,
8
8
  refreshHermesPluginFiles,
9
9
  shippedHermesPlugin
10
- } from "./chunk-I4RNLK5G.js";
10
+ } from "./chunk-TNPKYKXU.js";
11
11
  import {
12
12
  fxIgnoresInstructionsFile,
13
13
  fxInstructionsBody,
@@ -16,7 +16,7 @@ import {
16
16
  macAppFxBridge,
17
17
  refreshClineKey,
18
18
  switchFxHttpEntryToBridge
19
- } from "./chunk-2TEX3APZ.js";
19
+ } from "./chunk-4OP5GWHQ.js";
20
20
  import {
21
21
  OPENCODE_HOOK_BINARY,
22
22
  agentHookOwner,
@@ -28,16 +28,16 @@ import {
28
28
  openCodePluginDir,
29
29
  openCodeWired,
30
30
  withOpenCodeAskRules
31
- } from "./chunk-CV56MBIZ.js";
31
+ } from "./chunk-TSCFJJIB.js";
32
32
  import {
33
33
  detectClaudeVersion
34
- } from "./chunk-UB7VUSSX.js";
34
+ } from "./chunk-LUSWC2MV.js";
35
35
  import {
36
36
  addCompatibleHooks,
37
37
  compatibleConfigFile,
38
38
  compatibleInstructionsFile,
39
39
  hasCompatibleHooks
40
- } from "./chunk-5TMGEQAC.js";
40
+ } from "./chunk-VJZGR4N3.js";
41
41
  import {
42
42
  GEMINI_HOOK_BINARY,
43
43
  addGeminiHooks,
@@ -49,7 +49,7 @@ import {
49
49
  geminiWired,
50
50
  hasGeminiHooks,
51
51
  planHooks
52
- } from "./chunk-IEAABGST.js";
52
+ } from "./chunk-YU5ACKJP.js";
53
53
  import {
54
54
  CLAUDE_HOOK_BINARY,
55
55
  CLAUDE_HOOK_EVENTS,
@@ -95,7 +95,7 @@ import {
95
95
  renderAgentInstructions,
96
96
  supportedClaudeEvents,
97
97
  vscodePluginMcp
98
- } from "./chunk-UOUUTDWF.js";
98
+ } from "./chunk-PHBZK4SC.js";
99
99
  import {
100
100
  formatVersion,
101
101
  hookCommandsUseBareNames,
@@ -104,12 +104,12 @@ import {
104
104
  resolveGlobalBinary,
105
105
  writeFileAtomic,
106
106
  writeJsonAtomic
107
- } from "./chunk-2BJZFA7X.js";
107
+ } from "./chunk-64XK7DLE.js";
108
108
  import {
109
109
  HOOK_COMPATIBLE_PROFILES,
110
110
  isCompatibleAgentType,
111
111
  isRecord
112
- } from "./chunk-WG67W43Y.js";
112
+ } from "./chunk-JHWOM55B.js";
113
113
 
114
114
  // src/reapply.ts
115
115
  import { dirname, join } from "path";
@@ -1,19 +1,19 @@
1
1
  import {
2
2
  macAppBridgeAvailable
3
- } from "./chunk-CV56MBIZ.js";
3
+ } from "./chunk-TSCFJJIB.js";
4
4
  import {
5
5
  keyPrefix
6
- } from "./chunk-CXZAKS7S.js";
6
+ } from "./chunk-FRPR4OTH.js";
7
7
  import {
8
8
  bridgeAgentKey
9
- } from "./chunk-UOUUTDWF.js";
9
+ } from "./chunk-PHBZK4SC.js";
10
10
  import {
11
11
  readJsonSafe
12
- } from "./chunk-2BJZFA7X.js";
12
+ } from "./chunk-64XK7DLE.js";
13
13
  import {
14
14
  isRecord,
15
15
  isValidApiKey
16
- } from "./chunk-WG67W43Y.js";
16
+ } from "./chunk-JHWOM55B.js";
17
17
 
18
18
  // src/shared-mcp-key.ts
19
19
  var asRecord = (value) => value !== null && typeof value === "object" && !Array.isArray(value) ? value : void 0;
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  guardBinary
3
- } from "./chunk-UOUUTDWF.js";
3
+ } from "./chunk-PHBZK4SC.js";
4
4
 
5
5
  // src/bell/install.ts
6
6
  import { join } from "path";
@@ -4,23 +4,23 @@ import {
4
4
  } from "./chunk-XHKBHWLX.js";
5
5
  import {
6
6
  readControlMode
7
- } from "./chunk-CXZAKS7S.js";
7
+ } from "./chunk-FRPR4OTH.js";
8
8
  import {
9
9
  getMachineId
10
- } from "./chunk-YAMNJXCD.js";
10
+ } from "./chunk-V4JZHAOY.js";
11
11
  import {
12
12
  pusharyDir
13
- } from "./chunk-UOUUTDWF.js";
13
+ } from "./chunk-PHBZK4SC.js";
14
14
  import {
15
15
  readJsonSafe,
16
16
  resolveGlobalBinary,
17
17
  writeFileAtomic,
18
18
  writeJsonAtomic,
19
19
  writeJsonAtomicDurable
20
- } from "./chunk-2BJZFA7X.js";
20
+ } from "./chunk-64XK7DLE.js";
21
21
  import {
22
22
  getApiKey
23
- } from "./chunk-ONUAAXAW.js";
23
+ } from "./chunk-JSZT4Q23.js";
24
24
  import {
25
25
  CLI_PACKAGE_NAMES,
26
26
  cmdShimScriptOf,
@@ -30,7 +30,7 @@ import {
30
30
  } from "./chunk-ZVU6WMMU.js";
31
31
  import {
32
32
  isRecord
33
- } from "./chunk-WG67W43Y.js";
33
+ } from "./chunk-JHWOM55B.js";
34
34
 
35
35
  // src/daemon-lifecycle.ts
36
36
  import { dirname, join, win32 as win32Path } from "path";
@@ -1,11 +1,11 @@
1
1
  import {
2
2
  autoUpdateLock
3
- } from "./chunk-UOUUTDWF.js";
3
+ } from "./chunk-PHBZK4SC.js";
4
4
  import {
5
5
  installPackageGlobally,
6
6
  npmErrorMessage,
7
7
  writeJsonAtomic
8
- } from "./chunk-2BJZFA7X.js";
8
+ } from "./chunk-64XK7DLE.js";
9
9
 
10
10
  // src/upgrade-lock.ts
11
11
  import { fork } from "child_process";
@@ -3,10 +3,10 @@ import {
3
3
  forkFinishWithUpgradeLock,
4
4
  installWithUpgradeLock,
5
5
  releaseAutoUpdateLock
6
- } from "./chunk-ESRKYHWE.js";
6
+ } from "./chunk-SBK7WGSE.js";
7
7
  import {
8
8
  phoneStartIsRequired
9
- } from "./chunk-UYTPCZ4M.js";
9
+ } from "./chunk-4QGV3II7.js";
10
10
  import {
11
11
  describeAutoUpdateCheck,
12
12
  globalInstallLocation,
@@ -18,25 +18,25 @@ import {
18
18
  readAutoUpdateState,
19
19
  updateAutoUpdateState,
20
20
  upgradeEntryFor
21
- } from "./chunk-S2DLTWFG.js";
21
+ } from "./chunk-ZHL6F2KC.js";
22
22
  import {
23
23
  autoUpdateLock,
24
24
  autoUpdateLog,
25
25
  autoUpdateState
26
- } from "./chunk-UOUUTDWF.js";
26
+ } from "./chunk-PHBZK4SC.js";
27
27
  import {
28
28
  execNpm,
29
29
  fetchLatestPublishedVersion,
30
30
  npmErrorMessage,
31
31
  readGlobalCliInstallAt,
32
32
  readGlobalPrefix
33
- } from "./chunk-2BJZFA7X.js";
33
+ } from "./chunk-64XK7DLE.js";
34
34
  import {
35
35
  EXIT
36
36
  } from "./chunk-KZERVKTD.js";
37
37
  import {
38
38
  NPX_LATEST_COMMAND
39
- } from "./chunk-TTTK44RK.js";
39
+ } from "./chunk-GGGOFJBL.js";
40
40
  import {
41
41
  commandsPackage,
42
42
  globalUpgradeEntry,
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  findNativeClaudeInstall
3
- } from "./chunk-TO77OYLM.js";
3
+ } from "./chunk-7ZYVX2KN.js";
4
4
  import {
5
5
  isFile
6
- } from "./chunk-2BJZFA7X.js";
6
+ } from "./chunk-64XK7DLE.js";
7
7
 
8
8
  // src/wrapper/claudeBinary.ts
9
9
  import { homedir } from "os";
@@ -5,19 +5,19 @@ import {
5
5
  hermesAppWiringRecord,
6
6
  hermesConfigReceipt,
7
7
  hermesSetupReceipt
8
- } from "./chunk-UOUUTDWF.js";
8
+ } from "./chunk-PHBZK4SC.js";
9
9
  import {
10
10
  readJsonSafe,
11
11
  resolvePathBinary,
12
12
  writeJsonAtomic
13
- } from "./chunk-2BJZFA7X.js";
13
+ } from "./chunk-64XK7DLE.js";
14
14
  import {
15
15
  isNewerVersion,
16
16
  isReleaseVersion
17
17
  } from "./chunk-ZVU6WMMU.js";
18
18
  import {
19
19
  isRecord
20
- } from "./chunk-WG67W43Y.js";
20
+ } from "./chunk-JHWOM55B.js";
21
21
 
22
22
  // src/hermes/plugin-payload.ts
23
23
  import { createHash } from "crypto";
@@ -571,11 +571,14 @@ def _configured_key():
571
571
  return _key_in_pushary_config()
572
572
 
573
573
 
574
+ def pushary_config_path():
575
+ configured = os.environ.get("PUSHARY_CONFIG_FILE")
576
+ return Path(configured).expanduser() if configured else Path.home() / ".pushary" / "config.json"
577
+
578
+
574
579
  def _key_in_pushary_config():
575
- path = Path(os.environ.get("PUSHARY_CONFIG_FILE", "")).expanduser() \\
576
- if os.environ.get("PUSHARY_CONFIG_FILE") else Path.home() / ".pushary" / "config.json"
577
580
  try:
578
- value = json.loads(path.read_text(encoding="utf-8")).get("apiKey")
581
+ value = json.loads(pushary_config_path().read_text(encoding="utf-8")).get("apiKey")
579
582
  return value if isinstance(value, str) and value else None
580
583
  except Exception:
581
584
  return None
@@ -951,6 +954,7 @@ import threading
951
954
  import time
952
955
  from datetime import datetime, timezone
953
956
  from pathlib import Path
957
+ from typing import NamedTuple
954
958
 
955
959
  from . import api
956
960
 
@@ -958,8 +962,26 @@ logger = logging.getLogger(__name__)
958
962
  MAX_MESSAGES = 5000
959
963
  MAX_SOURCE_BYTES = 20_000_000
960
964
  POLL_SECONDS = 5
965
+ SNAPSHOT_REFRESH_SECONDS = 60
966
+ AUTHORIZATION_LIFETIME_SECONDS = 60
967
+ SNAPSHOT_RETENTION_SECONDS = 48 * 60 * 60
968
+ SNAPSHOT_PRUNE_INTERVAL_SECONDS = 60 * 60
969
+ SOURCE_UNAVAILABLE = "source_unavailable"
970
+ HISTORY_NOT_ALLOWED_WARNING = ("[pushary] Hermes session history is not saved on this computer; it needs "
971
+ "\`npx pushary@latest setup\` with the key Hermes uses, or Session history turned on in the Pushary Mac app")
972
+
973
+
974
+ class _Authorization(NamedTuple):
975
+ inputs: tuple[object, ...]
976
+ expires_at: float
977
+ command: list[str] | None
978
+
979
+
961
980
  _workers: dict[tuple[str, str], tuple[threading.Event, threading.Thread]] = {}
981
+ _authorizations: dict[str, _Authorization] = {}
962
982
  _lock = threading.Lock()
983
+ _next_prune_at = 0.0
984
+ _history_not_allowed_reported = False
963
985
 
964
986
 
965
987
  def _text_blocks(content: object) -> list[dict[str, object]]:
@@ -1088,8 +1110,7 @@ def _registration_command(key: str) -> list[str] | None:
1088
1110
  command = [helper, "hermes"]
1089
1111
  result = subprocess.run([helper, "authorize-export"],
1090
1112
  input=json.dumps({"apiKey": key, "baseUrl": api.BASE_URL}), text=True,
1091
- stdout=subprocess.PIPE, stderr=subprocess.DEVNULL, timeout=5, check=False,
1092
- env={**os.environ, "PUSHARY_API_KEY": key, "PUSHARY_BASE_URL": api.BASE_URL})
1113
+ stdout=subprocess.PIPE, stderr=subprocess.DEVNULL, timeout=5, check=False)
1093
1114
  if result.returncode != 0:
1094
1115
  return None
1095
1116
  authorization = json.loads(result.stdout)
@@ -1100,27 +1121,146 @@ def _registration_command(key: str) -> list[str] | None:
1100
1121
  return None
1101
1122
 
1102
1123
 
1124
+ def _file_state(path: Path | None) -> tuple[int, ...] | None:
1125
+ if path is None:
1126
+ return None
1127
+ try:
1128
+ info = os.lstat(path)
1129
+ except OSError:
1130
+ return None
1131
+ return (info.st_ino, info.st_mode, info.st_nlink, info.st_uid, info.st_size, info.st_mtime_ns, info.st_ctime_ns)
1132
+
1133
+
1134
+ def _authorization_inputs(key: str) -> tuple[object, ...]:
1135
+ root = Path.home() / ".pushary"
1136
+ helper = shutil.which("pushary-transcript-register")
1137
+ sources = (root / "native-transcript-consent.json", root / "bridge-auth", root / "bridge-auth" / "agent-key.json",
1138
+ root / "machine-id", root / "bin" / "pushary-bridge", api.pushary_config_path(),
1139
+ Path(helper) if helper else None)
1140
+ return (key, api.BASE_URL, os.environ.get("PUSHARY_TRANSCRIPTS"), helper, *(_file_state(source) for source in sources))
1141
+
1142
+
1143
+ def _authorized_command(key: str) -> list[str] | None:
1144
+ inputs = _authorization_inputs(key)
1145
+ now = time.monotonic()
1146
+ with _lock:
1147
+ cached = _authorizations.get(key)
1148
+ if cached is not None and cached.inputs == inputs and now < cached.expires_at:
1149
+ return cached.command
1150
+ command = _registration_command(key)
1151
+ with _lock:
1152
+ _authorizations[key] = _Authorization(inputs, now + AUTHORIZATION_LIFETIME_SECONDS, command)
1153
+ return command
1154
+
1155
+
1156
+ def _report_history_not_allowed_once() -> None:
1157
+ global _history_not_allowed_reported
1158
+ with _lock:
1159
+ if _history_not_allowed_reported:
1160
+ return
1161
+ _history_not_allowed_reported = True
1162
+ logger.warning(HISTORY_NOT_ALLOWED_WARNING)
1163
+
1164
+
1165
+ def _history_root() -> Path:
1166
+ return Path.home() / ".pushary" / "hermes-history"
1167
+
1168
+
1169
+ def _snapshot_expired(path: Path, now: float) -> bool:
1170
+ return now - path.lstat().st_mtime > SNAPSHOT_RETENTION_SECONDS
1171
+
1172
+
1173
+ def _prune_expired_snapshots(root: Path, now: float) -> None:
1174
+ try:
1175
+ bindings = [entry for entry in root.iterdir() if not entry.is_symlink() and entry.is_dir()]
1176
+ except OSError:
1177
+ return
1178
+ for binding in bindings:
1179
+ try:
1180
+ snapshots = list(binding.iterdir())
1181
+ except OSError:
1182
+ continue
1183
+ for snapshot in snapshots:
1184
+ try:
1185
+ if _snapshot_expired(snapshot, now):
1186
+ snapshot.unlink()
1187
+ except OSError:
1188
+ continue
1189
+
1190
+
1191
+ def _prune_expired_snapshots_periodically() -> None:
1192
+ global _next_prune_at
1193
+ now = time.monotonic()
1194
+ with _lock:
1195
+ if now < _next_prune_at:
1196
+ return
1197
+ _next_prune_at = now + SNAPSHOT_PRUNE_INTERVAL_SECONDS
1198
+ _prune_expired_snapshots(_history_root(), time.time())
1199
+
1200
+
1201
+ def _source_state(database_path: Path) -> tuple[object, ...]:
1202
+ return (_file_state(database_path), _file_state(database_path.with_name(database_path.name + "-wal")))
1203
+
1204
+
1205
+ def _snapshot_refresh_due(written_at: float | None, now: float) -> bool:
1206
+ return written_at is None or not 0 <= now - written_at < SNAPSHOT_REFRESH_SECONDS
1207
+
1208
+
1209
+ def _is_permanent_source_failure(failure: Exception) -> bool:
1210
+ return isinstance(failure, ValueError)
1211
+
1212
+
1213
+ def _snapshot_digest(messages: list[dict[str, object]], error: str | None, completed: bool) -> str:
1214
+ encoded = json.dumps([messages, error, completed], ensure_ascii=True, allow_nan=False).encode("utf-8")
1215
+ return hashlib.sha256(encoded).hexdigest()
1216
+
1217
+
1218
+ def _write_changed_snapshot(path: Path, session_id: str, messages: list[dict[str, object]], error: str | None,
1219
+ completed: bool, written: str | None, refresh_due: bool) -> str | None:
1220
+ digest = _snapshot_digest(messages, error, completed)
1221
+ if not refresh_due and digest == written:
1222
+ return None
1223
+ write_snapshot(path, session_id, messages, error=error, completed=completed)
1224
+ return digest
1225
+
1226
+
1103
1227
  def _export_loop(database_path: Path, session_id: str, key: str, command: list[str], stop: threading.Event) -> None:
1104
1228
  binding = hashlib.sha256((api.BASE_URL.rstrip("/") + "\\0" + key + "\\0" + str(database_path)).encode()).hexdigest()
1105
- path = Path.home() / ".pushary" / "hermes-history" / binding / f"{session_id}.json"
1229
+ path = _history_root() / binding / f"{session_id}.json"
1106
1230
  next_registration = 0
1231
+ written_at = None
1232
+ source_state = None
1233
+ written = None
1107
1234
  warned = False
1108
1235
  revoked = False
1109
1236
  try:
1110
1237
  while True:
1111
- if api._get_api_key() != key or _registration_command(key) != command:
1238
+ if api._get_api_key() != key or _authorized_command(key) != command:
1112
1239
  revoked = True
1113
1240
  break
1114
1241
  ending = stop.is_set()
1115
- try:
1116
- messages = read_display_history(database_path, session_id)
1117
- write_snapshot(path, session_id, messages, completed=ending)
1118
- warned = False
1119
- except Exception:
1120
- if not warned:
1242
+ now = time.time()
1243
+ refresh_due = ending or _snapshot_refresh_due(written_at, now)
1244
+ current_state = _source_state(database_path)
1245
+ if refresh_due or current_state != source_state:
1246
+ source_state = current_state
1247
+ error = None
1248
+ try:
1249
+ messages = read_display_history(database_path, session_id)
1250
+ except Exception as failure:
1251
+ messages, error = [], SOURCE_UNAVAILABLE
1252
+ if not _is_permanent_source_failure(failure):
1253
+ source_state = None
1254
+ try:
1255
+ digest = _write_changed_snapshot(path, session_id, messages, error, ending and error is None, written, refresh_due)
1256
+ except ValueError:
1257
+ messages, error = [], SOURCE_UNAVAILABLE
1258
+ digest = _write_changed_snapshot(path, session_id, messages, error, False, written, refresh_due)
1259
+ if digest is not None:
1260
+ written, written_at = digest, now
1261
+ if error is not None and not warned:
1121
1262
  logger.warning("[pushary] Hermes history source is unavailable; existing phone history is preserved")
1122
- warned = True
1123
- write_snapshot(path, session_id, [], error="source_unavailable")
1263
+ warned = error is not None
1124
1264
  if ending or time.monotonic() >= next_registration:
1125
1265
  payload = {"session_id": session_id, "cwd": str(database_path.parent), "transcript_path": str(path),
1126
1266
  "hook_event_name": "SessionEnd" if ending else "SessionStart"}
@@ -1148,16 +1288,18 @@ def start_history(session_id: object, parent_session_id: object = None) -> None:
1148
1288
  try:
1149
1289
  from hermes_constants import get_hermes_home
1150
1290
 
1151
- key = api._get_api_key()
1152
- command = _registration_command(key)
1153
- if command is None:
1154
- return
1291
+ _prune_expired_snapshots_periodically()
1155
1292
  database_path = (get_hermes_home() / "state.db").resolve()
1156
1293
  identity = (str(database_path), session_id)
1157
1294
  with _lock:
1158
1295
  if identity in _workers or len(_workers) >= 32:
1159
1296
  return
1160
- stop = threading.Event()
1297
+ key = api._get_api_key()
1298
+ command = _authorized_command(key)
1299
+ if command is None:
1300
+ _report_history_not_allowed_once()
1301
+ return
1302
+ stop = threading.Event()
1161
1303
  context = contextvars.copy_context()
1162
1304
  thread = threading.Thread(target=context.run, args=(_export_loop, database_path, session_id, key, command, stop),
1163
1305
  name="pushary-history", daemon=True)
@@ -1321,7 +1463,7 @@ def record_plugin_load(approval_transport_registered, home=None, pid=None, now=N
1321
1463
  logger.debug("[pushary] could not record the plugin load: %s", exc)
1322
1464
  return None
1323
1465
  ` },
1324
- { path: "plugin.yaml", contents: "name: pushary\nversion: 0.9.0\ndescription: Push notifications and human-in-the-loop for Hermes Agent via Pushary\nauthor: RalphNex OU\nrequires_env:\n - name: PUSHARY_API_KEY\n description: Your Pushary API key\n url: https://pushary.com/sign-up?from=hermes\n secret: true\nprovides_tools:\n - pushary_remind\n - pushary_notify\n - pushary_ask\n - pushary_wait\n - pushary_cancel\n - pushary_propose_scope\n - pushary_enroll\n - pushary_ask_end_user\nprovides_hooks:\n - pre_llm_call\n - pre_tool_call\n - post_tool_call\n - on_session_start\n - on_session_end\n - on_session_finalize\nprovides_approval_transports:\n - pushary\n" },
1466
+ { path: "plugin.yaml", contents: "name: pushary\nversion: 0.9.1\ndescription: Push notifications and human-in-the-loop for Hermes Agent via Pushary\nauthor: RalphNex OU\nrequires_env:\n - name: PUSHARY_API_KEY\n description: Your Pushary API key\n url: https://pushary.com/sign-up?from=hermes\n secret: true\nprovides_tools:\n - pushary_remind\n - pushary_notify\n - pushary_ask\n - pushary_wait\n - pushary_cancel\n - pushary_propose_scope\n - pushary_enroll\n - pushary_ask_end_user\nprovides_hooks:\n - pre_llm_call\n - pre_tool_call\n - post_tool_call\n - on_session_start\n - on_session_end\n - on_session_finalize\nprovides_approval_transports:\n - pushary\n" },
1325
1467
  { path: "schemas.py", contents: `PUSHARY_REMIND = {
1326
1468
  "name": "pushary_remind",
1327
1469
  "description": "Schedule a one-time personal reminder on Pushary, even after Hermes exits. Use body plus in_minutes or at (ISO timestamp with explicit UTC offset, within 30 days). Never guess the user's timezone. Confirm the returned time. No arguments lists pending reminders; cancel_reminder_id cancels a scheduled one. Use Hermes cron for agent work; use this for reminding the operator.",