@pushary/agent-hooks 0.49.1 → 0.50.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.
@@ -16,12 +16,12 @@ import {
16
16
  reportEvent,
17
17
  resolvePolicy,
18
18
  waitForAnswer
19
- } from "../chunk-O5MFSRWV.js";
19
+ } from "../chunk-SCXPHMAS.js";
20
20
  import {
21
21
  getMachineId
22
22
  } from "../chunk-RN3NOEJF.js";
23
23
  import "../chunk-DWED7BS3.js";
24
- import "../chunk-Z5PL3K7C.js";
24
+ import "../chunk-WI3F6XNZ.js";
25
25
  import {
26
26
  getApiKey,
27
27
  getBaseUrl
@@ -343,7 +343,7 @@ var createRemoteApprover = (deps) => {
343
343
  const sessionId = deps.getSessionId?.();
344
344
  const modeState = await fetchModeState2(deps.apiKey, sessionId);
345
345
  if (modeState.kill) return deny("Stopped by user \u2014 this agent was halted from Pushary");
346
- const policyConfig = await getPolicy2(deps.apiKey, modeState.policyVersion);
346
+ const policyConfig = await getPolicy2(deps.apiKey, modeState.policyVersion, "claude_code");
347
347
  const policy = resolvePolicy2(policyConfig, toolName, modeState.mode, input);
348
348
  if (policy.timeoutSeconds === 0 && policy.timeoutAction === "approve") return allow(input);
349
349
  if (policy.timeoutSeconds === 0 && policy.timeoutAction === "deny") {
@@ -6,15 +6,15 @@ import {
6
6
  removeCodexHooks,
7
7
  removeGeminiSettings,
8
8
  removeInstructionBlock
9
- } from "../chunk-SVVEC4QV.js";
9
+ } from "../chunk-FDRSMTJS.js";
10
10
  import {
11
11
  execNpm
12
12
  } from "../chunk-J7JWI3KU.js";
13
13
  import {
14
14
  removeClaudeMcpServers,
15
15
  removePusharySettings
16
- } from "../chunk-7EW3USQF.js";
17
- import "../chunk-Z5PL3K7C.js";
16
+ } from "../chunk-PUT2N5VA.js";
17
+ import "../chunk-WI3F6XNZ.js";
18
18
 
19
19
  // bin/pushary-clean.ts
20
20
  import { existsSync, readFileSync, writeFileSync, rmSync, readdirSync } from "fs";
@@ -31,21 +31,21 @@ import {
31
31
  toCodexWire,
32
32
  toPolicyLookup,
33
33
  waitForAnswer
34
- } from "../chunk-O5MFSRWV.js";
34
+ } from "../chunk-SCXPHMAS.js";
35
35
  import {
36
36
  getMachineId
37
37
  } from "../chunk-RN3NOEJF.js";
38
38
  import {
39
39
  isGatingMoment,
40
40
  recordKeylessMoment
41
- } from "../chunk-R5AJNXZS.js";
41
+ } from "../chunk-V42K3XQI.js";
42
42
  import "../chunk-DWED7BS3.js";
43
43
  import {
44
44
  DECISION_LINE_MAX,
45
45
  effectiveWaitSeconds,
46
46
  hookWaitClamped,
47
47
  hookWaitDeadline
48
- } from "../chunk-Z5PL3K7C.js";
48
+ } from "../chunk-WI3F6XNZ.js";
49
49
  import {
50
50
  getApiKey
51
51
  } from "../chunk-NKXSILEW.js";
