@pushary/agent-hooks 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.
- package/CHANGELOG.md +12 -0
- package/data/SKILL.md +132 -465
- package/data/cowork/SKILL.md +8 -8
- package/data/cursor-plugin/.cursor-plugin/plugin.json +1 -1
- package/data/cursor-plugin/CHANGELOG.md +8 -0
- package/data/cursor-plugin/commands/notify-when-done.md +1 -1
- package/data/cursor-plugin/rules/pushary.mdc +5 -4
- package/data/cursor-plugin/skills/pushary/SKILL.md +131 -464
- package/data/vscode-plugin/.claude-plugin/plugin.json +1 -1
- package/data/vscode-plugin/CHANGELOG.md +8 -0
- package/data/vscode-plugin/commands/notify-when-done.md +1 -1
- package/data/vscode-plugin/skills/pushary/SKILL.md +131 -464
- package/dist/{api-TZO5ACOS.js → api-4WEMR2G3.js} +10 -10
- package/dist/bin/pushary-bell-hook.js +26 -26
- package/dist/bin/pushary-bell.js +10 -10
- package/dist/bin/pushary-claude.js +32 -31
- package/dist/bin/pushary-clean.js +37 -33
- package/dist/bin/pushary-codex-bridge.js +21 -21
- package/dist/bin/pushary-codex-hook.js +44 -43
- package/dist/bin/pushary-codex-session.js +8 -8
- package/dist/bin/pushary-codex.js +14 -13
- package/dist/bin/pushary-connect.js +27 -26
- package/dist/bin/pushary-cowork.js +5 -5
- package/dist/bin/pushary-daemon.js +70 -43
- package/dist/bin/pushary-disconnect.js +26 -25
- package/dist/bin/pushary-display-hook.js +17 -17
- package/dist/bin/pushary-doctor.js +42 -41
- package/dist/bin/pushary-elicitation-hook.js +26 -26
- package/dist/bin/pushary-gemini-bridge.js +15 -15
- package/dist/bin/pushary-gemini-hook.js +35 -34
- package/dist/bin/pushary-hook.js +30 -30
- package/dist/bin/pushary-login.js +20 -20
- package/dist/bin/pushary-logout.js +25 -24
- package/dist/bin/pushary-mcp.js +11 -11
- package/dist/bin/pushary-mode.js +15 -15
- package/dist/bin/pushary-notification-hook.js +25 -25
- package/dist/bin/pushary-opencode-hook.js +35 -34
- package/dist/bin/pushary-permission-denied-hook.js +30 -30
- package/dist/bin/pushary-permission-hook.js +30 -30
- package/dist/bin/pushary-post-hook.js +25 -25
- package/dist/bin/pushary-prompt-hook.js +25 -25
- package/dist/bin/pushary-session-end-hook.js +27 -27
- package/dist/bin/pushary-session-start-hook.js +35 -35
- package/dist/bin/pushary-setup.js +48 -47
- package/dist/bin/pushary-stats.js +5 -5
- package/dist/bin/pushary-status.js +32 -31
- package/dist/bin/pushary-stop-hook.js +25 -25
- package/dist/bin/pushary-stopfailure-hook.js +25 -25
- package/dist/bin/pushary-suggestions.js +15 -15
- package/dist/bin/pushary-transcript-register.js +21 -20
- package/dist/bin/pushary-transcripts.js +30 -20
- package/dist/bin/pushary-upgrade.js +40 -39
- package/dist/bin/pushary-wait.js +15 -15
- package/dist/bin/pushary.js +7 -7
- package/dist/{chunk-3BFCJZBU.js → chunk-254B34JU.js} +1 -1
- package/dist/{chunk-NEWGYFKC.js → chunk-2VZY5XQ5.js} +7 -7
- package/dist/chunk-3MWQZKIO.js +843 -0
- package/dist/{chunk-RL2KE6HG.js → chunk-42DNYOC4.js} +23 -21
- package/dist/{chunk-DS5BR4K6.js → chunk-4J3NGYHG.js} +2 -2
- package/dist/{chunk-2TEX3APZ.js → chunk-4OP5GWHQ.js} +5 -5
- package/dist/{chunk-UYTPCZ4M.js → chunk-4QGV3II7.js} +4 -4
- package/dist/{chunk-MHUKYYYR.js → chunk-4TMKTLQX.js} +44 -1
- package/dist/{chunk-O6T3CLWT.js → chunk-5Z43EFX2.js} +2 -2
- package/dist/{chunk-2BJZFA7X.js → chunk-64XK7DLE.js} +3 -3
- package/dist/{chunk-IXVMMDIG.js → chunk-6HBWJXU6.js} +3 -3
- package/dist/{chunk-W3VRQEL3.js → chunk-7GYDMVHV.js} +2 -2
- package/dist/{chunk-TO77OYLM.js → chunk-7ZYVX2KN.js} +1 -1
- package/dist/{chunk-LGWTITVK.js → chunk-BGMYSRMY.js} +1 -1
- package/dist/{chunk-C6FQ4YST.js → chunk-BS5MTP34.js} +2 -2
- package/dist/{chunk-LNSCANYY.js → chunk-BUS7MXEE.js} +3 -3
- package/dist/{chunk-FJZSJZCT.js → chunk-CIICMXVH.js} +3 -3
- package/dist/{chunk-GUM3WG4B.js → chunk-CSOVOAX5.js} +6 -6
- package/dist/{chunk-AM3H6HWG.js → chunk-CZ7C5AJT.js} +1 -1
- package/dist/{chunk-3V6G2GBJ.js → chunk-DNBBWBYA.js} +5 -5
- package/dist/{chunk-72CIQCBA.js → chunk-EKWDNEPN.js} +3 -3
- package/dist/{chunk-6224ENW3.js → chunk-EX33ZFJ6.js} +3 -3
- package/dist/{chunk-6355HHSR.js → chunk-FNC4SPOD.js} +2 -2
- package/dist/{chunk-CXZAKS7S.js → chunk-FRPR4OTH.js} +4 -4
- package/dist/{chunk-PJ26BODT.js → chunk-FSSA76HW.js} +1 -1
- package/dist/{chunk-RRMRP62I.js → chunk-G2GOVKBU.js} +6 -6
- package/dist/{chunk-TTTK44RK.js → chunk-GGGOFJBL.js} +1 -1
- package/dist/{chunk-OXTRF7CP.js → chunk-GWHZUHIK.js} +1 -1
- package/dist/{chunk-TKFDPYXS.js → chunk-H6IJOGDH.js} +4 -4
- package/dist/{chunk-JXE4UQFJ.js → chunk-HARHFDZM.js} +1 -1
- package/dist/{chunk-AXE2ZOKS.js → chunk-HLAW7HUT.js} +2 -2
- package/dist/{chunk-G5BYSS6C.js → chunk-HVZJ2EEI.js} +6 -6
- package/dist/{chunk-OIW3JAWH.js → chunk-HWT42Z4O.js} +5 -5
- package/dist/{chunk-WG67W43Y.js → chunk-JHWOM55B.js} +3 -1
- package/dist/{chunk-JNCD2ZG3.js → chunk-JJCMQRBF.js} +3 -3
- package/dist/{chunk-FHPSVTS5.js → chunk-JLJWVKMJ.js} +54 -835
- package/dist/{chunk-ONUAAXAW.js → chunk-JSZT4Q23.js} +1 -1
- package/dist/{chunk-UB7VUSSX.js → chunk-LUSWC2MV.js} +3 -3
- package/dist/{chunk-M5XNFUXN.js → chunk-OGTZ7JR7.js} +1 -1
- package/dist/{chunk-QJJJENT2.js → chunk-OKWHHDV5.js} +5 -5
- package/dist/{chunk-5LLZSX7I.js → chunk-OSEGZLCO.js} +3 -3
- package/dist/{chunk-UOUUTDWF.js → chunk-PHBZK4SC.js} +3 -3
- package/dist/{chunk-JTYZEP4Q.js → chunk-POKCUYIQ.js} +1 -1
- package/dist/{chunk-D3QGBMO3.js → chunk-QF6OJUWQ.js} +1 -1
- package/dist/{chunk-APSYBSMG.js → chunk-QG5Y5A36.js} +10 -10
- package/dist/{chunk-J4TQYGHU.js → chunk-QSDZE2B7.js} +5 -5
- package/dist/{chunk-4S5XVFDV.js → chunk-RPNT4D3K.js} +1 -1
- package/dist/{chunk-6L2U3TP3.js → chunk-SAPRO7FI.js} +6 -6
- package/dist/{chunk-ESRKYHWE.js → chunk-SBK7WGSE.js} +2 -2
- package/dist/{chunk-L77YWBMK.js → chunk-TD5PW2VI.js} +6 -6
- package/dist/{chunk-WLOVGZ7R.js → chunk-TFJQAPIA.js} +2 -2
- package/dist/{chunk-I4RNLK5G.js → chunk-TNPKYKXU.js} +166 -24
- package/dist/{chunk-CV56MBIZ.js → chunk-TSCFJJIB.js} +6 -6
- package/dist/{chunk-ER4UCECJ.js → chunk-TZDEHPYZ.js} +1 -1
- package/dist/{chunk-NPBPH7KI.js → chunk-UGELL4RB.js} +2 -2
- package/dist/{chunk-6WOG2SCC.js → chunk-UIDWJ24H.js} +6 -13
- package/dist/{chunk-N3C3D4AV.js → chunk-UWL6VVE3.js} +4 -4
- package/dist/{chunk-YAMNJXCD.js → chunk-V4JZHAOY.js} +17 -11
- package/dist/{chunk-4TR4G7A4.js → chunk-VD24OIWR.js} +1 -1
- package/dist/{chunk-5TMGEQAC.js → chunk-VJZGR4N3.js} +7 -5
- package/dist/{chunk-GUI2NYCX.js → chunk-VVWL6AB4.js} +5 -5
- package/dist/{chunk-OXKVR2FP.js → chunk-W2IRYDG5.js} +4 -4
- package/dist/{chunk-3UBYG4G7.js → chunk-WRKRZJCW.js} +10 -8
- package/dist/{chunk-DJILSCSU.js → chunk-YQCCJ6U5.js} +3 -3
- package/dist/{chunk-IEAABGST.js → chunk-YU5ACKJP.js} +4 -4
- package/dist/{chunk-DX45XQ36.js → chunk-Z75D2NJN.js} +3 -3
- package/dist/{chunk-S2DLTWFG.js → chunk-ZHL6F2KC.js} +3 -3
- package/dist/{chunk-X2P3O4PX.js → chunk-ZNFAF74M.js} +1 -1
- package/dist/{chunk-WUS2LFTM.js → chunk-ZUZ7NTWO.js} +181 -75
- package/dist/{chunk-Z5HLACZT.js → chunk-ZWKF7WE4.js} +4 -4
- package/dist/{codex-app-cards-switch-3FAIEEB2.js → codex-app-cards-switch-26N5XSSZ.js} +11 -11
- package/dist/{codex-desktop-launch-LHMHRVGF.js → codex-desktop-launch-UU6CPB2I.js} +1 -1
- package/dist/{codex-session-launch-MXUBSTL2.js → codex-session-launch-66LBR4MG.js} +18 -18
- package/dist/{config-MZDRETL2.js → config-ESAAVJWO.js} +3 -3
- package/dist/{global-upgrade-ZDENZA42.js → global-upgrade-QJONTWQI.js} +10 -10
- package/dist/{identity-YZRDJOGY.js → identity-IZVPSC7B.js} +4 -4
- package/dist/{live-capabilities-KSQDWVT6.js → live-capabilities-OEUPZX67.js} +5 -5
- package/dist/{paths-DYZZBVYI.js → paths-GDWS3B46.js} +3 -3
- package/dist/{reapply-L3AI5KJH.js → reapply-P2VOZGWQ.js} +28 -27
- package/dist/src/index.js +29 -29
- package/dist/{upgrade-lock-X7F2OFYB.js → upgrade-lock-3QABNXBZ.js} +4 -4
- package/dist/{usage-TKZALYXF.js → usage-KDRZPUZS.js} +3 -3
- package/package.json +1 -1
- package/dist/chunk-VHLRXIMC.js +0 -74
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
2
|
findNativeClaudeInstall
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-7ZYVX2KN.js";
|
|
4
4
|
import {
|
|
5
5
|
parseVersion
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-64XK7DLE.js";
|
|
7
7
|
import {
|
|
8
8
|
isRecord
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-JHWOM55B.js";
|
|
10
10
|
|
|
11
11
|
// src/setup/agent-probe.ts
|
|
12
12
|
import { execSync } from "child_process";
|
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
import {
|
|
2
2
|
openCodeConfigDirFrom
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-TSCFJJIB.js";
|
|
4
4
|
import {
|
|
5
5
|
nativeClaudeInstallPath
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-7ZYVX2KN.js";
|
|
7
7
|
import {
|
|
8
8
|
grokHome
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-VJZGR4N3.js";
|
|
10
10
|
import {
|
|
11
11
|
claudeConfigDir,
|
|
12
12
|
clineDataDirectory,
|
|
13
13
|
codexHomeFrom
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-PHBZK4SC.js";
|
|
15
15
|
import {
|
|
16
16
|
HOOK_COMPATIBLE_PROFILES
|
|
17
|
-
} from "./chunk-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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
|
|
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.
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import {
|
|
2
2
|
ensureCodexTrustsPusharyHooks
|
|
3
|
-
} from "./chunk-
|
|
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-
|
|
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-
|
|
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-
|
|
31
|
+
} from "./chunk-TSCFJJIB.js";
|
|
32
32
|
import {
|
|
33
33
|
detectClaudeVersion
|
|
34
|
-
} from "./chunk-
|
|
34
|
+
} from "./chunk-LUSWC2MV.js";
|
|
35
35
|
import {
|
|
36
36
|
addCompatibleHooks,
|
|
37
37
|
compatibleConfigFile,
|
|
38
38
|
compatibleInstructionsFile,
|
|
39
39
|
hasCompatibleHooks
|
|
40
|
-
} from "./chunk-
|
|
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-
|
|
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-
|
|
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-
|
|
107
|
+
} from "./chunk-64XK7DLE.js";
|
|
108
108
|
import {
|
|
109
109
|
HOOK_COMPATIBLE_PROFILES,
|
|
110
110
|
isCompatibleAgentType,
|
|
111
111
|
isRecord
|
|
112
|
-
} from "./chunk-
|
|
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-
|
|
3
|
+
} from "./chunk-TSCFJJIB.js";
|
|
4
4
|
import {
|
|
5
5
|
keyPrefix
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-FRPR4OTH.js";
|
|
7
7
|
import {
|
|
8
8
|
bridgeAgentKey
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-PHBZK4SC.js";
|
|
10
10
|
import {
|
|
11
11
|
readJsonSafe
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-64XK7DLE.js";
|
|
13
13
|
import {
|
|
14
14
|
isRecord,
|
|
15
15
|
isValidApiKey
|
|
16
|
-
} from "./chunk-
|
|
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;
|
|
@@ -4,23 +4,23 @@ import {
|
|
|
4
4
|
} from "./chunk-XHKBHWLX.js";
|
|
5
5
|
import {
|
|
6
6
|
readControlMode
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-FRPR4OTH.js";
|
|
8
8
|
import {
|
|
9
9
|
getMachineId
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-V4JZHAOY.js";
|
|
11
11
|
import {
|
|
12
12
|
pusharyDir
|
|
13
|
-
} from "./chunk-
|
|
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-
|
|
20
|
+
} from "./chunk-64XK7DLE.js";
|
|
21
21
|
import {
|
|
22
22
|
getApiKey
|
|
23
|
-
} from "./chunk-
|
|
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-
|
|
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-
|
|
3
|
+
} from "./chunk-PHBZK4SC.js";
|
|
4
4
|
import {
|
|
5
5
|
installPackageGlobally,
|
|
6
6
|
npmErrorMessage,
|
|
7
7
|
writeJsonAtomic
|
|
8
|
-
} from "./chunk-
|
|
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-
|
|
6
|
+
} from "./chunk-SBK7WGSE.js";
|
|
7
7
|
import {
|
|
8
8
|
phoneStartIsRequired
|
|
9
|
-
} from "./chunk-
|
|
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-
|
|
21
|
+
} from "./chunk-ZHL6F2KC.js";
|
|
22
22
|
import {
|
|
23
23
|
autoUpdateLock,
|
|
24
24
|
autoUpdateLog,
|
|
25
25
|
autoUpdateState
|
|
26
|
-
} from "./chunk-
|
|
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-
|
|
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-
|
|
39
|
+
} from "./chunk-GGGOFJBL.js";
|
|
40
40
|
import {
|
|
41
41
|
commandsPackage,
|
|
42
42
|
globalUpgradeEntry,
|
|
@@ -5,19 +5,19 @@ import {
|
|
|
5
5
|
hermesAppWiringRecord,
|
|
6
6
|
hermesConfigReceipt,
|
|
7
7
|
hermesSetupReceipt
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-PHBZK4SC.js";
|
|
9
9
|
import {
|
|
10
10
|
readJsonSafe,
|
|
11
11
|
resolvePathBinary,
|
|
12
12
|
writeJsonAtomic
|
|
13
|
-
} from "./chunk-
|
|
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-
|
|
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(
|
|
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 =
|
|
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
|
|
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
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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.",
|