@pushary/agent-hooks 1.3.1 → 1.4.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.
Files changed (84) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/README.md +1 -1
  3. package/dist/bin/pushary-bell-hook.js +18 -18
  4. package/dist/bin/pushary-bell.js +8 -8
  5. package/dist/bin/pushary-claude.js +18 -18
  6. package/dist/bin/pushary-clean.js +470 -92
  7. package/dist/bin/pushary-codex-bridge.js +10 -10
  8. package/dist/bin/pushary-codex-hook.js +28 -26
  9. package/dist/bin/pushary-codex.js +6 -6
  10. package/dist/bin/pushary-connect.js +8 -8
  11. package/dist/bin/pushary-cowork.js +5 -5
  12. package/dist/bin/pushary-daemon.js +24 -24
  13. package/dist/bin/pushary-disconnect.js +12 -12
  14. package/dist/bin/pushary-doctor.js +75 -26
  15. package/dist/bin/pushary-elicitation-hook.js +17 -17
  16. package/dist/bin/pushary-gemini-bridge.js +9 -9
  17. package/dist/bin/pushary-gemini-hook.js +19 -19
  18. package/dist/bin/pushary-hook.js +27 -27
  19. package/dist/bin/pushary-login.js +9 -9
  20. package/dist/bin/pushary-logout.js +6 -6
  21. package/dist/bin/pushary-mode.js +5 -5
  22. package/dist/bin/pushary-notification-hook.js +20 -20
  23. package/dist/bin/pushary-opencode-hook.js +19 -19
  24. package/dist/bin/pushary-permission-denied-hook.js +24 -24
  25. package/dist/bin/pushary-permission-hook.js +25 -25
  26. package/dist/bin/pushary-post-hook.js +20 -20
  27. package/dist/bin/pushary-prompt-hook.js +20 -20
  28. package/dist/bin/pushary-session-end-hook.js +20 -20
  29. package/dist/bin/pushary-session-start-hook.js +22 -22
  30. package/dist/bin/pushary-setup.js +38 -59
  31. package/dist/bin/pushary-stats.js +4 -4
  32. package/dist/bin/pushary-status.js +12 -12
  33. package/dist/bin/pushary-stop-hook.js +20 -20
  34. package/dist/bin/pushary-stopfailure-hook.js +20 -20
  35. package/dist/bin/pushary-suggestions.js +2 -2
  36. package/dist/bin/pushary-transcript-register.js +16 -16
  37. package/dist/bin/pushary-transcripts.js +6 -6
  38. package/dist/bin/pushary-upgrade.js +16 -16
  39. package/dist/bin/pushary-wait.js +5 -5
  40. package/dist/bin/pushary.js +1 -1
  41. package/dist/{chunk-QDYJQLXT.js → chunk-2TOTCUJC.js} +2 -2
  42. package/dist/{chunk-I2OLM3DM.js → chunk-43Z3RLS5.js} +62 -12
  43. package/dist/{chunk-OMP3KDDU.js → chunk-5F5QQMCA.js} +1 -1
  44. package/dist/{chunk-SGZE2JBF.js → chunk-5IBVIMWT.js} +3 -3
  45. package/dist/{chunk-FGNABUPN.js → chunk-5MSBI6NP.js} +1 -1
  46. package/dist/{chunk-5UWDYOZX.js → chunk-7IEB2GV4.js} +80 -8
  47. package/dist/{chunk-EAGMQMRM.js → chunk-7PQZYCEP.js} +1 -1
  48. package/dist/{chunk-KHGIM2BN.js → chunk-A7XIL2WD.js} +3 -3
  49. package/dist/{chunk-YXWM4TNX.js → chunk-BYAXXBNO.js} +1 -1
  50. package/dist/{chunk-TW7KA27C.js → chunk-D4TNSUDY.js} +1 -1
  51. package/dist/{chunk-P232YODQ.js → chunk-ESLLVHDY.js} +6 -6
  52. package/dist/{chunk-LZQVAEWP.js → chunk-FAH3D74E.js} +2 -1
  53. package/dist/{chunk-XOUQUZ43.js → chunk-GIXLZFEY.js} +1 -1
  54. package/dist/{chunk-PHXU4NOP.js → chunk-GLPWCQ6V.js} +2 -2
  55. package/dist/{chunk-NDSOOJDX.js → chunk-GS5EI2YL.js} +1 -1
  56. package/dist/{chunk-R5CU7ED6.js → chunk-H25UB3CG.js} +1 -1
  57. package/dist/{chunk-LWT34GSY.js → chunk-HGN6VZTY.js} +7 -7
  58. package/dist/{chunk-WOBHHXW5.js → chunk-IF46POXX.js} +5 -4
  59. package/dist/{chunk-HCTSTMDD.js → chunk-JCWCTBYH.js} +3 -3
  60. package/dist/{chunk-XP2KRTZD.js → chunk-KZIXHLPQ.js} +1 -1
  61. package/dist/{chunk-AFOTOA24.js → chunk-NBCTKC4Z.js} +34 -3
  62. package/dist/{chunk-5SALHATY.js → chunk-NYHNLMQO.js} +11 -2
  63. package/dist/{chunk-PEV6ULSM.js → chunk-O5L2OJBB.js} +3 -3
  64. package/dist/{chunk-QOCWB4Q6.js → chunk-O6RN46OS.js} +3 -3
  65. package/dist/{chunk-5WLH2ENB.js → chunk-P4JVVJ2D.js} +1 -1
  66. package/dist/{chunk-XVE52YFZ.js → chunk-Q4UAMWXK.js} +1 -1
  67. package/dist/{chunk-6HH4IH4K.js → chunk-RGZY77CD.js} +3 -3
  68. package/dist/{chunk-465UENKO.js → chunk-RQM5SMMR.js} +1 -1
  69. package/dist/{chunk-RERJ2ZDV.js → chunk-S3CLZSV3.js} +5 -5
  70. package/dist/{chunk-CDSL3LLO.js → chunk-S3EUZZJB.js} +1 -1
  71. package/dist/{chunk-AFKOWCMQ.js → chunk-SGZ3IAWF.js} +1 -1
  72. package/dist/{chunk-GOQHN2UH.js → chunk-TWWLVOPB.js} +1 -1
  73. package/dist/{chunk-TGG4FWWB.js → chunk-U4FB623J.js} +2 -2
  74. package/dist/{chunk-34NNTYK6.js → chunk-UCGQEAKZ.js} +1 -1
  75. package/dist/{chunk-5XKMOZO6.js → chunk-UIN3RD2U.js} +4 -4
  76. package/dist/{chunk-REIZ5GXG.js → chunk-UTYTPDWN.js} +4 -4
  77. package/dist/{chunk-35RPUNYU.js → chunk-VENQSVKA.js} +1 -1
  78. package/dist/{chunk-JZA2JOGO.js → chunk-XJWMPQ6X.js} +1 -1
  79. package/dist/{chunk-KLZJEMSE.js → chunk-YYRZPEMA.js} +1 -1
  80. package/dist/{chunk-CKLWJ6EL.js → chunk-ZIGGEKZW.js} +33 -7
  81. package/dist/{reapply-ADFRVH5N.js → reapply-B3S2LRMT.js} +8 -8
  82. package/dist/src/index.d.ts +19 -16
  83. package/dist/src/index.js +12 -12
  84. package/package.json +1 -1