@@ -170,7 +170,7 @@ var decidePermissionRequest = async (input) => {
170
170
  const modeState = await fetchModeState(apiKey, input.session_id);
171
171
  if (modeState.kill) return codexDeny(KILL_REASON);
172
172
  const lookup = toPolicyLookup(input.tool_name ?? "", input.tool_input ?? {}, input.cwd);
173
- const policy = await getPolicy(apiKey, modeState.policyVersion);
173
+ const policy = await getPolicy(apiKey, modeState.policyVersion, "codex");
174
174
  const toolPolicy = resolvePolicy(policy, lookup.tool, modeState.mode, lookup.input);
175
175
  if (toolPolicy.timeoutSeconds === 0 && toolPolicy.timeoutAction === "approve") return codexAllow();
176
176
  if (toolPolicy.timeoutSeconds === 0 && toolPolicy.timeoutAction === "deny") {
@@ -229,7 +229,7 @@ var decidePreToolUse = async (input) => {
229
229
  const modeState = await fetchModeState(apiKey, input.session_id);
230
230
  if (modeState.kill) return codexDeny(KILL_REASON);
231
231
  const lookup = toPolicyLookup(input.tool_name ?? "", input.tool_input ?? {}, input.cwd);
232
- const policy = await getPolicy(apiKey, modeState.policyVersion);
232
+ const policy = await getPolicy(apiKey, modeState.policyVersion, "codex");
233
233
  const toolPolicy = resolvePolicy(policy, lookup.tool, modeState.mode, lookup.input);
234
234
  if (toolPolicy.timeoutSeconds === 0 && toolPolicy.timeoutAction === "approve") return codexPass();
235
235
  if (toolPolicy.timeoutSeconds === 0 && toolPolicy.timeoutAction === "deny") {
@@ -3,12 +3,12 @@ import {
3
3
  askUser,
4
4
  reportEvent,
5
5
  waitForAnswer
6
- } from "../chunk-O5MFSRWV.js";
6
+ } from "../chunk-SCXPHMAS.js";
7
7
  import {
8
8
  getMachineId
9
9
  } from "../chunk-RN3NOEJF.js";
10
10
  import "../chunk-DWED7BS3.js";
11
- import "../chunk-Z5PL3K7C.js";
11
+ import "../chunk-WI3F6XNZ.js";
12
12
  import {
13
13
  getApiKey
14
14
  } from "../chunk-NKXSILEW.js";
@@ -11,18 +11,18 @@ import {
11
11
  missingGeminiHookEvents,
12
12
  readCodexMcpAuth,
13
13
  untrustedCodexHookEvents
14
- } from "../chunk-SVVEC4QV.js";
14
+ } from "../chunk-FDRSMTJS.js";
15
15
  import {
16
16
  execNpm
17
17
  } from "../chunk-J7JWI3KU.js";
18
18
  import {
19
19
  readLedgerSummary
20
- } from "../chunk-R5AJNXZS.js";
20
+ } from "../chunk-V42K3XQI.js";
21
21
  import {
22
22
  callMcpTool,
23
23
  sendMcpRequest
24
24
  } from "../chunk-DWED7BS3.js";
25
- import "../chunk-Z5PL3K7C.js";
25
+ import "../chunk-WI3F6XNZ.js";
26
26
  import {
27
27
  getBaseUrl
28
28
  } from "../chunk-NKXSILEW.js";
@@ -23,21 +23,21 @@ import {
23
23
  savePendingQuestion,
24
24
  sendNotification,
25
25
  waitForAnswer
26
- } from "../chunk-O5MFSRWV.js";
26
+ } from "../chunk-SCXPHMAS.js";
27
27
  import {
28
28
  getMachineId
29
29
  } from "../chunk-RN3NOEJF.js";
30
30
  import {
31
31
  isGatingMoment,
32
32
  recordKeylessMoment
33
- } from "../chunk-R5AJNXZS.js";
33
+ } from "../chunk-V42K3XQI.js";
34
34
  import "../chunk-DWED7BS3.js";
35
35
  import {
36
36
  DECISION_LINE_MAX,
37
37
  effectiveWaitSeconds,
38
38
  hookWaitClamped,
39
39
  hookWaitDeadline
40
- } from "../chunk-Z5PL3K7C.js";
40
+ } from "../chunk-WI3F6XNZ.js";
41
41
  import {
42
42
  getApiKey
43
43
  } from "../chunk-NKXSILEW.js";
