@pushary/agent-hooks 1.7.2 → 1.8.0
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 +14 -0
- package/README.md +13 -1
- package/data/cursor-plugin/CHANGELOG.md +1 -0
- package/data/cursor-plugin/scripts/pushary-gate.mjs +2 -21
- package/data/cursor-plugin/scripts/pushary-gate.test.mjs +23 -1
- package/data/cursor-plugin/scripts/redaction.mjs +51 -0
- package/data/vscode-plugin/CHANGELOG.md +1 -0
- package/data/vscode-plugin/scripts/pushary-gate.mjs +1 -21
- package/data/vscode-plugin/scripts/redaction.mjs +51 -0
- package/dist/bin/pushary-bell-hook.js +23 -23
- package/dist/bin/pushary-bell.js +9 -9
- package/dist/bin/pushary-claude.js +28 -28
- package/dist/bin/pushary-clean.js +91 -107
- package/dist/bin/pushary-codex-bridge.js +18 -18
- package/dist/bin/pushary-codex-hook.js +42 -43
- package/dist/bin/pushary-codex.js +13 -13
- package/dist/bin/pushary-connect.js +13 -13
- package/dist/bin/pushary-cowork.js +5 -5
- package/dist/bin/pushary-daemon.js +27 -27
- package/dist/bin/pushary-disconnect.js +18 -18
- package/dist/bin/pushary-doctor.js +85 -79
- package/dist/bin/pushary-elicitation-hook.js +25 -25
- package/dist/bin/pushary-gemini-bridge.js +12 -12
- package/dist/bin/pushary-gemini-hook.js +29 -30
- package/dist/bin/pushary-hook.js +32 -32
- package/dist/bin/pushary-login.js +12 -12
- package/dist/bin/pushary-logout.js +7 -7
- package/dist/bin/pushary-mcp.js +85 -28
- package/dist/bin/pushary-mode.js +9 -9
- package/dist/bin/pushary-notification-hook.js +28 -28
- package/dist/bin/pushary-opencode-hook.js +28 -29
- package/dist/bin/pushary-permission-denied-hook.js +32 -32
- package/dist/bin/pushary-permission-hook.js +31 -31
- package/dist/bin/pushary-post-hook.js +28 -28
- package/dist/bin/pushary-prompt-hook.js +28 -28
- package/dist/bin/pushary-session-end-hook.js +28 -28
- package/dist/bin/pushary-session-start-hook.js +32 -32
- package/dist/bin/pushary-setup.js +60 -127
- package/dist/bin/pushary-stats.js +5 -5
- package/dist/bin/pushary-status.js +15 -15
- package/dist/bin/pushary-stop-hook.js +28 -28
- package/dist/bin/pushary-stopfailure-hook.js +28 -28
- package/dist/bin/pushary-suggestions.js +4 -4
- package/dist/bin/pushary-transcript-register.js +19 -19
- package/dist/bin/pushary-transcripts.js +7 -7
- package/dist/bin/pushary-upgrade.js +231 -105
- package/dist/bin/pushary-wait.js +9 -9
- package/dist/bin/pushary.js +6 -6
- package/dist/{chunk-VQDJ6KB3.js → chunk-2ABTSGFT.js} +3 -3
- package/dist/{chunk-UFNBKYWT.js → chunk-2OY3ZZ5N.js} +3 -3
- package/dist/{chunk-A6WQF7FQ.js → chunk-3EVPNIBE.js} +3 -3
- package/dist/{chunk-L6IF4JOF.js → chunk-6J2JC2RT.js} +3 -3
- package/dist/{chunk-3NSRL5E7.js → chunk-7Z7Q4GGS.js} +2 -2
- package/dist/{chunk-NYBZJ44G.js → chunk-A2A2YTMG.js} +108 -25
- package/dist/{chunk-SKEW3TQX.js → chunk-AHV4LNB5.js} +1 -1
- package/dist/{chunk-KAJT6HPI.js → chunk-AXPCESYO.js} +3 -3
- package/dist/{chunk-JMSB6CJT.js → chunk-C2WCPBF7.js} +1 -1
- package/dist/chunk-CJVVKOSR.js +73 -0
- package/dist/{chunk-2B4XCMH6.js → chunk-CULUZTWQ.js} +283 -41
- package/dist/{chunk-6QZK4O36.js → chunk-CVQYGMSR.js} +4 -4
- package/dist/{chunk-SEIZQZEE.js → chunk-D2FX3EPW.js} +1 -1
- package/dist/{chunk-LI3C2IQY.js → chunk-DB3ONZB4.js} +2 -2
- package/dist/{chunk-YU54LNC6.js → chunk-DE4Y6TCC.js} +6 -6
- package/dist/{chunk-65RN6ZCO.js → chunk-E4FJTUH4.js} +1 -1
- package/dist/{chunk-RLRDPZG4.js → chunk-EPCXUP3P.js} +3 -1
- package/dist/{chunk-OXEF6JHD.js → chunk-EX3GAPA4.js} +1 -1
- package/dist/{chunk-E3PLF7YB.js → chunk-EYK3GSRH.js} +4 -4
- package/dist/{chunk-SJN64NJX.js → chunk-GPEGOGRJ.js} +1 -1
- package/dist/chunk-GU3EJCVV.js +43 -0
- package/dist/{chunk-YNJ2H2FD.js → chunk-HQ6G3B5R.js} +2 -2
- package/dist/{chunk-6NHXKTQU.js → chunk-HZVVNGEW.js} +2 -2
- package/dist/{chunk-ZUKZJQGR.js → chunk-IPVKH2ST.js} +1 -1
- package/dist/{chunk-T3PRPFDY.js → chunk-J5TIV3J6.js} +5 -5
- package/dist/{chunk-F6BMJOSW.js → chunk-JU6W3XAU.js} +1 -1
- package/dist/{chunk-AWZNDM3B.js → chunk-JYG4B33G.js} +1 -1
- package/dist/{chunk-ZMWX5SDA.js → chunk-JZTHGAPA.js} +8 -8
- package/dist/{chunk-3RO2UWJQ.js → chunk-KGDWDSNL.js} +2 -2
- package/dist/{chunk-MY3UZO5U.js → chunk-KVQBWI5T.js} +3 -3
- package/dist/{chunk-HOIGXMI7.js → chunk-LXQ5FBBD.js} +1 -1
- package/dist/{chunk-D4OGEWLH.js → chunk-M5ICYGHT.js} +3 -3
- package/dist/{chunk-YLCSKVQQ.js → chunk-M5JDZ45W.js} +4 -4
- package/dist/{chunk-SKMWPXGV.js → chunk-MDEPU45E.js} +183 -28
- package/dist/{chunk-R7ZETVNR.js → chunk-MPWXGONV.js} +3 -3
- package/dist/{chunk-2XOHXZRC.js → chunk-NGDD6TXY.js} +20 -5
- package/dist/{chunk-WWTLAZQA.js → chunk-NQTHFF4W.js} +178 -39
- package/dist/{chunk-KLXYMPTM.js → chunk-OKXS7WDZ.js} +1 -1
- package/dist/{chunk-Q7CDBJXU.js → chunk-OLQRRFBX.js} +1 -1
- package/dist/{chunk-VSGKQ3HI.js → chunk-P7WQEVWA.js} +2 -2
- package/dist/{chunk-UUTWHVSV.js → chunk-Q7E4I6UA.js} +28 -16
- package/dist/{chunk-WXIVUIXA.js → chunk-R7437YI3.js} +2 -2
- package/dist/{chunk-5GIBNUBD.js → chunk-RHR6NARM.js} +2 -2
- package/dist/{chunk-QSZ37ROP.js → chunk-SAXBC4AZ.js} +1 -1
- package/dist/{chunk-U3MS6D5X.js → chunk-SIFVFP6T.js} +1 -1
- package/dist/{chunk-TVJYGZXB.js → chunk-SLFABHW3.js} +40 -10
- package/dist/{chunk-M2JGLFFS.js → chunk-TY4JT7W3.js} +1 -1
- package/dist/{chunk-YU4222G2.js → chunk-UI36QSBI.js} +28 -16
- package/dist/{chunk-OJRB5QKN.js → chunk-VQKRG3AC.js} +6 -6
- package/dist/{chunk-4S7CCOXB.js → chunk-WE2J62NC.js} +31 -17
- package/dist/{chunk-PWNQGITG.js → chunk-ZSZSSJCV.js} +1 -1
- package/dist/{chunk-I7B2DNVU.js → chunk-ZVSKMPBZ.js} +1 -1
- package/dist/{reapply-D6RSDYCK.js → reapply-CW3MA66V.js} +9 -9
- package/dist/src/index.js +20 -20
- package/package.json +1 -1
- package/dist/chunk-INR3RVQ3.js +0 -92
- package/dist/chunk-IZVAVXHQ.js +0 -255
package/dist/chunk-IZVAVXHQ.js
DELETED
|
@@ -1,255 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
AUTO_DECISION_WINDOW_MS,
|
|
3
|
-
canonicalGateInputs,
|
|
4
|
-
claimThrottle,
|
|
5
|
-
deriveToolTarget,
|
|
6
|
-
permissionDeniedAction,
|
|
7
|
-
releaseThrottle,
|
|
8
|
-
reportEvent
|
|
9
|
-
} from "./chunk-2B4XCMH6.js";
|
|
10
|
-
import {
|
|
11
|
-
DEFAULT_SESSION
|
|
12
|
-
} from "./chunk-N6IOSZOE.js";
|
|
13
|
-
import {
|
|
14
|
-
canonicalJson
|
|
15
|
-
} from "./chunk-NYBZJ44G.js";
|
|
16
|
-
import {
|
|
17
|
-
writeFileAtomic
|
|
18
|
-
} from "./chunk-T7E75EKA.js";
|
|
19
|
-
import {
|
|
20
|
-
BLOCKED_ACTION_GRANT_TTL_SECONDS,
|
|
21
|
-
blockedActionIdentity,
|
|
22
|
-
machineDecisionFor,
|
|
23
|
-
shouldDeferToNativeMode,
|
|
24
|
-
wireVerdictFor
|
|
25
|
-
} from "./chunk-WWTLAZQA.js";
|
|
26
|
-
|
|
27
|
-
// src/machine-decision-audit.ts
|
|
28
|
-
import { basename } from "path";
|
|
29
|
-
var reportMachineDenial = async (input, verdict, agent) => {
|
|
30
|
-
const decision = machineDecisionFor(wireVerdictFor(verdict, 0));
|
|
31
|
-
if (!decision) return;
|
|
32
|
-
const sessionKey = input.session_id || DEFAULT_SESSION;
|
|
33
|
-
const toolTarget = deriveToolTarget(input.tool_name, input.tool_input);
|
|
34
|
-
const throttleKey = `machinedecision:${agent.type}:${sessionKey}:${input.tool_name}:${decision.origin}:${toolTarget ?? ""}`;
|
|
35
|
-
const throttle = claimThrottle(throttleKey, AUTO_DECISION_WINDOW_MS);
|
|
36
|
-
if (throttle === "throttled") return;
|
|
37
|
-
if (throttle === "unavailable") {
|
|
38
|
-
console.error("[Pushary] Could not access the audit throttle; reporting this denial without deduplication.");
|
|
39
|
-
}
|
|
40
|
-
try {
|
|
41
|
-
const response = await reportEvent({
|
|
42
|
-
event: "permission_denied",
|
|
43
|
-
occurredAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
44
|
-
toolUseId: input.tool_use_id,
|
|
45
|
-
agentType: agent.type,
|
|
46
|
-
agentName: `${agent.label} - ${basename(input.cwd ?? process.cwd())}`,
|
|
47
|
-
action: permissionDeniedAction(input.tool_name),
|
|
48
|
-
sessionId: input.session_id,
|
|
49
|
-
decisionSource: "policy_auto",
|
|
50
|
-
decisionOrigin: decision.origin,
|
|
51
|
-
decisionOutcome: decision.outcome,
|
|
52
|
-
toolName: input.tool_name,
|
|
53
|
-
toolTarget
|
|
54
|
-
}, { maxAttempts: 1, timeoutMs: 800, requireOk: true, skipDrain: true });
|
|
55
|
-
if (response?.ok !== true) throw new Error("Audit delivery was not acknowledged");
|
|
56
|
-
} catch {
|
|
57
|
-
console.error("[Pushary] Could not record the machine denial. The action remains blocked; its audit delivery will be retried on the next matching denial.");
|
|
58
|
-
if (throttle === "passed") {
|
|
59
|
-
try {
|
|
60
|
-
releaseThrottle(throttleKey);
|
|
61
|
-
} catch {
|
|
62
|
-
console.error("[Pushary] Could not reset the failed audit throttle.");
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
};
|
|
67
|
-
|
|
68
|
-
// src/native-defer.ts
|
|
69
|
-
var nativeDeferOptions = () => {
|
|
70
|
-
const respect = process.env.PUSHARY_RESPECT_PERMISSION_MODE;
|
|
71
|
-
const bypass = process.env.PUSHARY_DEFER_BYPASS;
|
|
72
|
-
return {
|
|
73
|
-
respectPermissionMode: !(respect === "0" || respect === "false"),
|
|
74
|
-
deferBypass: bypass === "1" || bypass === "true"
|
|
75
|
-
};
|
|
76
|
-
};
|
|
77
|
-
var defersToNativeMode = (permissionMode, toolName) => shouldDeferToNativeMode(permissionMode, toolName, nativeDeferOptions());
|
|
78
|
-
|
|
79
|
-
// src/codex-call-decision.ts
|
|
80
|
-
import { createHash } from "crypto";
|
|
81
|
-
import { existsSync, mkdirSync, readFileSync, readdirSync, rmSync, statSync, writeFileSync } from "fs";
|
|
82
|
-
import { tmpdir, userInfo } from "os";
|
|
83
|
-
import { join } from "path";
|
|
84
|
-
var CODEX_DECISION_TTL_MS = 60 * 1e3;
|
|
85
|
-
var userScope = () => {
|
|
86
|
-
try {
|
|
87
|
-
const info = userInfo();
|
|
88
|
-
const id = typeof info.uid === "number" && info.uid >= 0 ? String(info.uid) : info.username;
|
|
89
|
-
return id.replace(/[^A-Za-z0-9_-]/g, "_").slice(0, 128) || "default";
|
|
90
|
-
} catch {
|
|
91
|
-
return "default";
|
|
92
|
-
}
|
|
93
|
-
};
|
|
94
|
-
var decisionDir = () => join(tmpdir(), `pushary-codex-decisions-${userScope()}`);
|
|
95
|
-
var decisionFile = (key) => join(decisionDir(), key);
|
|
96
|
-
var PRESENTATION_ONLY_INPUT_KEYS = ["description"];
|
|
97
|
-
var identifyingToolInput = (toolInput) => {
|
|
98
|
-
if (!toolInput) return {};
|
|
99
|
-
const identifying = {};
|
|
100
|
-
for (const [key, value] of Object.entries(toolInput)) {
|
|
101
|
-
if (PRESENTATION_ONLY_INPUT_KEYS.includes(key)) continue;
|
|
102
|
-
identifying[key] = value;
|
|
103
|
-
}
|
|
104
|
-
return identifying;
|
|
105
|
-
};
|
|
106
|
-
var codexCallKey = (identity) => {
|
|
107
|
-
if (!identity.sessionId || !identity.turnId || !identity.toolName) return null;
|
|
108
|
-
const material = canonicalJson([
|
|
109
|
-
identity.sessionId,
|
|
110
|
-
identity.turnId,
|
|
111
|
-
identity.toolName,
|
|
112
|
-
identifyingToolInput(identity.toolInput)
|
|
113
|
-
]);
|
|
114
|
-
return createHash("sha256").update(material, "utf-8").digest("hex").slice(0, 32);
|
|
115
|
-
};
|
|
116
|
-
var parseDecision = (raw) => {
|
|
117
|
-
try {
|
|
118
|
-
const parsed = JSON.parse(raw);
|
|
119
|
-
if (parsed.kind === "approved") {
|
|
120
|
-
return typeof parsed.toolUseId === "string" && parsed.toolUseId ? { kind: "approved", toolUseId: parsed.toolUseId } : null;
|
|
121
|
-
}
|
|
122
|
-
if (parsed.kind === "handled") return { kind: "handled" };
|
|
123
|
-
if (parsed.kind === "denied") {
|
|
124
|
-
return { kind: "denied", reason: typeof parsed.reason === "string" ? parsed.reason : "" };
|
|
125
|
-
}
|
|
126
|
-
return null;
|
|
127
|
-
} catch {
|
|
128
|
-
return null;
|
|
129
|
-
}
|
|
130
|
-
};
|
|
131
|
-
var sweepExpired = (dir, nowMs) => {
|
|
132
|
-
try {
|
|
133
|
-
for (const name of readdirSync(dir)) {
|
|
134
|
-
const path = join(dir, name);
|
|
135
|
-
try {
|
|
136
|
-
if (nowMs - statSync(path).mtimeMs >= CODEX_DECISION_TTL_MS) rmSync(path, { force: true });
|
|
137
|
-
} catch {
|
|
138
|
-
continue;
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
} catch {
|
|
142
|
-
return;
|
|
143
|
-
}
|
|
144
|
-
};
|
|
145
|
-
var recordCodexCallDecision = (key, decision) => {
|
|
146
|
-
if (!key) return;
|
|
147
|
-
try {
|
|
148
|
-
const dir = decisionDir();
|
|
149
|
-
if (!existsSync(dir)) mkdirSync(dir, { recursive: true, mode: 448 });
|
|
150
|
-
else sweepExpired(dir, Date.now());
|
|
151
|
-
writeFileSync(decisionFile(key), JSON.stringify(decision), { encoding: "utf-8", mode: 384 });
|
|
152
|
-
} catch {
|
|
153
|
-
return;
|
|
154
|
-
}
|
|
155
|
-
};
|
|
156
|
-
var consumeCodexCallDecision = (key) => {
|
|
157
|
-
if (!key) return null;
|
|
158
|
-
const path = decisionFile(key);
|
|
159
|
-
try {
|
|
160
|
-
const fresh = Date.now() - statSync(path).mtimeMs < CODEX_DECISION_TTL_MS;
|
|
161
|
-
const raw = readFileSync(path, "utf-8");
|
|
162
|
-
rmSync(path, { force: true });
|
|
163
|
-
return fresh ? parseDecision(raw) : null;
|
|
164
|
-
} catch {
|
|
165
|
-
return null;
|
|
166
|
-
}
|
|
167
|
-
};
|
|
168
|
-
|
|
169
|
-
// src/blocked-action-hint.ts
|
|
170
|
-
import { createHash as createHash2 } from "crypto";
|
|
171
|
-
import { existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync2, readdirSync as readdirSync2, rmSync as rmSync2, statSync as statSync2 } from "fs";
|
|
172
|
-
import { tmpdir as tmpdir2 } from "os";
|
|
173
|
-
import { join as join2 } from "path";
|
|
174
|
-
var BLOCKED_ACTION_HINT_TTL_MS = BLOCKED_ACTION_GRANT_TTL_SECONDS * 1e3;
|
|
175
|
-
var FINGERPRINT = /^[0-9a-f]{64}$/;
|
|
176
|
-
var isFingerprint = (value) => typeof value === "string" && FINGERPRINT.test(value);
|
|
177
|
-
var blockedActionSubject = (source, sessionId, toolName, toolInput, cwd) => {
|
|
178
|
-
if (source !== "claude") return { source, sessionId, toolName, toolInput };
|
|
179
|
-
return cwd ? { source, sessionId, cwd, toolName, toolInput } : null;
|
|
180
|
-
};
|
|
181
|
-
var blockedActionFingerprint = ({ source = "claude", sessionId, toolName, toolInput, cwd }) => {
|
|
182
|
-
if (!sessionId || !toolInput) return null;
|
|
183
|
-
const canonical = canonicalGateInputs(source, toolName, [toolInput], cwd);
|
|
184
|
-
if (!canonical || canonical.toolInputs.length !== 1) return null;
|
|
185
|
-
const [canonicalInput] = canonical.toolInputs;
|
|
186
|
-
if (!canonicalInput) return null;
|
|
187
|
-
const subject = blockedActionSubject(source, sessionId, canonical.toolName, canonicalInput, cwd);
|
|
188
|
-
return subject ? createHash2("sha256").update(blockedActionIdentity(subject), "utf8").digest("hex") : null;
|
|
189
|
-
};
|
|
190
|
-
var hintDir = () => join2(tmpdir2(), `pushary-blocked-action-hints-${userScope()}`);
|
|
191
|
-
var hintFile = (fingerprint) => join2(hintDir(), fingerprint);
|
|
192
|
-
var isExpired = (mtimeMs, nowMs) => nowMs - mtimeMs >= BLOCKED_ACTION_HINT_TTL_MS;
|
|
193
|
-
var sweepExpired2 = (dir, nowMs) => {
|
|
194
|
-
try {
|
|
195
|
-
for (const name of readdirSync2(dir)) {
|
|
196
|
-
const path = join2(dir, name);
|
|
197
|
-
try {
|
|
198
|
-
if (isExpired(statSync2(path).mtimeMs, nowMs)) rmSync2(path, { force: true });
|
|
199
|
-
} catch {
|
|
200
|
-
continue;
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
} catch {
|
|
204
|
-
return;
|
|
205
|
-
}
|
|
206
|
-
};
|
|
207
|
-
var recordBlockedActionHint = (fingerprint) => {
|
|
208
|
-
if (!isFingerprint(fingerprint)) return false;
|
|
209
|
-
try {
|
|
210
|
-
const dir = hintDir();
|
|
211
|
-
if (!existsSync2(dir)) mkdirSync2(dir, { recursive: true, mode: 448 });
|
|
212
|
-
else sweepExpired2(dir, Date.now());
|
|
213
|
-
writeFileAtomic(hintFile(fingerprint), JSON.stringify({ fingerprint }), 384);
|
|
214
|
-
return true;
|
|
215
|
-
} catch {
|
|
216
|
-
return false;
|
|
217
|
-
}
|
|
218
|
-
};
|
|
219
|
-
var hasBlockedActionHint = (fingerprint) => {
|
|
220
|
-
if (!isFingerprint(fingerprint)) return false;
|
|
221
|
-
const path = hintFile(fingerprint);
|
|
222
|
-
try {
|
|
223
|
-
const stat = statSync2(path);
|
|
224
|
-
if (!stat.isFile()) return false;
|
|
225
|
-
if (isExpired(stat.mtimeMs, Date.now())) {
|
|
226
|
-
rmSync2(path, { force: true });
|
|
227
|
-
return false;
|
|
228
|
-
}
|
|
229
|
-
const parsed = JSON.parse(readFileSync2(path, "utf-8"));
|
|
230
|
-
return parsed?.fingerprint === fingerprint;
|
|
231
|
-
} catch {
|
|
232
|
-
return false;
|
|
233
|
-
}
|
|
234
|
-
};
|
|
235
|
-
var forgetBlockedActionHint = (fingerprint) => {
|
|
236
|
-
if (!isFingerprint(fingerprint)) return;
|
|
237
|
-
try {
|
|
238
|
-
rmSync2(hintFile(fingerprint), { force: true });
|
|
239
|
-
} catch {
|
|
240
|
-
return;
|
|
241
|
-
}
|
|
242
|
-
};
|
|
243
|
-
|
|
244
|
-
export {
|
|
245
|
-
reportMachineDenial,
|
|
246
|
-
defersToNativeMode,
|
|
247
|
-
userScope,
|
|
248
|
-
codexCallKey,
|
|
249
|
-
recordCodexCallDecision,
|
|
250
|
-
consumeCodexCallDecision,
|
|
251
|
-
blockedActionFingerprint,
|
|
252
|
-
recordBlockedActionHint,
|
|
253
|
-
hasBlockedActionHint,
|
|
254
|
-
forgetBlockedActionHint
|
|
255
|
-
};
|