@@ -4,15 +4,15 @@ import {
4
4
  } from "../chunk-CVWG2N3Y.js";
5
5
  import {
6
6
  exitOnFailedResponse
7
- } from "../chunk-NDSOOJDX.js";
8
- import "../chunk-TW7KA27C.js";
7
+ } from "../chunk-GS5EI2YL.js";
8
+ import "../chunk-D4TNSUDY.js";
9
9
  import {
10
10
  createIo
11
11
  } from "../chunk-5RUIFDTP.js";
12
12
  import {
13
13
  exitOnHelpFlag
14
- } from "../chunk-GOQHN2UH.js";
15
- import "../chunk-LZQVAEWP.js";
14
+ } from "../chunk-TWWLVOPB.js";
15
+ import "../chunk-FAH3D74E.js";
16
16
  import {
17
17
  rejectUnknownFlags
18
18
  } from "../chunk-GMXKITVA.js";
@@ -28,7 +28,7 @@ import {
28
28
  getApiKey,
29
29
  getBaseUrl
30
30
  } from "../chunk-2UMNXADU.js";
31
- import "../chunk-5UWDYOZX.js";
31
+ import "../chunk-7IEB2GV4.js";
32
32
 
33
33
  // bin/pushary-wait.ts
34
34
  exitOnHelpFlag("wait");
@@ -3,7 +3,7 @@ import {
3
3
  COMMANDS,
4
4
  isCommandName,
5
5
  renderHelp
6
- } from "../chunk-LZQVAEWP.js";
6
+ } from "../chunk-FAH3D74E.js";
7
7
  import {
8
8
  getPackageVersion
9
9
  } from "../chunk-DINDL2CF.js";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  SETUP_FLAGS
3
- } from "./chunk-FGNABUPN.js";
3
+ } from "./chunk-5MSBI6NP.js";
4
4
  import {
5
5
  parseFlags
6
6
  } from "./chunk-GMXKITVA.js";
@@ -14,7 +14,7 @@ import {
14
14
  CLAUDE_CONNECTOR_URL,
15
15
  COWORK_CONNECTOR_LIMIT,
16
16
  COWORK_STANDING_INSTRUCTIONS
17
- } from "./chunk-5UWDYOZX.js";
17
+ } from "./chunk-7IEB2GV4.js";
18
18
 
19
19
  // src/cli/cowork.ts
20
20
  import { readFileSync } from "fs";
@@ -1,22 +1,24 @@
1
1
  import {
2
+ activeHookSource,
2
3
  activeIdentity
3
- } from "./chunk-LWT34GSY.js";
4
+ } from "./chunk-HGN6VZTY.js";
4
5
  import {
5
6
  blockedActionFingerprint,
6
7
  defersToNativeMode,
7
8
  forgetBlockedActionHint,
8
9
  hasBlockedActionHint,
9
10
  recordBlockedActionHint
10
- } from "./chunk-HCTSTMDD.js";
11
+ } from "./chunk-JCWCTBYH.js";
11
12
  import {
12
13
  isGatingMoment,
13
14
  recordKeylessMoment
14
- } from "./chunk-OMP3KDDU.js";
15
+ } from "./chunk-5F5QQMCA.js";
15
16
  import {
16
17
  denyReasonFrom,
17
18
  isDeferAnswer
18
19
  } from "./chunk-YHG74UFF.js";