@@ -209,7 +209,7 @@ var decideBeforeTool = async (input) => {
209
209
  const modeState = await fetchModeState(apiKey, input.session_id);
210
210
  if (modeState.kill) return geminiDeny(KILL_REASON);
211
211
  const lookup = toGeminiPolicyLookup(input.tool_name ?? "", input.tool_input ?? {});
212
- const policy = await getPolicy(apiKey, modeState.policyVersion);
212
+ const policy = await getPolicy(apiKey, modeState.policyVersion, "gemini_cli");
213
213
  const toolPolicy = resolvePolicy(policy, lookup.tool, modeState.mode, lookup.input);
214
214
  if (toolPolicy.timeoutSeconds === 0 && toolPolicy.timeoutAction === "approve") return geminiAllow();
215
215
  if (toolPolicy.timeoutSeconds === 0 && toolPolicy.timeoutAction === "deny") {
@@ -1,14 +1,14 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  handlePreToolUse
4
- } from "../chunk-OQMEGREX.js";
5
- import "../chunk-7EW3USQF.js";
4
+ } from "../chunk-M24KUFLW.js";
5
+ import "../chunk-PUT2N5VA.js";
6
6
  import "../chunk-KQYIHZ5E.js";
7
- import "../chunk-O5MFSRWV.js";
7
+ import "../chunk-SCXPHMAS.js";
8
8
  import "../chunk-RN3NOEJF.js";
9
- import "../chunk-R5AJNXZS.js";
9
+ import "../chunk-V42K3XQI.js";
10
10
  import "../chunk-DWED7BS3.js";
11
- import "../chunk-Z5PL3K7C.js";
11
+ import "../chunk-WI3F6XNZ.js";
12
12
  import "../chunk-NKXSILEW.js";
13
13
 
14
14
  // bin/pushary-hook.ts
@@ -1,10 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  handleNotification
4
- } from "../chunk-O5MFSRWV.js";
4
+ } from "../chunk-SCXPHMAS.js";
5
5
  import "../chunk-RN3NOEJF.js";
6
6
  import "../chunk-DWED7BS3.js";
7
- import "../chunk-Z5PL3K7C.js";
7
+ import "../chunk-WI3F6XNZ.js";
8
8
  import "../chunk-NKXSILEW.js";
9
9
 
10
10
  // bin/pushary-notification-hook.ts
@@ -1,14 +1,14 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  handlePermissionDenied
4
- } from "../chunk-OQMEGREX.js";
5
- import "../chunk-7EW3USQF.js";
4
+ } from "../chunk-M24KUFLW.js";
5
+ import "../chunk-PUT2N5VA.js";
6
6
  import "../chunk-KQYIHZ5E.js";
7
- import "../chunk-O5MFSRWV.js";
7
+ import "../chunk-SCXPHMAS.js";
8
8
  import "../chunk-RN3NOEJF.js";
9
- import "../chunk-R5AJNXZS.js";
9
+ import "../chunk-V42K3XQI.js";
10
10
  import "../chunk-DWED7BS3.js";
11
- import "../chunk-Z5PL3K7C.js";
11
+ import "../chunk-WI3F6XNZ.js";
12
12
  import "../chunk-NKXSILEW.js";
13
13
 
14
14
  // bin/pushary-permission-denied-hook.ts
@@ -1,14 +1,14 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  handlePermissionRequest
4
- } from "../chunk-OQMEGREX.js";
5
- import "../chunk-7EW3USQF.js";
4
+ } from "../chunk-M24KUFLW.js";
5
+ import "../chunk-PUT2N5VA.js";
6
6
  import "../chunk-KQYIHZ5E.js";
7
- import "../chunk-O5MFSRWV.js";
7
+ import "../chunk-SCXPHMAS.js";
8
8
  import "../chunk-RN3NOEJF.js";
9
- import "../chunk-R5AJNXZS.js";
9
+ import "../chunk-V42K3XQI.js";
10
10
  import "../chunk-DWED7BS3.js";
11
- import "../chunk-Z5PL3K7C.js";
11
+ import "../chunk-WI3F6XNZ.js";
12
12
  import "../chunk-NKXSILEW.js";
13
13
 
14
14
  // bin/pushary-permission-hook.ts
@@ -1,10 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  handlePostToolUse
4
- } from "../chunk-O5MFSRWV.js";
4
+ } from "../chunk-SCXPHMAS.js";
5
5
  import "../chunk-RN3NOEJF.js";
6
6
  import "../chunk-DWED7BS3.js";
7
- import "../chunk-Z5PL3K7C.js";
7
+ import "../chunk-WI3F6XNZ.js";
8
8
  import "../chunk-NKXSILEW.js";
9
9
 
10
10
  // bin/pushary-post-hook.ts
