@pushary/agent-hooks 0.87.2 → 0.87.3
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/data/cursor-plugin/hooks/hooks.json +7 -0
- package/data/cursor-plugin/scripts/pushary-gate.mjs +85 -28
- package/data/cursor-plugin/scripts/pushary-gate.test.mjs +42 -1
- package/dist/bin/pushary-bell-hook.js +4 -4
- package/dist/bin/pushary-bell.js +7 -6
- package/dist/bin/pushary-claude.js +14 -11
- package/dist/bin/pushary-clean.js +36 -20
- package/dist/bin/pushary-codex-bridge.js +6 -5
- package/dist/bin/pushary-codex-hook.js +129 -52
- package/dist/bin/pushary-codex.js +4 -3
- package/dist/bin/pushary-connect.js +6 -6
- package/dist/bin/pushary-daemon.js +5 -4
- package/dist/bin/pushary-disconnect.js +65 -8
- package/dist/bin/pushary-doctor.js +79 -18
- package/dist/bin/pushary-elicitation-hook.js +1 -1
- package/dist/bin/pushary-gemini-bridge.js +6 -5
- package/dist/bin/pushary-gemini-hook.js +12 -10
- package/dist/bin/pushary-hook.js +6 -5
- package/dist/bin/pushary-login.js +4 -4
- package/dist/bin/pushary-logout.js +5 -4
- package/dist/bin/pushary-mode.js +3 -3
- package/dist/bin/pushary-notification-hook.js +4 -3
- package/dist/bin/pushary-opencode-hook.d.ts +1 -0
- package/dist/bin/pushary-opencode-hook.js +329 -0
- package/dist/bin/pushary-permission-denied-hook.js +6 -5
- package/dist/bin/pushary-permission-hook.js +6 -5
- package/dist/bin/pushary-post-hook.js +4 -3
- package/dist/bin/pushary-prompt-hook.js +4 -3
- package/dist/bin/pushary-session-end-hook.js +4 -3
- package/dist/bin/pushary-session-start-hook.js +4 -3
- package/dist/bin/pushary-setup.js +238 -84
- package/dist/bin/pushary-stats.js +2 -2
- package/dist/bin/pushary-status.js +5 -5
- package/dist/bin/pushary-stop-hook.js +4 -3
- package/dist/bin/pushary-stopfailure-hook.js +4 -3
- package/dist/bin/pushary-upgrade.js +7 -6
- package/dist/bin/pushary-wait.js +3 -3
- package/dist/{chunk-5646VKR4.js → chunk-2JYQAW4B.js} +5 -53
- package/dist/chunk-7J67NYK4.js +287 -0
- package/dist/{chunk-M7RYIZX6.js → chunk-B7ZBHWOW.js} +10 -1
- package/dist/{chunk-7YJS2VTG.js → chunk-DMW54VWK.js} +12 -1
- package/dist/{chunk-LT2VLPUK.js → chunk-DPUOW6NS.js} +18 -2
- package/dist/{chunk-3LKYBFWA.js → chunk-EJL3PJU7.js} +1 -1
- package/dist/chunk-GIRT7677.js +11 -0
- package/dist/{chunk-HAUTLCMA.js → chunk-ICERLAE2.js} +1 -1
- package/dist/{chunk-ZZE6FN7T.js → chunk-K42RXJPG.js} +52 -1
- package/dist/{chunk-Y3UTTFTZ.js → chunk-KBS53WOE.js} +1 -1
- package/dist/{chunk-SJVLFIYP.js → chunk-KCAFCGGY.js} +1 -1
- package/dist/{chunk-A4I5JGCW.js → chunk-KFAH6KRE.js} +1 -1
- package/dist/{chunk-QBWYNVKY.js → chunk-LGVHYFIB.js} +1 -1
- package/dist/{chunk-HJ44HIUW.js → chunk-OMA2OZRE.js} +11 -9
- package/dist/{chunk-ECEUMWBH.js → chunk-QLMX5LLV.js} +1 -1
- package/dist/{chunk-XW5CHAQX.js → chunk-TAYHEJFF.js} +2 -2
- package/dist/{chunk-JAICP7BA.js → chunk-TD3M232T.js} +1 -1
- package/dist/{chunk-WLLBFDVY.js → chunk-TV3UFGT7.js} +1 -1
- package/dist/{chunk-7MBDFNUC.js → chunk-VIHRP25S.js} +1 -1
- package/dist/{chunk-B3V2H6SA.js → chunk-VL5ZNK6V.js} +43 -12
- package/dist/{chunk-V2XPF77V.js → chunk-VOE4SYPR.js} +42 -279
- package/dist/{chunk-OT4YD54Q.js → chunk-YX6ZKCC5.js} +1 -1
- package/dist/{chunk-2T3GX7WG.js → chunk-YYTLTEGU.js} +1 -1
- package/dist/chunk-ZGC3W23N.js +371 -0
- package/dist/src/index.js +6 -5
- package/package.json +2 -1
|
@@ -1,7 +1,11 @@
|
|
|
1
|
+
import {
|
|
2
|
+
PRETOOLUSE_GATED_TOOLS,
|
|
3
|
+
PRETOOLUSE_PHONE_ANSWERED_TOOLS
|
|
4
|
+
} from "./chunk-ATBKJNWS.js";
|
|
1
5
|
import {
|
|
2
6
|
HOOK_BUDGETS,
|
|
3
7
|
HOOK_LOCATOR_BINARY
|
|
4
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-DMW54VWK.js";
|
|
5
9
|
|
|
6
10
|
// src/gemini-config.ts
|
|
7
11
|
var GEMINI_HOOK_BINARY = "pushary-gemini-hook";
|
|
@@ -102,7 +106,54 @@ var missingGeminiHookEvents = (settings) => {
|
|
|
102
106
|
}).map((definition) => definition.event);
|
|
103
107
|
};
|
|
104
108
|
|
|
109
|
+
// src/agent-version.ts
|
|
110
|
+
var parseVersion = (raw) => {
|
|
111
|
+
const match = raw.match(/(\d+)\.(\d+)\.(\d+)/);
|
|
112
|
+
return match ? [Number(match[1]), Number(match[2]), Number(match[3])] : null;
|
|
113
|
+
};
|
|
114
|
+
var compareVersion = (a, b) => {
|
|
115
|
+
for (let index = 0; index < 3; index++) {
|
|
116
|
+
if (a[index] !== b[index]) return a[index] < b[index] ? -1 : 1;
|
|
117
|
+
}
|
|
118
|
+
return 0;
|
|
119
|
+
};
|
|
120
|
+
var atLeast = (version, floor) => compareVersion(version, floor) >= 0;
|
|
121
|
+
|
|
122
|
+
// src/claude-events.ts
|
|
123
|
+
var GATED_TOOLS = [...PRETOOLUSE_GATED_TOOLS, ...PRETOOLUSE_PHONE_ANSWERED_TOOLS].join("|");
|
|
124
|
+
var MIRRORED_TOOLS = [...PRETOOLUSE_GATED_TOOLS, "TodoWrite"].join("|");
|
|
125
|
+
var ESTABLISHED = [1, 0, 0];
|
|
126
|
+
var OBSERVED = [2, 0, 0];
|
|
127
|
+
var CLAUDE_HOOK_EVENTS = [
|
|
128
|
+
{ event: "PreToolUse", binary: "pushary-hook", matcher: GATED_TOOLS, timeout: HOOK_BUDGETS.claude.budgetSeconds, delivery: "approval", since: ESTABLISHED },
|
|
129
|
+
{ event: "PostToolUse", binary: "pushary-post-hook", matcher: MIRRORED_TOOLS, timeout: 10, delivery: "telemetry", since: ESTABLISHED },
|
|
130
|
+
{ event: "Stop", binary: "pushary-stop-hook", timeout: 10, delivery: "telemetry", since: ESTABLISHED },
|
|
131
|
+
{ event: "UserPromptSubmit", binary: "pushary-prompt-hook", timeout: 10, delivery: "telemetry", since: ESTABLISHED },
|
|
132
|
+
{ event: "Notification", binary: "pushary-notification-hook", matcher: "permission_prompt|idle_prompt|agent_needs_input|agent_completed", timeout: 10, delivery: "telemetry", since: ESTABLISHED },
|
|
133
|
+
{ event: "SessionStart", binary: "pushary-session-start-hook", matcher: "startup|resume|clear", timeout: 10, delivery: "telemetry", since: ESTABLISHED },
|
|
134
|
+
{ event: "SessionEnd", binary: "pushary-session-end-hook", timeout: 10, delivery: "telemetry", since: ESTABLISHED },
|
|
135
|
+
{ event: "StopFailure", binary: "pushary-stopfailure-hook", timeout: 10, delivery: "telemetry", since: ESTABLISHED },
|
|
136
|
+
{ event: "PermissionRequest", binary: "pushary-permission-hook", timeout: HOOK_BUDGETS.claude.budgetSeconds, delivery: "approval", since: ESTABLISHED },
|
|
137
|
+
{ event: "PermissionDenied", binary: "pushary-permission-denied-hook", timeout: 60, delivery: "telemetry", since: ESTABLISHED },
|
|
138
|
+
// Observability only. Each carries session shape the island cannot get any other
|
|
139
|
+
// way, and none of them decides anything.
|
|
140
|
+
{ event: "SubagentStart", binary: "pushary-session-start-hook", timeout: 10, delivery: "telemetry", since: OBSERVED },
|
|
141
|
+
{ event: "SubagentStop", binary: "pushary-stop-hook", timeout: 10, delivery: "telemetry", since: OBSERVED },
|
|
142
|
+
{ event: "PreCompact", binary: "pushary-post-hook", matcher: "manual|auto", timeout: 10, delivery: "telemetry", since: OBSERVED },
|
|
143
|
+
{ event: "PostCompact", binary: "pushary-post-hook", matcher: "manual|auto", timeout: 10, delivery: "telemetry", since: OBSERVED },
|
|
144
|
+
{ event: "TeammateIdle", binary: "pushary-notification-hook", timeout: 10, delivery: "telemetry", since: OBSERVED },
|
|
145
|
+
// An MCP server asking the person for a value, answered from the phone rather
|
|
146
|
+
// than the terminal. No matcher: Claude Code matches this event on the MCP
|
|
147
|
+
// server's name, and we have no business preferring one server over another.
|
|
148
|
+
{ event: "Elicitation", binary: "pushary-elicitation-hook", timeout: HOOK_BUDGETS.claude.budgetSeconds, delivery: "approval", since: OBSERVED, cliOnly: true }
|
|
149
|
+
];
|
|
150
|
+
var supportedClaudeEvents = (version) => CLAUDE_HOOK_EVENTS.filter((hook) => version ? atLeast(version, hook.since) : hook.since === ESTABLISHED);
|
|
151
|
+
|
|
105
152
|
export {
|
|
153
|
+
parseVersion,
|
|
154
|
+
compareVersion,
|
|
155
|
+
CLAUDE_HOOK_EVENTS,
|
|
156
|
+
supportedClaudeEvents,
|
|
106
157
|
GEMINI_HOOK_BINARY,
|
|
107
158
|
addGeminiMcpServer,
|
|
108
159
|
addGeminiHooks,
|
|
@@ -5,25 +5,19 @@ import {
|
|
|
5
5
|
import {
|
|
6
6
|
isGatingMoment,
|
|
7
7
|
recordKeylessMoment
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-VIHRP25S.js";
|
|
9
9
|
import {
|
|
10
10
|
denyReasonFrom,
|
|
11
11
|
isDeferAnswer
|
|
12
12
|
} from "./chunk-YHG74UFF.js";
|
|
13
13
|
import {
|
|
14
|
-
deriveAction,
|
|
15
|
-
deriveActionBody,
|
|
16
|
-
deriveBlocker,
|
|
17
|
-
deriveToolTarget,
|
|
18
|
-
describeToolCall,
|
|
19
14
|
fetchModeState,
|
|
20
15
|
getPolicy,
|
|
21
16
|
readLastPrompt,
|
|
22
17
|
readLastUserPrompt,
|
|
23
18
|
repoKeyFor,
|
|
24
|
-
scopePathFor,
|
|
25
19
|
throttlePass
|
|
26
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-VOE4SYPR.js";
|
|
27
21
|
import {
|
|
28
22
|
DEFAULT_SESSION,
|
|
29
23
|
askUser,
|
|
@@ -32,6 +26,14 @@ import {
|
|
|
32
26
|
savePendingQuestion,
|
|
33
27
|
sendNotification
|
|
34
28
|
} from "./chunk-J3YDT4HM.js";
|
|
29
|
+
import {
|
|
30
|
+
deriveAction,
|
|
31
|
+
deriveActionBody,
|
|
32
|
+
deriveBlocker,
|
|
33
|
+
deriveToolTarget,
|
|
34
|
+
describeToolCall,
|
|
35
|
+
scopePathFor
|
|
36
|
+
} from "./chunk-7J67NYK4.js";
|
|
35
37
|
import {
|
|
36
38
|
getMachineId
|
|
37
39
|
} from "./chunk-RN3NOEJF.js";
|
|
@@ -48,7 +50,7 @@ import {
|
|
|
48
50
|
hookWaitDeadline,
|
|
49
51
|
parseAgentQuestionAnswers,
|
|
50
52
|
resolveGate
|
|
51
|
-
} from "./chunk-
|
|
53
|
+
} from "./chunk-DMW54VWK.js";
|
|
52
54
|
|
|
53
55
|
// src/decision-episode.ts
|
|
54
56
|
var EPISODE_PATH = "/api/agent/decision-episode";
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
hasGeminiHooks
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-K42RXJPG.js";
|
|
4
4
|
import {
|
|
5
5
|
hasCodexHooks
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-VL5ZNK6V.js";
|
|
7
7
|
|
|
8
8
|
// src/diagnostics/wiring.ts
|
|
9
9
|
var record = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
@@ -1,7 +1,10 @@
|
|
|
1
|
+
import {
|
|
2
|
+
canonicalJson
|
|
3
|
+
} from "./chunk-GIRT7677.js";
|
|
1
4
|
import {
|
|
2
5
|
HOOK_BUDGETS,
|
|
3
6
|
HOOK_LOCATOR_BINARY
|
|
4
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-DMW54VWK.js";
|
|
5
8
|
|
|
6
9
|
// src/codex-config.ts
|
|
7
10
|
import { createHash } from "crypto";
|
|
@@ -20,7 +23,11 @@ var CODEX_HOOK_EVENTS = [
|
|
|
20
23
|
{ event: "PostToolUse", matcher: "Bash|apply_patch", delivery: "telemetry", timeout: 10 },
|
|
21
24
|
{ event: "UserPromptSubmit", delivery: "telemetry", timeout: 10 },
|
|
22
25
|
{ event: "Stop", delivery: "telemetry", timeout: 10 },
|
|
23
|
-
{ event: "SessionStart", matcher: "startup|resume", delivery: "telemetry", timeout: 10 }
|
|
26
|
+
{ event: "SessionStart", matcher: "startup|resume", delivery: "telemetry", timeout: 10 },
|
|
27
|
+
// Codex caps SessionEnd at 3s and defaults it to 1s, unlike every other event
|
|
28
|
+
// here. It also runs synchronously whatever `async` says, so the handler must
|
|
29
|
+
// fit the window rather than assume the 10s Claude Code allows.
|
|
30
|
+
{ event: "SessionEnd", delivery: "telemetry", timeout: 3 }
|
|
24
31
|
];
|
|
25
32
|
var CODEX_EVENT_KEY = {
|
|
26
33
|
PermissionRequest: "permission_request",
|
|
@@ -28,8 +35,10 @@ var CODEX_EVENT_KEY = {
|
|
|
28
35
|
PostToolUse: "post_tool_use",
|
|
29
36
|
UserPromptSubmit: "user_prompt_submit",
|
|
30
37
|
Stop: "stop",
|
|
31
|
-
SessionStart: "session_start"
|
|
38
|
+
SessionStart: "session_start",
|
|
39
|
+
SessionEnd: "session_end"
|
|
32
40
|
};
|
|
41
|
+
var codexEventKeyOf = (event) => event.replace(/([a-z0-9])([A-Z])/g, "$1_$2").toLowerCase();
|
|
33
42
|
var asRecord = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
34
43
|
var ensureRecord = (target, key) => {
|
|
35
44
|
const existing = asRecord(target[key]);
|
|
@@ -121,19 +130,39 @@ var missingCodexHookEvents = (config) => {
|
|
|
121
130
|
return !Array.isArray(entries) || !entries.some(isPusharyCodexHook);
|
|
122
131
|
}).map((definition) => definition.event);
|
|
123
132
|
};
|
|
124
|
-
var canonicalJson = (value) => {
|
|
125
|
-
if (value === null || typeof value !== "object") return JSON.stringify(value);
|
|
126
|
-
if (Array.isArray(value)) return "[" + value.map(canonicalJson).join(",") + "]";
|
|
127
|
-
const obj = value;
|
|
128
|
-
return "{" + Object.keys(obj).sort().map((key) => JSON.stringify(key) + ":" + canonicalJson(obj[key])).join(",") + "}";
|
|
129
|
-
};
|
|
130
133
|
var codexHookTrustHash = (definition, command) => {
|
|
131
|
-
const hook = {
|
|
134
|
+
const hook = { command, timeout: definition.timeout, type: "command" };
|
|
132
135
|
if (definition.statusMessage) hook.statusMessage = definition.statusMessage;
|
|
133
|
-
const
|
|
134
|
-
if (definition.matcher)
|
|
136
|
+
const entry = { hooks: [hook] };
|
|
137
|
+
if (definition.matcher) entry.matcher = definition.matcher;
|
|
138
|
+
return codexEntryTrustHash(CODEX_EVENT_KEY[definition.event], entry);
|
|
139
|
+
};
|
|
140
|
+
var codexEntryTrustHash = (eventKey, entry) => {
|
|
141
|
+
const hooks = Array.isArray(entry.hooks) ? entry.hooks : [];
|
|
142
|
+
const identity = {
|
|
143
|
+
event_name: eventKey,
|
|
144
|
+
hooks: hooks.map((hook) => ({ async: false, ...asRecord(hook) }))
|
|
145
|
+
};
|
|
146
|
+
if (typeof entry.matcher === "string") identity.matcher = entry.matcher;
|
|
135
147
|
return "sha256:" + createHash("sha256").update(canonicalJson(identity), "utf8").digest("hex");
|
|
136
148
|
};
|
|
149
|
+
var codexTrustSchemeAgrees = (hooksConfig, config, hooksJsonPath) => {
|
|
150
|
+
const hooks = asRecord(hooksConfig?.hooks);
|
|
151
|
+
const state = asRecord(asRecord(config.hooks)?.state);
|
|
152
|
+
if (!hooks || !state) return false;
|
|
153
|
+
for (const [event, entries] of Object.entries(hooks)) {
|
|
154
|
+
if (!Array.isArray(entries)) continue;
|
|
155
|
+
const eventKey = CODEX_EVENT_KEY[event] ?? codexEventKeyOf(event);
|
|
156
|
+
for (const [index, entry] of entries.entries()) {
|
|
157
|
+
const record = asRecord(entry);
|
|
158
|
+
if (!record) continue;
|
|
159
|
+
const stored = asRecord(state[`${hooksJsonPath}:${eventKey}:${index}:0`])?.trusted_hash;
|
|
160
|
+
if (typeof stored !== "string") continue;
|
|
161
|
+
if (stored === codexEntryTrustHash(eventKey, record)) return true;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
return false;
|
|
165
|
+
};
|
|
137
166
|
var codexHookPositions = (hooksConfig) => {
|
|
138
167
|
const hooks = asRecord(hooksConfig?.hooks);
|
|
139
168
|
const positions = {};
|
|
@@ -237,8 +266,10 @@ export {
|
|
|
237
266
|
removeCodexHooks,
|
|
238
267
|
hasCodexHooks,
|
|
239
268
|
missingCodexHookEvents,
|
|
269
|
+
codexTrustSchemeAgrees,
|
|
240
270
|
codexHookPositions,
|
|
241
271
|
untrustedCodexHookEvents,
|
|
272
|
+
removeCodexHookTrust,
|
|
242
273
|
removeCodexSettings,
|
|
243
274
|
addCodexHookTrust,
|
|
244
275
|
claudeSettings,
|