19
20
  import {
21
+ AUTO_DECISION_WINDOW_MS,
20
22
  DEFAULT_SESSION,
21
23
  UNNAMED_ACTION,
22
24
  deriveAction,
@@ -34,7 +36,7 @@ import {
34
36
  scopePathFor,
35
37
  settleQuestionsForToolUse,
36
38
  throttlePass
37
- } from "./chunk-WOBHHXW5.js";
39
+ } from "./chunk-IF46POXX.js";
38
40
  import {
39
41
  DECISION_STOP_REASON,
40
42
  askUser,
@@ -49,18 +51,22 @@ import {
49
51
  } from "./chunk-LGXUGYUH.js";
50
52
  import {
51
53
  getMachineId
52
- } from "./chunk-465UENKO.js";
54
+ } from "./chunk-RQM5SMMR.js";
53
55
  import {
54
56
  getApiKey,
55
57
  getBaseUrl
56
58
  } from "./chunk-2UMNXADU.js";
57
59
  import {
60
+ AGENT_QUESTION_STRATEGY,
58
61
  GATE_BLOCK_REASON_MAX_LENGTH,
59
62
  GATE_WIRE,
63
+ agentQuestionStrategy,
60
64
  buildDecisionEpisodeFeatures,
61
65
  claudePermissionDeniedOutput,
62
66
  claudePermissionRequestOutput,
63
67
  claudePreToolUseOutput,
68
+ claudePromptsBeforeAnEdit,
69
+ editAsksAtTheAgentsPrompt,
64
70
  effectiveWaitSeconds,
65
71
  hookHoldWindowMs,
66
72
  hookWaitClamped,
@@ -69,11 +75,13 @@ import {
69
75
  isBlockedActionRequest,
70
76
  isPermissionRequestOwnedByPreToolUse,
71
77
  isPusharyOwnTool,
78
+ machineDecisionFor,
72
79
  parseAgentQuestionAnswers,
73
80
  parseAgentQuestions,
74
81
  resolveGate,
75
- stoppedOutput
76
- } from "./chunk-5UWDYOZX.js";
82
+ stoppedOutput,
83
+ wireVerdictFor
84
+ } from "./chunk-7IEB2GV4.js";
77
85
 
78
86
  // src/decision-episode.ts
79
87
  var EPISODE_PATH = "/api/agent/decision-episode";
@@ -103,12 +111,38 @@ import { tmpdir } from "os";
103
111
  import { existsSync, writeFileSync } from "fs";
104
112
  var START_MS = Date.now();
105
113
  var ACTIVE_AGENT = activeIdentity(process.argv);
114
+ var ACTIVE_SOURCE = activeHookSource(process.argv);
106
115
  var agentNameIn = (projectName) => `${ACTIVE_AGENT.label} - ${projectName}`;
107
116
  var allow = (reason) => claudePreToolUseOutput("allow", reason);
108
117
  var deny = (reason) => claudePreToolUseOutput("deny", reason);
109
118
  var allowWithInput = (updatedInput) => claudePreToolUseOutput("allow", void 0, updatedInput);
110
119
  var ask = (reason) => claudePreToolUseOutput("ask", reason);
111
120
  var halted = (reason) => stoppedOutput(deny(reason), reason);