@@ -1,10 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  handleUserPrompt
4
- } from "../chunk-O5MFSRWV.js";
4
+ } from "../chunk-SCXPHMAS.js";
5
5
  import "../chunk-RN3NOEJF.js";
6
6
  import "../chunk-DWED7BS3.js";
7
- import "../chunk-Z5PL3K7C.js";
7
+ import "../chunk-WI3F6XNZ.js";
8
8
  import "../chunk-NKXSILEW.js";
9
9
 
10
10
  // bin/pushary-prompt-hook.ts
@@ -1,10 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  handleSessionEnd
4
- } from "../chunk-O5MFSRWV.js";
4
+ } from "../chunk-SCXPHMAS.js";
5
5
  import "../chunk-RN3NOEJF.js";
6
6
  import "../chunk-DWED7BS3.js";
7
- import "../chunk-Z5PL3K7C.js";
7
+ import "../chunk-WI3F6XNZ.js";
8
8
  import "../chunk-NKXSILEW.js";
9
9
 
10
10
  // bin/pushary-session-end-hook.ts
@@ -1,10 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  handleSessionStart
4
- } from "../chunk-O5MFSRWV.js";
4
+ } from "../chunk-SCXPHMAS.js";
5
5
  import "../chunk-RN3NOEJF.js";
6
6
  import "../chunk-DWED7BS3.js";
7
- import "../chunk-Z5PL3K7C.js";
7
+ import "../chunk-WI3F6XNZ.js";
8
8
  import "../chunk-NKXSILEW.js";
9
9
 
10
10
  // bin/pushary-session-start-hook.ts
@@ -13,7 +13,7 @@ import {
13
13
  renderAgentInstructions,
14
14
  renderProjectAgentInstructions,
15
15
  writeInstructionBlock
16
- } from "../chunk-SVVEC4QV.js";
16
+ } from "../chunk-FDRSMTJS.js";
17
17
  import {
18
18
  execNpm,
19
19
  npmErrorMessage,
@@ -25,15 +25,15 @@ import {
25
25
  addClaudeMcpServer,
26
26
  addPusharyHooks,
27
27
  addPusharyToolPermissions
28
- } from "../chunk-7EW3USQF.js";
28
+ } from "../chunk-PUT2N5VA.js";
29
29
  import {
30
30
  reportEvent
31
- } from "../chunk-O5MFSRWV.js";
31
+ } from "../chunk-SCXPHMAS.js";
32
32
  import "../chunk-RN3NOEJF.js";
33
33
  import "../chunk-DWED7BS3.js";
34
34
  import {
35
35
  isValidApiKey
36
- } from "../chunk-Z5PL3K7C.js";
36
+ } from "../chunk-WI3F6XNZ.js";
37
37
  import "../chunk-NKXSILEW.js";
38
38
 
39
39
  // bin/pushary-setup.ts
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  readLedgerSummary
4
- } from "../chunk-R5AJNXZS.js";
5
- import "../chunk-Z5PL3K7C.js";
4
+ } from "../chunk-V42K3XQI.js";
5
+ import "../chunk-WI3F6XNZ.js";
6
6
  import {
7
7
  getApiKey
8
8
  } from "../chunk-NKXSILEW.js";
@@ -1,10 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  handleStop
4
- } from "../chunk-O5MFSRWV.js";
4
+ } from "../chunk-SCXPHMAS.js";
5
5
  import "../chunk-RN3NOEJF.js";
6
6
  import "../chunk-DWED7BS3.js";
7
- import "../chunk-Z5PL3K7C.js";
7
+ import "../chunk-WI3F6XNZ.js";
8
8
  import "../chunk-NKXSILEW.js";
9
9
 
10
10
  // bin/pushary-stop-hook.ts
@@ -1,10 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  handleStopFailure
4
- } from "../chunk-O5MFSRWV.js";
4
+ } from "../chunk-SCXPHMAS.js";
5
5
  import "../chunk-RN3NOEJF.js";
6
6
  import "../chunk-DWED7BS3.js";
7
- import "../chunk-Z5PL3K7C.js";
7
+ import "../chunk-WI3F6XNZ.js";
8
8
  import "../chunk-NKXSILEW.js";
9
9
 
10
10
  // bin/pushary-stopfailure-hook.ts
@@ -8,8 +8,8 @@ import {
8
8
  addClaudeMcpServer,
9
9
  addPusharyHooks,
10
10
  addPusharyToolPermissions
11
- } from "../chunk-7EW3USQF.js";
12
- import "../chunk-Z5PL3K7C.js";
11
+ } from "../chunk-PUT2N5VA.js";
12
+ import "../chunk-WI3F6XNZ.js";
13
13
  import {
14
14
  getApiKey
15
15
  } from "../chunk-NKXSILEW.js";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  HOOK_BUDGETS
3
- } from "./chunk-Z5PL3K7C.js";
3
+ } from "./chunk-WI3F6XNZ.js";
4
4
 
5
5
  // src/codex-config.ts
6
6
  import { createHash } from "crypto";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  PRETOOLUSE_HANDLED_TOOLS
3
- } from "./chunk-7EW3USQF.js";
3
+ } from "./chunk-PUT2N5VA.js";
4
4
  import {
5
5
  denyReasonFrom,
6
6
  isDeferAnswer
@@ -23,19 +23,19 @@ import {
23
23
  sendNotification,
24
24
  throttlePass,
25
25
  waitForAnswer
26
- } from "./chunk-O5MFSRWV.js";
26
+ } from "./chunk-SCXPHMAS.js";
27
27
  import {
28
28
  getMachineId
29
29
  } from "./chunk-RN3NOEJF.js";
30
30
  import {
31
31
  isGatingMoment,
32
32
  recordKeylessMoment
33
- } from "./chunk-R5AJNXZS.js";
33
+ } from "./chunk-V42K3XQI.js";
34
34
  import {
35
35
  effectiveWaitSeconds,
36
36
  hookWaitClamped,
37
37
  hookWaitDeadline
38
- } from "./chunk-Z5PL3K7C.js";
38
+ } from "./chunk-WI3F6XNZ.js";
39
39
  import {
40
40
  getApiKey
41
41
  } from "./chunk-NKXSILEW.js";
@@ -322,7 +322,7 @@ var handlePreToolUse = async (input) => {
322
322
  }
323
323
  try {
324
324
  const modeState = await fetchModeState(apiKey, input.session_id);
325
- const policy = await getPolicy(apiKey, modeState.policyVersion);
325
+ const policy = await getPolicy(apiKey, modeState.policyVersion, "claude_code");
326
326
  if (modeState.kill) {
327
327
  return deny("Stopped by user \u2014 this agent was halted from Pushary");
328
328
  }
@@ -374,7 +374,7 @@ var handlePermissionRequest = async (input) => {
374
374
  }
375
375
  try {
376
376
  const modeState = await fetchModeState(apiKey, input.session_id);
377
- const policy = await getPolicy(apiKey, modeState.policyVersion);
377
+ const policy = await getPolicy(apiKey, modeState.policyVersion, "claude_code");
378
378
  if (modeState.kill) return permReqDeny("Stopped by user \u2014 this agent was halted from Pushary");
379
379
  const toolPolicy = resolvePolicy(policy, input.tool_name, modeState.mode, input.tool_input);
380
380
  if (toolPolicy.timeoutSeconds === 0 && toolPolicy.timeoutAction === "approve") return permReqAllow();
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  HOOK_BUDGETS
3
- } from "./chunk-Z5PL3K7C.js";
3
+ } from "./chunk-WI3F6XNZ.js";
4
4
 
5
5
  // src/claude-config.ts
6
6
  import { join } from "path";