121
+ var reportMachineDecision = async (input, verdict) => {
122
+ const decision = machineDecisionFor(wireVerdictFor(verdict, 0));
123
+ if (!decision) return;
124
+ const sessionKey = input.session_id || DEFAULT_SESSION;
125
+ const toolTarget = deriveToolTarget(input.tool_name, input.tool_input);
126
+ const throttleKey = `machinedecision:${sessionKey}:${input.tool_name}:${decision.origin}:${toolTarget ?? ""}`;
127
+ if (!throttlePass(throttleKey, AUTO_DECISION_WINDOW_MS)) return;
128
+ await reportEvent(
129
+ {
130
+ event: "permission_denied",
131
+ occurredAt: (/* @__PURE__ */ new Date()).toISOString(),
132
+ toolUseId: input.tool_use_id,
133
+ agentType: ACTIVE_AGENT.type,
134
+ agentName: agentNameIn(basename(input.cwd ?? process.cwd())),
135
+ action: permissionDeniedAction(input.tool_name),
136
+ sessionId: input.session_id,
137
+ decisionSource: "policy_auto",
138
+ decisionOrigin: decision.origin,
139
+ decisionOutcome: decision.outcome,
140
+ toolName: input.tool_name,
141
+ toolTarget
142
+ },
143
+ { maxAttempts: 1, timeoutMs: 800 }
144
+ ).catch(() => void 0);
145
+ };
112
146
  var storedWaitDeadline = (result, requestedDeadline) => {
113
147
  const stored = result.waitEndsAt ? Date.parse(result.waitEndsAt) : Number.NaN;
114
148
  return Number.isFinite(stored) ? Math.min(stored, requestedDeadline) : requestedDeadline;
@@ -519,11 +553,19 @@ var handlePreToolUse = async (input, canonical) => {
519
553
  try {
520
554
  const modeState = await fetchModeState(apiKey, input.session_id);
521
555
  const policy = await getPolicy(apiKey, modeState.policyVersion, ACTIVE_AGENT.type, true, input.session_id);
522
- const verdict = resolveGate(claudeGateInput(modeState, policy, input, canonical));
523
- if (verdict.kind === "kill") return halted(verdict.reason);
524
- if (verdict.kind === "deny") return deny(verdict.reason);
556
+ const gateInput = claudeGateInput(modeState, policy, input, canonical);
557
+ const verdict = resolveGate(gateInput);
558
+ if (verdict.kind === "kill") {
559
+ await reportMachineDecision(input, verdict);
560
+ return halted(verdict.reason);
561
+ }
562
+ if (verdict.kind === "deny") {
563
+ await reportMachineDecision(input, verdict);
564
+ return deny(verdict.reason);
565
+ }
525
566
  if (canonical === null) return deny(unreadableReason);
526
567
  if (verdict.kind === "gate" && verdict.scopeReason && (canonical?.toolInputs.length ?? 0) > 1) return deny(batchScopeReason);
568
+ if (agentQuestionStrategy(ACTIVE_SOURCE, "PreToolUse", input.tool_name) === AGENT_QUESTION_STRATEGY.native) return void 0;
527
569
  if (input.tool_name === "AskUserQuestion") {
528
570
  return handleAskUserQuestion(apiKey, input, modeState);
529
571
  }
@@ -534,6 +576,7 @@ var handlePreToolUse = async (input, canonical) => {
534
576
  if (!(verdict.kind === "gate" && verdict.scopeReason) && shouldDeferToNativeMode(input.permission_mode, input.tool_name)) {
535
577
  return void 0;
536
578
  }
579
+ if (claudePromptsBeforeAnEdit(input.permission_mode) && editAsksAtTheAgentsPrompt(gateInput, verdict)) return handleTerminalOnly();
537
580
  switch (verdict.kind) {
538
581
  case "allow":
539
582
  return allow();
@@ -574,9 +617,16 @@ var handlePermissionRequest = async (input, canonical) => {
574
617
  const modeState = await fetchModeState(apiKey, input.session_id);
575
618
  const policy = await getPolicy(apiKey, modeState.policyVersion, ACTIVE_AGENT.type, true, input.session_id);
576
619
  const verdict = resolveGate(claudeGateInput(modeState, policy, input, canonical));
577
- if (verdict.kind === "kill") return permReqHalted(verdict.reason);
578
- if (verdict.kind === "deny") return permReqDeny(verdict.reason);
620
+ if (verdict.kind === "kill") {
621
+ await reportMachineDecision(input, verdict);
622
+ return permReqHalted(verdict.reason);
623
+ }
624
+ if (verdict.kind === "deny") {
625
+ await reportMachineDecision(input, verdict);
626
+ return permReqDeny(verdict.reason);
627
+ }
579
628
  if (canonical === null) return permReqDeny(unreadableReason);
629
+ if (agentQuestionStrategy(ACTIVE_SOURCE, "PermissionRequest", input.tool_name) === AGENT_QUESTION_STRATEGY.native) return void 0;
580
630
  if (verdict.kind === "gate" && verdict.scopeReason && (canonical?.toolInputs.length ?? 0) > 1) return permReqDeny(batchScopeReason);
581
631
  if (!(verdict.kind === "gate" && verdict.scopeReason) && isPermissionRequestOwnedByPreToolUse(input.tool_name, input.permission_mode, shouldDeferToNativeMode)) return void 0;
582
632
  switch (verdict.kind) {
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  isSafeReadOnlyCommand
3
- } from "./chunk-5UWDYOZX.js";
3
+ } from "./chunk-7IEB2GV4.js";
4
4
 
5
5
  // src/ledger.ts
6
6
  import { appendFileSync, existsSync, mkdirSync, readFileSync, renameSync, rmSync, statSync } from "fs";
@@ -1,17 +1,17 @@
1
1
  import {
2
2
  CLAUDE_HOOK_EVENTS,
3
3
  supportedClaudeEvents
4
- } from "./chunk-KHGIM2BN.js";
4
+ } from "./chunk-A7XIL2WD.js";
5
5
  import {
6
6
  guardBinary,
7
7
  holdBudgetOf,
8
8
  isProtectedByScope,
9
9
  registerHookEntry
10
- } from "./chunk-34NNTYK6.js";
10
+ } from "./chunk-UCGQEAKZ.js";
11
11
  import {
12
12
  HOOK_LOCATOR_BINARY,
13
13
  withHookHoldBudget
14
- } from "./chunk-5UWDYOZX.js";
14
+ } from "./chunk-7IEB2GV4.js";
15
15
 
16
16
  // src/claude-config.ts
17
17
  import { join } from "path";
@@ -7,7 +7,7 @@ import {
7
7
  import {
8
8
  COMPATIBLE_AGENT_TYPES,
9
9
  isValidApiKey
10
- } from "./chunk-5UWDYOZX.js";
10
+ } from "./chunk-7IEB2GV4.js";
11
11
 
12
12
  // src/setup/options.ts
13
13
  var AGENT_CHOICES = ["claude_code", "codex", "gemini_cli", "hermes", "cursor", "vscode", "opencode", ...COMPATIBLE_AGENT_TYPES, "custom"];
@@ -216,7 +216,7 @@ var AGENTS = {
216
216
  connectVia: "Native hooks",
217
217
  setupCommand: "npx @pushary/agent-hooks setup",
218
218
  enforcementDetail: "exec, write and edit through native PreToolUse hooks, answered in Devin's own approve/block schema.",
219
- capabilityNote: "Devin CLI answers PreToolUse with `{decision}` rather than a permission decision. That schema has no third value, so where another agent would be asked, Pushary stays silent and Devin's own prompt decides; and it cannot carry an updated tool input, so Devin's own question and plan dialogs are left alone rather than answered from the phone without the answer reaching it. It also fires no Notification event."
219
+ capabilityNote: "Devin CLI answers PreToolUse with `{decision}` rather than a permission decision. Pushary blocks an unanswered approval because this adapter cannot request a native prompt reliably. Question and plan dialogs remain with Devin; this adapter does not render their answers. It also fires no Notification event."
220
220
  },
221
221
  windsurf: {
222
222
  ...defaults,
@@ -292,7 +292,8 @@ var toToolLookup = (profile, toolName, toolInput, resolvePath = (path) => path)
292
292
  var renderPreToolUseVerdict = (profile, verdict) => {
293
293
  if (profile.wire === "permission_decision") return verdict;
294
294
  const { permissionDecision, permissionDecisionReason, updatedInput } = verdict.hookSpecificOutput;
295
- if (permissionDecision === "ask" || updatedInput) return void 0;
295
+ if (permissionDecision === "ask") return { decision: "block", reason: permissionDecisionReason ?? "Pushary requires an explicit approval before this tool can run." };
296
+ if (updatedInput) return void 0;
296
297
  const stop = verdict.continue === false ? { continue: false, ...verdict.stopReason ? { stopReason: verdict.stopReason } : {} } : {};
297
298
  return {
298
299
  decision: permissionDecision === "allow" ? "approve" : "block",
@@ -333,7 +334,9 @@ var QWEN_CODE = {
333
334
  replace: "Edit",
334
335
  read_file: "Read",
335
336
  WriteFile: "Write",
336
- ReadFile: "Read"
337
+ ReadFile: "Read",
338
+ ask_user_question: "AskUserQuestion",
339
+ exit_plan_mode: "ExitPlanMode"
337
340
  },
338
341
  extraMatchers: ["mcp__.*"],
339
342
  pathInputKeys: ["absolute_path"],
@@ -415,12 +418,14 @@ var DEVIN = {
415
418
  exec: "Bash",
416
419
  write: "Write",
417
420
  edit: "Edit",
421
+ apply_patch: "Edit",
422
+ notebook_edit: "NotebookEdit",
418
423
  read: "Read",
419
424
  webfetch: "WebFetch",
420
425
  web_search: "WebSearch",
421
426
  run_subagent: "Task"
422
427
  },
423
- extraMatchers: ["mcp__.*"],
428
+ extraMatchers: ["mcp__.*", "mcp_call_tool", "write_to_process"],
424
429
  pathInputKeys: [],
425
430
  shellCommandKeys: [],
426
431
  shellContextKeys: [],
@@ -602,6 +607,8 @@ var hookWaitClamped = (startMs, policyWaitSeconds, agent, nowMs) => startMs + ho
602
607
  var effectiveWaitSeconds = (timeoutAction, policySeconds, agent) => timeoutAction === "wait" ? hookMaxWaitSeconds(agent) : policySeconds;
603
608
  var OPENCODE_APPROVAL_EVENT = "permission.ask";
604
609
  var CURSOR_GATE_EVENTS = ["beforeShellExecution", "beforeMCPExecution", "preToolUse"];
610
+ var SUPPORT_EMAIL = "business@pushary.com";
611
+ var SUPPORT_DISCORD = "https://discord.gg/9v3VvUByrr";
605
612
  var HOOK_LOCATOR_BINARY = "pushary-bridge";
606
613
  var HOOK_BATCH_MAX_BYTES = 4 * 1024 * 1024;
607
614
  var REPO_KEY_MAX_LENGTH = 200;
@@ -1344,6 +1351,10 @@ var ANSWER_SOURCES = [
1344
1351
  ];
1345
1352
  var isAnswerSource = (value) => typeof value === "string" && ANSWER_SOURCES.includes(value);
1346
1353
  var SETTLED_OUTCOMES = ["approve", "deny"];
1354
+ var SETTLED_OUTCOME = {
1355
+ approve: "approve",
1356
+ deny: "deny"
1357
+ };
1347
1358
  var isSettledOutcome = (value) => typeof value === "string" && SETTLED_OUTCOMES.includes(value);
1348
1359
  var classifyDecisionRisk = (toolName, toolTarget) => {
1349
1360
  const parts = [toolName, toolTarget ?? ""];
@@ -1621,6 +1632,10 @@ var resolvePolicyAcross = (config, toolName, modeOverride, toolInputs, cwd, repo
1621
1632
  };
1622
1633
  var isModeStateDegraded = (state) => state?.degraded === true;
1623
1634
  var KILL_REASON = "Stopped by user, this agent was halted from Pushary";
1635
+ var GATE_ALLOW_SOURCE = {
1636
+ allowlist: "allowlist",
1637
+ policy: "policy"
1638
+ };
1624
1639
  var PUSHARY_TOOL_PREFIX = "mcp__pushary__";
1625
1640
  var isPusharyOwnTool = (toolName) => toolName.startsWith(PUSHARY_TOOL_PREFIX);
1626
1641
  var strictestScopeVerdict = (scope, paths) => {
@@ -1649,7 +1664,15 @@ var shouldDeferToNativeMode = (permissionMode, toolName, options = {}) => {
1649
1664
  if (permissionMode === "bypassPermissions") return options.deferBypass === true;
1650
1665
  return false;
1651
1666
  };
1652
- var ASKED_IN_EVERY_MODE_TOOLS = ["AskUserQuestion", "ExitPlanMode"];
1667
+ var AGENT_QUESTION_TOOL = "AskUserQuestion";
1668
+ var AGENT_QUESTION_STRATEGY = { native: "native_dialog", updatedInput: "updated_input", none: "not_a_question" };
1669
+ var UPDATED_INPUT_QUESTION_SOURCES = /* @__PURE__ */ new Set(["claude", "qoder", "codebuddy", "droid"]);
1670
+ function agentQuestionStrategy(source, event, toolName) {
1671
+ if (toolName !== AGENT_QUESTION_TOOL) return AGENT_QUESTION_STRATEGY.none;
1672
+ if (source !== null && event === "PreToolUse" && UPDATED_INPUT_QUESTION_SOURCES.has(source)) return AGENT_QUESTION_STRATEGY.updatedInput;
1673
+ return AGENT_QUESTION_STRATEGY.native;
1674
+ }
1675
+ var ASKED_IN_EVERY_MODE_TOOLS = [AGENT_QUESTION_TOOL, "ExitPlanMode"];
1653
1676
  var ASKED_IN_EVERY_MODE = new Set(ASKED_IN_EVERY_MODE_TOOLS);
1654
1677
  var isAskedInEveryMode = (toolName) => ASKED_IN_EVERY_MODE.has(toolName);
1655
1678
  var NATIVE_BLOCK_EVENTS = /* @__PURE__ */ new Set(["PermissionDenied"]);
@@ -1728,6 +1751,8 @@ var resolveGate = (input) => {
1728
1751
  scopePath: scopeReason && "path" in scopeVerdict ? widenablePath(scopeVerdict.path) : void 0
1729
1752
  };
1730
1753
  };
1754
+ var editAsksAtTheAgentsPrompt = (input, verdict) => verdict.kind === "gate" && !verdict.scopeReason && (input.modeState.mode ?? input.config.modeOverride ?? null) === null && verdict.policy.origin === "builtin" && verdict.policy.mode === "push_first" && ACCEPT_EDITS_DEFER_TOOLS.has(input.toolName);
1755
+ var claudePromptsBeforeAnEdit = (permissionMode) => permissionMode === "default";
1731
1756
  var toPolicyVersion = (value) => typeof value === "string" || typeof value === "number" ? String(value) : null;
1732
1757
  var modeStateFromResponse = (data) => {
1733
1758
  const mode = data.override?.mode;
@@ -1747,6 +1772,42 @@ var modeStateFromResponse = (data) => {
1747
1772
  var GATE_WIRE = 1;
1748
1773
  var GATE_MAX_TOOL_INPUTS = 32;
1749
1774
  var GATE_BLOCK_REASON_MAX_LENGTH = 200;
1775
+ var wireVerdictFor = (verdict, deadlineSeconds) => {
1776
+ switch (verdict.kind) {
1777
+ case "kill":
1778
+ return { kind: "kill", reason: verdict.reason };
1779
+ case "allow":
1780
+ return { kind: "allow", source: verdict.source, policy: verdict.policy };
1781
+ case "deny":
1782
+ return { kind: "deny", reason: verdict.reason, policy: verdict.policy };
1783
+ case "defer":
1784
+ return { kind: "no_opinion", reason: verdict.reason === "own_tool" ? "not_gated" : "degraded" };
1785
+ case "gate":
1786
+ return {
1787
+ kind: "ask",
1788
+ policy: verdict.policy,
1789
+ deadlineSeconds,
1790
+ ...verdict.scopeReason ? { scopeReason: verdict.scopeReason } : {},
1791
+ ...verdict.scopePath ? { scopePath: verdict.scopePath } : {}
1792
+ };
1793
+ }
1794
+ };
1795
+ var machineDecisionFor = (verdict) => {
1796
+ switch (verdict.kind) {
1797
+ case "kill":
1798
+ return { origin: DECISION_ORIGIN.killSwitch, outcome: SETTLED_OUTCOME.deny };
1799
+ case "allow":
1800
+ return {
1801
+ origin: verdict.source === GATE_ALLOW_SOURCE.allowlist ? DECISION_ORIGIN.safeReadonly : DECISION_ORIGIN.autopilot,
1802
+ outcome: SETTLED_OUTCOME.approve
1803
+ };
1804
+ case "deny":
1805
+ return { origin: DECISION_ORIGIN.autopilot, outcome: SETTLED_OUTCOME.deny };
1806
+ case "ask":
1807
+ case "no_opinion":
1808
+ return null;
1809
+ }
1810
+ };
1750
1811
  var claudePermissionDeniedOutput = (decision) => ({
1751
1812
  hookSpecificOutput: {
1752
1813
  hookEventName: "PermissionDenied",
@@ -1807,7 +1868,6 @@ var claudePermissionRequestOutput = (decision, reason, updatedInput) => ({
1807
1868
  decision: decision === "allow" ? { behavior: "allow", ...updatedInput ? { updatedInput } : {} } : { behavior: "deny", ...reason ? { message: reason } : {} }
1808
1869
  }
1809
1870
  });
1810
- var COMPATIBLE_GATE_EVENT = "PreToolUse";
1811
1871
  var gateOutputFor = (source, event, decision, reason) => {
1812
1872
  let output;
1813
1873
  if (source === "claude" || source === "vscode") {
@@ -1847,8 +1907,12 @@ var gateOutputFor = (source, event, decision, reason) => {
1847
1907
  };
1848
1908
  } else {
1849
1909
  const profile = hookCompatibleProfileBySource(source);
1850
- if (profile && event === COMPATIBLE_GATE_EVENT) {
1851
- output = renderPreToolUseVerdict(profile, claudePreToolUseOutput(decision, reason));
1910
+ if (profile?.events.includes(event)) {
1911
+ if (event === "PreToolUse") {
1912
+ output = renderPreToolUseVerdict(profile, claudePreToolUseOutput(decision, reason));
1913
+ } else if (event === "PermissionRequest" && decision !== "ask") {
1914
+ output = claudePermissionRequestOutput(decision, reason);
1915
+ }
1852
1916
  }
1853
1917
  }
1854
1918
  return output;
@@ -2037,6 +2101,8 @@ export {
2037
2101
  effectiveWaitSeconds,
2038
2102
  OPENCODE_APPROVAL_EVENT,
2039
2103
  CURSOR_GATE_EVENTS,
2104
+ SUPPORT_EMAIL,
2105
+ SUPPORT_DISCORD,
2040
2106
  HOOK_LOCATOR_BINARY,
2041
2107
  normalizeRepoRemote,
2042
2108
  localRepoKey,
@@ -2055,6 +2121,8 @@ export {
2055
2121
  KILL_REASON,
2056
2122
  isPusharyOwnTool,
2057
2123
  shouldDeferToNativeMode,
2124
+ AGENT_QUESTION_STRATEGY,
2125
+ agentQuestionStrategy,
2058
2126
  CODEX_RESCUE_TOOL_NAME,
2059
2127
  isBlockedActionRequest,
2060
2128
  BLOCKED_ACTION_GRANT_TTL_SECONDS,
@@ -2063,10 +2131,14 @@ export {
2063
2131
  isPermissionRequestOwnedByPreToolUse,
2064
2132
  CLAUDE_PRETOOLUSE_MATCHER,
2065
2133
  resolveGate,
2134
+ editAsksAtTheAgentsPrompt,
2135
+ claudePromptsBeforeAnEdit,
2066
2136
  modeStateFromResponse,
2067
2137
  GATE_WIRE,
2068
2138
  GATE_MAX_TOOL_INPUTS,
2069
2139
  GATE_BLOCK_REASON_MAX_LENGTH,
2140
+ wireVerdictFor,
2141
+ machineDecisionFor,
2070
2142
  claudePermissionDeniedOutput,
2071
2143
  codexStopRescueOutput,
2072
2144
  stoppedOutput,
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  guardBinary
3
- } from "./chunk-34NNTYK6.js";
3
+ } from "./chunk-UCGQEAKZ.js";
4
4
 
5
5
  // src/bell/install.ts
6
6
  import { join } from "path";
@@ -1,16 +1,16 @@
1
1
  import {
2
2
  NOTIFICATION_MATCHED_TYPES,
3
3
  POSTTOOLUSE_MIRRORED_TOOLS
4
- } from "./chunk-XP2KRTZD.js";
4
+ } from "./chunk-KZIXHLPQ.js";
5
5
  import {
6
6
  isProtectedByScope,
7
7
  registerHookEntry
8
- } from "./chunk-34NNTYK6.js";
8
+ } from "./chunk-UCGQEAKZ.js";
9
9
  import {
10
10
  CLAUDE_PRETOOLUSE_MATCHER,
11
11
  HOOK_BUDGETS,
12
12
  HOOK_LOCATOR_BINARY
13
- } from "./chunk-5UWDYOZX.js";
13
+ } from "./chunk-7IEB2GV4.js";
14
14
 
15
15
  // src/gemini-config.ts
16
16
  var GEMINI_HOOK_BINARY = "pushary-gemini-hook";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  readTranscriptPreference
3
- } from "./chunk-5SALHATY.js";
3
+ } from "./chunk-NYHNLMQO.js";
4
4
 
5
5
  // src/transcript-setting.ts
6
6
  var transcriptEnvironmentPreference = () => {
@@ -13,7 +13,7 @@ import {
13
13
  import {
14
14
  isValidApiKey,
15
15
  normalizeKeyScope
16
- } from "./chunk-5UWDYOZX.js";
16
+ } from "./chunk-7IEB2GV4.js";
17
17
 
18
18
  // src/session.ts
19
19
  var samePusharyAccount = (left, right) => left.kind === "ok" && right.kind === "ok" && left.identity.workspaceId !== "" && left.identity.workspaceId === right.identity.workspaceId && left.identity.siteId === right.identity.siteId;
@@ -6,16 +6,16 @@ import {
6
6
  } from "./chunk-SGZJ5LYH.js";
7
7
  import {
8
8
  hookOwnerOf
9
- } from "./chunk-REIZ5GXG.js";
9
+ } from "./chunk-UTYTPDWN.js";
10
10
  import {
11
11
  ensureDaemonRunning
12
- } from "./chunk-6HH4IH4K.js";
12
+ } from "./chunk-RGZY77CD.js";
13
13
  import {
14
14
  buildTranscriptRecords,
15
15
  decodePublicKey,
16
16
  generateSessionKey,
17
17
  wrapSessionKey
18
- } from "./chunk-CDSL3LLO.js";
18
+ } from "./chunk-S3EUZZJB.js";
19
19
  import {
20
20
  fetchTranscriptRecipients,
21
21
  uploadTranscriptRecords
@@ -23,14 +23,14 @@ import {
23
23
  import {
24
24
  transcriptEnvironmentPreference,
25
25
  transcriptsEnabled
26
- } from "./chunk-YXWM4TNX.js";
26
+ } from "./chunk-BYAXXBNO.js";
27
27
  import {
28
28
  codexHome,
29
29
  pusharyDir
30
- } from "./chunk-34NNTYK6.js";
30
+ } from "./chunk-UCGQEAKZ.js";
31
31
  import {
32
32
  readTranscriptPreference
33
- } from "./chunk-5SALHATY.js";
33
+ } from "./chunk-NYHNLMQO.js";
34
34
  import {
35
35
  writeJsonAtomic
36
36
  } from "./chunk-T7E75EKA.js";
@@ -59,7 +59,7 @@ var COMMAND_LIST = [
59
59
  },
60
60
  { name: "doctor", module: "pushary-doctor", summary: "Verify your Pushary installation is working" },
61
61
  { name: "disconnect", module: "pushary-disconnect", summary: "Turn off one agent (claude|codex|gemini|cursor)" },
62
- { name: "clean", module: "pushary-clean", summary: "Remove all Pushary configuration (--yes for non-interactive)" },
62
+ { name: "clean", module: "pushary-clean", summary: "Remove Pushary from this machine (--everything: the Mac app's setup too, --dry-run: preview)" },
63
63
  { name: "mode", module: "pushary-mode", summary: "Choose where approval requests appear" },
64
64
  { name: "wait", module: "pushary-wait", summary: 'Show or set the "wait for your phone" ladder (pushary wait 45)' },
65
65
  { name: "stats", module: "pushary-stats", summary: "Show the approval moments your agents hit while not connected" },
@@ -98,6 +98,7 @@ var COMMAND_OPTIONS = {
98
98
  ],
99
99
  clean: [
100
100
  ["--dry-run", "List everything it would remove and change nothing"],
101
+ ["--everything", "Also remove what the Mac app set up: quits it, then its keychain items, local state and editor extension"],
101
102
  ["--yes", "Remove everything without asking"]
102
103
  ],
103
104
  transcripts: [
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  pusharyDir
3
- } from "./chunk-34NNTYK6.js";
3
+ } from "./chunk-UCGQEAKZ.js";
4
4
  import {
5
5
  readJsonSafe
6
6
  } from "./chunk-T7E75EKA.js";
@@ -2,10 +2,10 @@ import {
2
2
  resolveBinary,
3
3
  resolveGlobalPackageVersion,
4
4
  resolvePackageVersionFromBinary
5
- } from "./chunk-34NNTYK6.js";
5
+ } from "./chunk-UCGQEAKZ.js";
6
6
  import {
7
7
  MANAGED_PROVIDERS
8
- } from "./chunk-5UWDYOZX.js";
8
+ } from "./chunk-7IEB2GV4.js";
9
9
 
10
10
  // src/live-capabilities.ts
11
11
  var CLAUDE_BRIDGE_CAPABILITY = "claude-bridge";
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  describeKeyCheck,
3
3
  keyCheckFromResponse
4
- } from "./chunk-TW7KA27C.js";
4
+ } from "./chunk-D4TNSUDY.js";
5
5
  import {
6
6
  createIo
7
7
  } from "./chunk-5RUIFDTP.js";