@@ -8,6 +8,7 @@ import {
8
8
  import {
9
9
  ACTION_BODY_MAX,
10
10
  DECISION_LINE_MAX,
11
+ classifyDecisionRisk,
11
12
  extractPolicyArg,
12
13
  isApprovalMode,
13
14
  isSafeReadOnlyCommand,
@@ -15,7 +16,7 @@ import {
15
16
  matchToolPattern,
16
17
  redactSecrets,
17
18
  redactSecretsDeep
18
- } from "./chunk-Z5PL3K7C.js";
19
+ } from "./chunk-WI3F6XNZ.js";
19
20
  import {
20
21
  getApiKey,
21
22
  getBaseUrl
@@ -46,14 +47,15 @@ var isWaitForAnswerResponse = (data) => {
46
47
 
47
48
  // src/policy.ts
48
49
  var CACHE_TTL_MS = 60 * 1e3;
49
- var cacheFile = (apiKey) => {
50
- const hash = createHash("sha256").update(apiKey).digest("hex").slice(0, 12);
50
+ var cacheFile = (apiKey, agent) => {
51
+ const hash = createHash("sha256").update(agent ? `${apiKey}:${agent}` : apiKey).digest("hex").slice(0, 12);
51
52
  return join(tmpdir(), `pushary-policy-${hash}.json`);
52
53
  };
53
- var fetchPolicy = async (apiKey) => {
54
+ var fetchPolicy = async (apiKey, agent) => {
54
55
  return withRetry(async () => {
55
56
  const baseUrl = getBaseUrl();
56
- const response = await fetch(`${baseUrl}/api/mcp/policy`, {
57
+ const url = agent ? `${baseUrl}/api/mcp/policy?agent=${encodeURIComponent(agent)}` : `${baseUrl}/api/mcp/policy`;
58
+ const response = await fetch(url, {
57
59
  headers: { "Authorization": `Bearer ${apiKey}` },
58
60
  signal: AbortSignal.timeout(1e4)
59
61
  });
@@ -65,8 +67,8 @@ var fetchPolicy = async (apiKey) => {
65
67
  return raw;
66
68
  }, { maxAttempts: 2 });
67
69
  };
68
- var getPolicy = async (apiKey, expectedVersion) => {
69
- const path = cacheFile(apiKey);
70
+ var getPolicy = async (apiKey, expectedVersion, agent) => {
71
+ const path = cacheFile(apiKey, agent);
70
72
  let staleCache = null;
71
73
  if (existsSync(path)) {
72
74
  try {
@@ -83,7 +85,7 @@ var getPolicy = async (apiKey, expectedVersion) => {
83
85
  }
84
86
  }
85
87
  try {
86
- const policy = await fetchPolicy(apiKey);
88
+ const policy = await fetchPolicy(apiKey, agent);
87
89
  try {
88
90
  writeFileSync(path, JSON.stringify({ ...policy, _cachedAt: Date.now(), _policyVersion: expectedVersion ?? null }), "utf-8");
89
91
  } catch {
@@ -141,6 +143,16 @@ var resolvePolicy = (config, toolName, modeOverride, toolInput) => {
141
143
  if (!governedBySpecificRule && toolName === "Bash" && typeof arg === "string" && isSafeReadOnlyCommand(arg)) {
142
144
  base = autoApprove(base.tool);
143
145
  }
146
+ const wouldAutoApprove = base.timeoutSeconds === 0 && base.timeoutAction === "approve";
147
+ if (!governedBySpecificRule && wouldAutoApprove && toolName === "Bash" && typeof arg === "string" && classifyDecisionRisk(toolName, arg) === "destructive") {
148
+ base = {
149
+ tool: base.tool,
150
+ timeoutSeconds: config.defaultTimeoutSeconds,
151
+ timeoutAction: "wait",
152
+ mode: "push_first",
153
+ pushFirstSeconds: config.defaultPushFirstSeconds ?? 20
154
+ };
155
+ }
144
156
  const effectiveOverride = modeOverride ?? config.modeOverride;
145
157
  if (effectiveOverride) {
146
158
  return { ...base, mode: effectiveOverride };
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  isSafeReadOnlyCommand
3
- } from "./chunk-Z5PL3K7C.js";
3
+ } from "./chunk-WI3F6XNZ.js";
4
4
 
5
5
  // src/ledger.ts
6
6
  import { appendFileSync, existsSync, mkdirSync, readFileSync, renameSync, rmSync, statSync } from "fs";
@@ -279,6 +279,31 @@ var API_KEY_PATTERN = /^pk_[a-f0-9]+\.[a-f0-9]+$/;
279
279
  var isValidApiKey = (value) => API_KEY_PATTERN.test(value);
280
280
  var ACTION_BODY_MAX = 4e3;
281
281
  var DECISION_LINE_MAX = 500;
282
+ var APPROVAL_FATIGUE_FLAG_PATTERNS = [
283
+ /\brm\s+-[a-z]*r/i,
284
+ /\brmdir\b/i,
285
+ /git\s+push[^\n]*(--force|--force-with-lease|\s-f\b)/i,
286
+ /git\s+reset\s+--hard/i,
287
+ /git\s+clean\s+-[a-z]*f/i,
288
+ /\bdrop\s+(table|database|schema)\b/i,
289
+ /\bdelete\s+from\b/i,
290
+ /\btruncate\b/i,
291
+ /\b(deploy|publish|release)\b/i,
292
+ /\bnpm\s+publish\b/i,
293
+ /\bsudo\b/i,
294
+ /chmod\s+-?R?\s*777/i,
295
+ /\bkubectl\s+delete\b/i,
296
+ /\bterraform\s+(apply|destroy)\b/i,
297
+ /\bdocker\s+system\s+prune\b/i
298
+ ];
299
+ var HIGH_CONSEQUENCE_PATTERNS = [
300
+ /(^|[\\/])\.env(\.|$|[\\/])/i,
301
+ /secrets?/i,
302
+ /credentials?/i,
303
+ /(^|[\\/])deploy/i,
304
+ /\.github[\\/]workflows/i,
305
+ /package\.json/i
306
+ ];
282
307
  var SECRET_REDACTION_RULES = [
283
308
  { pattern: /-----BEGIN[A-Z0-9 ]*PRIVATE KEY-----[\s\S]*?-----END[A-Z0-9 ]*PRIVATE KEY-----/g, replacement: "[redacted key]" },
284
309
  { pattern: /\bsk-[A-Za-z0-9_-]{16,}\b/g, replacement: "[redacted]" },
@@ -303,6 +328,13 @@ var SECRET_REDACTION_RULES = [
303
328
  var HIGH_ENTROPY_RULE = { pattern: /[A-Za-z0-9+/]{40,}={0,2}/g, replacement: "[redacted]" };
304
329
  var redactSecrets = (text) => SECRET_REDACTION_RULES.reduce((acc, rule) => acc.replace(rule.pattern, rule.replacement), text);
305
330
  var redactSecretsDeep = (text) => redactSecrets(text).replace(HIGH_ENTROPY_RULE.pattern, HIGH_ENTROPY_RULE.replacement);
331
+ var classifyDecisionRisk = (toolName, toolTarget) => {
332
+ const parts = [toolName, toolTarget ?? ""];
333
+ const matchesAny = (patterns) => parts.some((part) => patterns.some((re) => re.test(part)));
334
+ if (matchesAny(APPROVAL_FATIGUE_FLAG_PATTERNS)) return "destructive";
335
+ if (matchesAny(HIGH_CONSEQUENCE_PATTERNS)) return "elevated";
336
+ return "standard";
337
+ };
306
338
 
307
339
  export {
308
340
  isApprovalMode,
@@ -318,5 +350,6 @@ export {
318
350
  ACTION_BODY_MAX,
319
351
  DECISION_LINE_MAX,
320
352
  redactSecrets,
321
- redactSecretsDeep
353
+ redactSecretsDeep,
354
+ classifyDecisionRisk
322
355
  };
@@ -26,7 +26,7 @@ interface ReceiptMeta {
26
26
  ok: boolean;
27
27
  }
28
28
 
29
- declare const getPolicy: (apiKey: string, expectedVersion?: string | null) => Promise<PolicyConfig>;
29
+ declare const getPolicy: (apiKey: string, expectedVersion?: string | null, agent?: string) => Promise<PolicyConfig>;
30
30
  type AutoResolveOrigin = 'safe_readonly' | 'policy_timeout';
31
31
  declare const resolvePolicy: (config: PolicyConfig, toolName: string, modeOverride?: ApprovalMode | null, toolInput?: Record<string, unknown>) => ToolPolicy;
32
32
  interface ModeState {
package/dist/src/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  handlePreToolUse
3
- } from "../chunk-OQMEGREX.js";
4
- import "../chunk-7EW3USQF.js";
3
+ } from "../chunk-M24KUFLW.js";
4
+ import "../chunk-PUT2N5VA.js";
5
5
  import "../chunk-KQYIHZ5E.js";
6
6
  import {
7
7
  askUser,
@@ -15,11 +15,11 @@ import {
15
15
  reportEvent,
16
16
  resolvePolicy,
17
17
  waitForAnswer
18
- } from "../chunk-O5MFSRWV.js";
18
+ } from "../chunk-SCXPHMAS.js";
19
19
  import "../chunk-RN3NOEJF.js";
20
- import "../chunk-R5AJNXZS.js";
20
+ import "../chunk-V42K3XQI.js";
21
21
  import "../chunk-DWED7BS3.js";
22
- import "../chunk-Z5PL3K7C.js";
22
+ import "../chunk-WI3F6XNZ.js";
23
23
  import {
24
24
  getApiKey,
25
25
  getBaseUrl
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pushary/agent-hooks",
3
- "version": "0.49.1",
3
+ "version": "0.50.0",
4
4
  "description": "Permission hooks for AI coding agents: route tool approvals through Pushary push notifications",
5
5
  "keywords": [
6
6
  "pushary",
@@ -73,12 +73,13 @@
73
73
  "scripts": {
74
74
  "build": "node scripts/bundle-plugin.mjs && tsup",
75
75
  "dev": "tsup --watch",
76
- "test": "bun test src/api.test.ts && bun test src/claude-config.test.ts && bun test src/config.test.ts && bun test src/mcp-http.test.ts && bun test src/retry.test.ts && bun test src/usage.test.ts && bun test src/validate.test.ts && bun test src/policy.test.ts && bun test src/npm.test.ts && bun test src/identity.test.ts && bun test src/pending.test.ts && bun test src/events.test.ts && bun test src/describe.test.ts && bun test src/reapply.test.ts && bun test src/suggestions.test.ts && bun test src/safe-commands.test.ts && bun test src/hook.test.ts && bun test src/codex-adapter.test.ts && bun test src/codex-config.test.ts && bun test src/gemini-adapter.test.ts && bun test src/gemini-config.test.ts && bun test src/instruction-file.test.ts && bun test src/pairing.test.ts && bun test src/wait-ladder.test.ts && bun test src/ledger.test.ts && bun test src/wrapper/wrapper.test.ts && bun test src/wrapper/messageQueue.test.ts && bun test src/wrapper/approver.test.ts && bun test src/wrapper/remoteLoop.test.ts && bun test src/wrapper/spawnClaude.test.ts && bun test src/wrapper/wsProtocol.test.ts && bun test src/wrapper/relayClient.test.ts && bun test src/wrapper/modeLoop.test.ts && bun test src/wrapper/stdinHandoff.test.ts && bun test src/wrapper/localLeg.test.ts"
76
+ "test": "bun test src/api.test.ts && bun test src/claude-config.test.ts && bun test src/config.test.ts && bun test src/mcp-http.test.ts && bun test src/retry.test.ts && bun test src/usage.test.ts && bun test src/validate.test.ts && bun test src/policy.test.ts && bun test src/npm.test.ts && bun test src/identity.test.ts && bun test src/pending.test.ts && bun test src/events.test.ts && bun test src/describe.test.ts && bun test src/reapply.test.ts && bun test src/suggestions.test.ts && bun test src/safe-commands.test.ts && bun test src/hook.test.ts && bun test src/codex-adapter.test.ts && bun test src/codex-config.test.ts && bun test src/gemini-adapter.test.ts && bun test src/gemini-config.test.ts && bun test src/instruction-file.test.ts && bun test src/pairing.test.ts && bun test src/wait-ladder.test.ts && bun test src/ledger.test.ts && bun test src/wrapper/wrapper.test.ts && bun test src/wrapper/messageQueue.test.ts && bun test src/wrapper/approver.test.ts && bun test src/wrapper/remoteLoop.test.ts && bun test src/wrapper/spawnClaude.test.ts && bun test src/wrapper/wsProtocol.test.ts && bun test src/wrapper/relayClient.test.ts && bun test src/wrapper/modeLoop.test.ts && bun test src/wrapper/stdinHandoff.test.ts && bun test src/wrapper/localLeg.test.ts && bun test src/crypto.test.ts && bun test src/transcript-capture.test.ts"
77
77
  },
78
78
  "dependencies": {
79
79
  "@inquirer/prompts": "^8.4.2",
80
80
  "qrcode-terminal": "^0.12.0",
81
- "smol-toml": "^1.6.1"
81
+ "smol-toml": "^1.6.1",
82
+ "tweetnacl": "^1.0.3"
82
83
  },
83
84
  "devDependencies": {
84
85
  "@pushary/contracts": "workspace:*",