@@ -3,7 +3,7 @@ import {
3
3
  decodePublicKey,
4
4
  generateSessionKey,
5
5
  wrapSessionKey
6
- } from "./chunk-CDSL3LLO.js";
6
+ } from "./chunk-S3EUZZJB.js";
7
7
  import {
8
8
  fetchTranscriptRecipients,
9
9
  uploadTranscriptRecords
@@ -3,28 +3,28 @@ import {
3
3
  addPusharyToolPermissions,
4
4
  isPusharyHook,
5
5
  removeClaudeMcpServers
6
- } from "./chunk-SGZE2JBF.js";
6
+ } from "./chunk-5IBVIMWT.js";
7
7
  import {
8
8
  CLAUDE_HOOK_EVENTS,
9
9
  addGeminiMcpServer,
10
10
  removeGeminiMcpServer
11
- } from "./chunk-KHGIM2BN.js";
11
+ } from "./chunk-A7XIL2WD.js";
12
12
  import {
13
13
  CLAUDE_CODE_AGENT,
14
14
  parseApplyPatchFiles
15
- } from "./chunk-WOBHHXW5.js";
15
+ } from "./chunk-IF46POXX.js";
16
16
  import {
17
17
  guardBinary,
18
18
  holdBudgetOf,
19
19
  registerHookEntry
20
- } from "./chunk-34NNTYK6.js";
20
+ } from "./chunk-UCGQEAKZ.js";
21
21
  import {
22
22
  PHONE_ANSWERED_TOOLS,
23
23
  hookCompatibleProfile,
24
24
  hookCompatibleProfileBySource,
25
25
  toToolLookup,
26
26
  withHookHoldBudget
27
- } from "./chunk-5UWDYOZX.js";
27
+ } from "./chunk-7IEB2GV4.js";
28
28
 
29
29
  // src/compatible-config.ts
30
30
  import { homedir } from "os";
@@ -50,14 +50,14 @@ var namedSource = (argv) => {
50
50
  const inline = argv.find((argument) => argument.startsWith(`${SOURCE_FLAG}=`));
51
51
  if (inline) return inline.slice(SOURCE_FLAG.length + 1);
52
52
  const index = argv.indexOf(SOURCE_FLAG);
53
- return index === -1 ? void 0 : argv[index + 1];
53
+ return index === -1 ? void 0 : argv[index + 1] ?? "";
54
54
  };
55
55
  var activeHookSource = (argv) => {
56
56
  const named = namedAgent(argv);
57
57
  if (named !== void 0) return hookCompatibleProfile(named)?.source ?? null;
58
58
  const source = namedSource(argv);
59
59
  if (source === void 0) return "claude";
60
- return hookCompatibleProfileBySource(source)?.source ?? "claude";
60
+ return source === "claude" ? "claude" : hookCompatibleProfileBySource(source)?.source ?? null;
61
61
  };
62
62
  var PATCH_INPUT_KEYS = ["command", "patch", "input"];
63
63
  var patchedFiles = (toolInput) => [...new Set(PATCH_INPUT_KEYS.flatMap((key) => parseApplyPatchFiles(toolInput[key])))];