@pushary/agent-hooks 0.95.8 → 0.98.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 (82) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/dist/bin/pushary-bell-hook.js +19 -21
  3. package/dist/bin/pushary-bell.js +6 -8
  4. package/dist/bin/pushary-claude.js +21 -23
  5. package/dist/bin/pushary-clean.js +19 -22
  6. package/dist/bin/pushary-codex-bridge.js +13 -14
  7. package/dist/bin/pushary-codex-hook.js +452 -104
  8. package/dist/bin/pushary-codex.js +8 -8
  9. package/dist/bin/pushary-connect.js +6 -6
  10. package/dist/bin/pushary-cowork.js +3 -3
  11. package/dist/bin/pushary-daemon.js +25 -27
  12. package/dist/bin/pushary-disconnect.js +6 -8
  13. package/dist/bin/pushary-doctor.js +28 -23
  14. package/dist/bin/pushary-elicitation-hook.js +19 -21
  15. package/dist/bin/pushary-gemini-bridge.js +11 -12
  16. package/dist/bin/pushary-gemini-hook.js +22 -24
  17. package/dist/bin/pushary-hook.js +24 -26
  18. package/dist/bin/pushary-login.js +7 -8
  19. package/dist/bin/pushary-logout.js +4 -4
  20. package/dist/bin/pushary-mode.js +3 -3
  21. package/dist/bin/pushary-notification-hook.js +21 -23
  22. package/dist/bin/pushary-opencode-hook.js +22 -24
  23. package/dist/bin/pushary-permission-denied-hook.js +28 -27
  24. package/dist/bin/pushary-permission-hook.js +24 -26
  25. package/dist/bin/pushary-post-hook.js +21 -23
  26. package/dist/bin/pushary-prompt-hook.js +21 -23
  27. package/dist/bin/pushary-session-end-hook.js +21 -23
  28. package/dist/bin/pushary-session-start-hook.js +24 -26
  29. package/dist/bin/pushary-setup.js +45 -40
  30. package/dist/bin/pushary-stats.js +2 -2
  31. package/dist/bin/pushary-status.js +10 -11
  32. package/dist/bin/pushary-stop-hook.js +21 -23
  33. package/dist/bin/pushary-stopfailure-hook.js +21 -23
  34. package/dist/bin/pushary-transcript-register.js +17 -19
  35. package/dist/bin/pushary-transcripts.js +5 -5
  36. package/dist/bin/pushary-upgrade.js +17 -20
  37. package/dist/bin/pushary-wait.js +3 -3
  38. package/dist/{chunk-GOPIMNVC.js → chunk-2GLOHBYB.js} +1 -1
  39. package/dist/{chunk-BQL7UDIB.js → chunk-3RVCQDC4.js} +1 -1
  40. package/dist/chunk-53QCTSWY.js +202 -0
  41. package/dist/{chunk-3YFJGKJQ.js → chunk-5B5QV3KC.js} +8 -8
  42. package/dist/{chunk-FRXZMCVA.js → chunk-5KLAETSU.js} +1 -1
  43. package/dist/{chunk-SDNTRS76.js → chunk-6C6MS57P.js} +1 -1
  44. package/dist/{chunk-A22VQ5ZL.js → chunk-6CYB77VD.js} +111 -55
  45. package/dist/{chunk-QD73WATQ.js → chunk-6LRGZJFB.js} +21 -21
  46. package/dist/{chunk-Z4JVEL7R.js → chunk-A76GNRIK.js} +191 -2
  47. package/dist/{chunk-RE25RJ5A.js → chunk-B35OCFXH.js} +4 -4
  48. package/dist/{chunk-UKMLSQXA.js → chunk-CP34FI4B.js} +2 -2
  49. package/dist/{chunk-I7LKI4WO.js → chunk-DOD3A6EU.js} +6 -8
  50. package/dist/{chunk-N6FAXQDD.js → chunk-FAJ4R7IB.js} +1 -1
  51. package/dist/{chunk-U5RIXR7I.js → chunk-HB2KB7QL.js} +1 -1
  52. package/dist/{chunk-QV266EGS.js → chunk-HFQ7WSHZ.js} +11 -14
  53. package/dist/{chunk-QJPYJVL3.js → chunk-IRLTW6KF.js} +1 -1
  54. package/dist/{chunk-Y6SCMXR6.js → chunk-J44RPHUZ.js} +2 -2
  55. package/dist/{chunk-R6LBGKWQ.js → chunk-JQU6PMH4.js} +17 -15
  56. package/dist/{chunk-4DUEIUUO.js → chunk-K6CH5NVW.js} +1 -1
  57. package/dist/{chunk-XZWUOCJZ.js → chunk-LBNAGGLV.js} +1 -1
  58. package/dist/{chunk-XS33QBJA.js → chunk-LGXUGYUH.js} +50 -1
  59. package/dist/{chunk-XO3JR3OP.js → chunk-LX4NNYII.js} +2 -2
  60. package/dist/{chunk-XEVOPOKT.js → chunk-M4TVEMLH.js} +1 -1
  61. package/dist/{chunk-TY6IJY6I.js → chunk-NIXVMMJM.js} +1 -1
  62. package/dist/{chunk-VERXHIQF.js → chunk-NQGYPTJQ.js} +1 -1
  63. package/dist/{chunk-EMGGUO2Q.js → chunk-OBYC6SN4.js} +1 -1
  64. package/dist/{chunk-XUTSZYYJ.js → chunk-OPR2BPD4.js} +1 -4
  65. package/dist/{chunk-MC5WUOJJ.js → chunk-OUAE3GKO.js} +75 -1
  66. package/dist/{chunk-ATKGKAP6.js → chunk-QXIF6R4P.js} +16 -13
  67. package/dist/chunk-SI7GYBQP.js +15 -0
  68. package/dist/{chunk-VMQU7YEJ.js → chunk-SJLGOVC6.js} +1 -1
  69. package/dist/{chunk-54W7ELDQ.js → chunk-TZHDKRMG.js} +2 -2
  70. package/dist/{chunk-F4TMY6XI.js → chunk-V47GITZV.js} +3 -3
  71. package/dist/{chunk-L7NOG547.js → chunk-VZD2GEKB.js} +1 -1
  72. package/dist/{chunk-R3OZ5ISU.js → chunk-W2MG2P46.js} +4 -4
  73. package/dist/{chunk-E47WV7BJ.js → chunk-WU6LSXJ6.js} +2 -2
  74. package/dist/{chunk-NOVA7KZ2.js → chunk-XL3RKJYQ.js} +308 -35
  75. package/dist/{chunk-5GKNAQTG.js → chunk-XOOWC62T.js} +1 -1
  76. package/dist/{chunk-SKPXQRKN.js → chunk-XPPXOKIG.js} +1 -1
  77. package/dist/{chunk-HUGIKB5O.js → chunk-YJDRHQLG.js} +6 -6
  78. package/dist/src/index.js +11 -11
  79. package/package.json +1 -1
  80. package/dist/chunk-3AJIK5AO.js +0 -57
  81. package/dist/chunk-L4ZCFMWW.js +0 -177
  82. package/dist/chunk-VDI645ZX.js +0 -18
@@ -2,15 +2,16 @@ import {
2
2
  CLAUDE_HOOK_EVENTS,
3
3
  GEMINI_HOOK_EVENTS,
4
4
  hasGeminiHooks
5
- } from "./chunk-QV266EGS.js";
5
+ } from "./chunk-HFQ7WSHZ.js";
6
6
  import {
7
7
  CODEX_HOOK_EVENTS,
8
8
  hasCodexHooks
9
- } from "./chunk-NOVA7KZ2.js";
9
+ } from "./chunk-XL3RKJYQ.js";
10
10
  import {
11
11
  CURSOR_GATE_EVENTS,
12
- HOOK_LOCATOR_BINARY
13
- } from "./chunk-MC5WUOJJ.js";
12
+ HOOK_LOCATOR_BINARY,
13
+ hookCommandHoldBudget
14
+ } from "./chunk-OUAE3GKO.js";
14
15
 
15
16
  // src/diagnostics/wiring.ts
16
17
  var record = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : null;
@@ -72,31 +73,20 @@ var toSeconds = (timeout, unit) => {
72
73
  };
73
74
  var registeredHooks = (entries, shape, unit) => {
74
75
  if (!Array.isArray(entries)) return [];
75
- return entries.flatMap((entry, entryIndex) => {
76
+ return entries.flatMap((entry) => {
76
77
  const container = record(entry);
77
78
  if (!container) return [];
78
79
  const hooks = shape === "nested" ? Array.isArray(container.hooks) ? container.hooks : [] : [container];
79
- return hooks.flatMap((hook, hookIndex) => {
80
+ return hooks.flatMap((hook) => {
80
81
  const command = record(hook)?.command;
81
82
  if (typeof command !== "string" || command.trim() === "") return [];
82
- return [{
83
- command,
84
- timeoutSeconds: toSeconds(record(hook)?.timeout, unit),
85
- order: [entryIndex, hookIndex]
86
- }];
83
+ return [{ command, timeoutSeconds: toSeconds(record(hook)?.timeout, unit) }];
87
84
  });
88
85
  });
89
86
  };
90
- var runsBefore = (a, b) => a[0] < b[0] || a[0] === b[0] && a[1] < b[1];
91
87
  var holdersOnEvent = (event, hooks) => {
92
- const ours = hooks.find((hook) => pusharyHookExecutable(hook.command) !== null);
93
- if (!ours) return [];
94
- return hooks.filter((hook) => pusharyHookExecutable(hook.command) === null).map((hook) => ({
95
- event,
96
- command: hook.command,
97
- timeoutSeconds: hook.timeoutSeconds,
98
- position: runsBefore(hook.order, ours.order) ? "before" : "after"
99
- }));
88
+ if (!hooks.some((hook) => pusharyHookExecutable(hook.command) !== null)) return [];
89
+ return hooks.filter((hook) => pusharyHookExecutable(hook.command) === null).map((hook) => ({ event, command: hook.command, timeoutSeconds: hook.timeoutSeconds }));
100
90
  };
101
91
  var otherHolders = (hooks, events, shape, unit) => {
102
92
  const table = record(hooks);
@@ -111,6 +101,14 @@ var claudeOtherHolders = (settings) => otherHolders(record(settings)?.hooks, CLA
111
101
  var codexOtherHolders = (hooks) => otherHolders(record(hooks)?.hooks, CODEX_GATE_EVENTS, "nested", "seconds");
112
102
  var geminiOtherHolders = (settings) => otherHolders(record(settings)?.hooks, GEMINI_GATE_EVENTS, "nested", "milliseconds");
113
103
  var cursorOtherHolders = (userHooks) => otherHolders(record(userHooks)?.hooks, CURSOR_GATE_EVENTS, "flat", "seconds");
104
+ var holdEvents = (table) => table.filter((hook) => hook.holds).map((hook) => hook.event);
105
+ var unprovenHolds = (hooks, events) => {
106
+ const table = record(hooks);
107
+ if (!table) return [];
108
+ return events.filter((event) => registeredHooks(table[event], "nested", "seconds").some((hook) => pusharyHookExecutable(hook.command) !== null && hookCommandHoldBudget(hook.command) === void 0));
109
+ };
110
+ var claudeUnprovenHolds = (settings) => unprovenHolds(record(settings)?.hooks, holdEvents(CLAUDE_HOOK_EVENTS));
111
+ var codexUnprovenHolds = (hooks) => unprovenHolds(record(hooks)?.hooks, holdEvents(CODEX_HOOK_EVENTS));
114
112
  var holdsLong = (holder) => holder.timeoutSeconds !== null && holder.timeoutSeconds > LONG_HOLD_SECONDS;
115
113
  var SHELLS = /* @__PURE__ */ new Set(["/bin/sh", "/bin/bash", "/bin/zsh", "/usr/bin/env"]);
116
114
  var holderExecutable = (command) => {
@@ -125,7 +123,7 @@ var describeHoldTimeout = (holder) => {
125
123
  if (seconds >= 60 && seconds % 60 === 0) return `${seconds / 60}m timeout`;
126
124
  return `${seconds}s timeout`;
127
125
  };
128
- var describeOtherHolder = (holder) => `another hook on ${holder.event}: ${holderExecutable(holder.command)} (${describeHoldTimeout(holder)}, runs ${holder.position} ours)`;
126
+ var describeOtherHolder = (holder) => `another hook also registered on ${holder.event}: ${holderExecutable(holder.command)} (${describeHoldTimeout(holder)})`;
129
127
  var describeHoldSentence = (agentLabel) => `${agentLabel} waits for every hook on an event, so the terminal cannot move until that one returns, however fast your phone answered.`;
130
128
 
131
129
  export {
@@ -139,6 +137,8 @@ export {
139
137
  codexOtherHolders,
140
138
  geminiOtherHolders,
141
139
  cursorOtherHolders,
140
+ claudeUnprovenHolds,
141
+ codexUnprovenHolds,
142
142
  holdsLong,
143
143
  describeOtherHolder,
144
144
  describeHoldSentence
@@ -3,9 +3,10 @@ import {
3
3
  AGENT_LABELS_BY_TYPE,
4
4
  DECISION_LINE_MAX,
5
5
  FILE_TARGET_TOOLS,
6
+ GATE_MAX_TOOL_INPUTS,
6
7
  redactSecrets,
7
8
  redactSecretsDeep
8
- } from "./chunk-MC5WUOJJ.js";
9
+ } from "./chunk-OUAE3GKO.js";
9
10
 
10
11
  // ../hook-mapping/src/describe.ts
11
12
  import { isAbsolute, relative, resolve } from "path";
@@ -173,6 +174,90 @@ var OPENCODE_TOOL_NAMES = {
173
174
  websearch: "WebSearch",
174
175
  task: "Task"
175
176
  };
177
+ var canonicalGateInputs = (source, toolName, toolInputs, cwd) => {
178
+ if (source === "codex" && toolName === "apply_patch" || source === "vscode" && ["apply_patch", "applyPatch", "copilot_applyPatch"].includes(toolName)) {
179
+ const paths = [...new Set(toolInputs.flatMap((input) => parseApplyPatchFiles(input.command ?? input.input ?? input.patch)))];
180
+ if (paths.length > GATE_MAX_TOOL_INPUTS) return null;
181
+ return {
182
+ toolName: "Edit",
183
+ toolInputs: paths.length === 0 ? toolInputs.map((input) => ({ file_path: input.command })) : paths.map((path) => ({ file_path: cwd && !isAbsolute(path) ? resolve(cwd, path) : path }))
184
+ };
185
+ }
186
+ if (source === "opencode") {
187
+ const canonical2 = OPENCODE_TOOL_NAMES[toolName] ?? toolName;
188
+ return {
189
+ toolName: canonical2,
190
+ toolInputs: toolInputs.map((input) => {
191
+ const path = input.file_path;
192
+ return typeof path === "string" && path.length > 0 && cwd && !isAbsolute(path) ? { ...input, file_path: resolve(cwd, path) } : input;
193
+ })
194
+ };
195
+ }
196
+ if (source === "vscode" || source === "cursor") {
197
+ if (source === "vscode" && toolName.startsWith("mcp_pushary_")) return { toolName: `mcp__pushary__${toolName.slice("mcp_pushary_".length)}`, toolInputs };
198
+ const alias = toolName.replace(/^copilot_/, "").toLowerCase().replace(/[^a-z0-9]/g, "");
199
+ const shells = [
200
+ "runterminalcommand",
201
+ "runinterminal",
202
+ "runcommand",
203
+ "terminalcommand",
204
+ "executecommand",
205
+ "runinterminalcommand",
206
+ "bash",
207
+ "shell",
208
+ "terminal",
209
+ "sendtoterminal",
210
+ "powershell",
211
+ "localshell"
212
+ ];
213
+ const canonical2 = shells.includes(alias) ? "Bash" : ["createfile", "write", "createdirectory", "createnewjupyternotebook"].includes(alias) ? "Write" : ["replacestringinfile", "multireplacestringinfile", "replacestring", "multireplacestring", "inserteditintofile", "insertedit", "edit", "editfile", "editfiles", "editnotebookfile", "editnotebook"].includes(alias) ? "Edit" : [
214
+ "readfile",
215
+ "viewimage",
216
+ "listdir",
217
+ "listdirectory",
218
+ "filesearch",
219
+ "findfiles",
220
+ "grepsearch",
221
+ "findtextinfiles",
222
+ "semanticsearch",
223
+ "searchcodebase",
224
+ "searchworkspacesymbols",
225
+ "geterrors",
226
+ "getchangedfiles",
227
+ "readprojectstructure",
228
+ "getnotebooksummary",
229
+ "readnotebookcelloutput",
230
+ "testsearch",
231
+ "findtestfiles",
232
+ "testfailure",
233
+ "getvscodeapi",
234
+ "getterminaloutput",
235
+ "gettaskoutput",
236
+ "terminalselection",
237
+ "terminallastcommand"
238
+ ].includes(alias) ? "Read" : toolName;
239
+ const inputs = toolInputs.flatMap((input) => ["multireplacestringinfile", "multireplacestring"].includes(alias) && Array.isArray(input.replacements) ? input.replacements.filter((item) => !!item && typeof item === "object" && !Array.isArray(item)) : alias === "editfiles" && Array.isArray(input.files) ? input.files.map((file) => typeof file === "string" ? { file_path: file } : file).filter((file) => !!file && typeof file === "object" && !Array.isArray(file)) : [input]);
240
+ if (inputs.length > GATE_MAX_TOOL_INPUTS) return null;
241
+ return { toolName: canonical2, toolInputs: inputs.map((input) => {
242
+ if (canonical2 === "Bash") return { ...input, command: [input.command, input.commandLine, input.command_line, input.cmd, input.script, input.input, input.text].find((value) => typeof value === "string") };
243
+ const path = input.file_path ?? input.filePath ?? input.path;
244
+ return typeof path === "string" ? { ...input, file_path: cwd && !isAbsolute(path) ? resolve(cwd, path) : path } : input;
245
+ }) };
246
+ }
247
+ if (source !== "gemini") return { toolName, toolInputs };
248
+ if (toolName === "run_shell_command") {
249
+ return { toolName: "Bash", toolInputs: toolInputs.map((input) => ({ command: input.command })) };
250
+ }
251
+ const canonical = { write_file: "Write", replace: "Edit", read_file: "Read" }[toolName];
252
+ if (!canonical) return { toolName, toolInputs };
253
+ return {
254
+ toolName: canonical,
255
+ toolInputs: toolInputs.map((input) => {
256
+ const path = [input.file_path, input.absolute_path, input.path].find((value) => typeof value === "string" && value.length > 0);
257
+ return path ? { file_path: cwd && !isAbsolute(path) ? resolve(cwd, path) : path } : {};
258
+ })
259
+ };
260
+ };
176
261
  var EXIT_CODE_FAILURE = /^Error: Exit code \d+/;
177
262
  var isToolResultError = (toolResult) => {
178
263
  try {
@@ -388,6 +473,105 @@ var sessionIdentityOf = (payload) => {
388
473
  return { sessionId: agentId, parentSessionId: sessionId };
389
474
  };
390
475
 
476
+ // ../hook-mapping/src/codex-rollout.ts
477
+ var CODEX_TURN_CONTEXT_TYPE = "turn_context";
478
+ var CODEX_AUTO_REVIEWER = "auto_review";
479
+ var CODEX_GUARDIAN_THREAD_SOURCE = "guardian_review";
480
+ var CODEX_PLANNED_ACTION_MARKER = "Planned action JSON:\n";
481
+ var CODEX_APPROVAL_REQUEST_END = ">>> APPROVAL REQUEST END";
482
+ var CODEX_SHELL_TOOL = "exec_command";
483
+ var CODEX_SHELL_COMMAND_FLAGS = ["-lc", "-c"];
484
+ var asRecord = (value) => value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
485
+ var parseRecord = (text) => {
486
+ try {
487
+ return asRecord(JSON.parse(text));
488
+ } catch {
489
+ return null;
490
+ }
491
+ };
492
+ var nonEmptyString = (value) => typeof value === "string" && value.length > 0 ? value : void 0;
493
+ var isTimestamp = (value) => typeof value === "string" && Number.isFinite(Date.parse(value));
494
+ var readCodexTurnContext = (line) => {
495
+ const entry = parseRecord(line);
496
+ if (entry?.type !== CODEX_TURN_CONTEXT_TYPE) return null;
497
+ const payload = asRecord(entry.payload);
498
+ const turnId = nonEmptyString(payload?.turn_id);
499
+ if (!payload || !turnId) return null;
500
+ return {
501
+ turnId,
502
+ approvalsReviewer: payload.approvals_reviewer === CODEX_AUTO_REVIEWER ? "auto_review" : "user",
503
+ ...isTimestamp(entry.timestamp) ? { startedAt: entry.timestamp } : {}
504
+ };
505
+ };
506
+ var isCodexGuardianRolloutHead = (line, sessionId) => {
507
+ if (!sessionId) return false;
508
+ const entry = parseRecord(line);
509
+ const payload = asRecord(entry?.payload);
510
+ return entry?.type === "session_meta" && payload?.parent_thread_id === sessionId && payload.thread_source === CODEX_GUARDIAN_THREAD_SOURCE;
511
+ };
512
+ var readCodexPlannedAction = (message) => {
513
+ const marker = message.lastIndexOf(CODEX_PLANNED_ACTION_MARKER);
514
+ if (marker < 0) return null;
515
+ const body = message.slice(marker + CODEX_PLANNED_ACTION_MARKER.length);
516
+ const end = body.lastIndexOf(CODEX_APPROVAL_REQUEST_END);
517
+ if (end < 0 || body.slice(end + CODEX_APPROVAL_REQUEST_END.length).trim().length > 0) return null;
518
+ return parseRecord(body.slice(0, end).trim());
519
+ };
520
+ var readCodexReviewVerdict = (message) => {
521
+ const verdict = parseRecord(message.trim());
522
+ if (verdict?.outcome !== "allow" && verdict?.outcome !== "deny") return null;
523
+ const rationale = typeof verdict.rationale === "string" ? verdict.rationale.trim() : "";
524
+ return { outcome: verdict.outcome, ...rationale ? { rationale } : {} };
525
+ };
526
+ var scanCodexGuardianReviews = (lines) => {
527
+ const reviews = [];
528
+ let pending = null;
529
+ for (const line of lines) {
530
+ const entry = parseRecord(line.text);
531
+ const payload = asRecord(entry?.payload);
532
+ if (entry?.type !== "event_msg" || typeof payload?.message !== "string") continue;
533
+ if (payload.type === "user_message") {
534
+ const plannedAction = readCodexPlannedAction(payload.message);
535
+ pending = plannedAction ? { offset: line.offset, plannedAction, ...isTimestamp(entry.timestamp) ? { requestedAt: entry.timestamp } : {} } : null;
536
+ continue;
537
+ }
538
+ if (payload.type !== "agent_message" || !pending) continue;
539
+ const verdict = readCodexReviewVerdict(payload.message);
540
+ if (!verdict) continue;
541
+ reviews.push({ ...pending, verdict });
542
+ pending = null;
543
+ }
544
+ return { reviews, ...pending ? { pendingOffset: pending.offset } : {} };
545
+ };
546
+ var codexPlannedShellCommand = (plannedAction) => {
547
+ if (plannedAction?.tool !== CODEX_SHELL_TOOL) return null;
548
+ const argv = plannedAction.command;
549
+ if (!Array.isArray(argv) || argv.length !== 3) return null;
550
+ const [shell, flag, command] = argv;
551
+ if (typeof shell !== "string" || shell.trim().length === 0) return null;
552
+ if (typeof flag !== "string" || !CODEX_SHELL_COMMAND_FLAGS.includes(flag)) return null;
553
+ if (typeof command !== "string" || command.trim().length === 0) return null;
554
+ const cwd = nonEmptyString(plannedAction.cwd);
555
+ return { command, ...cwd ? { cwd } : {} };
556
+ };
557
+ var codexReviewerDenialsSince = (reviews, since) => {
558
+ const sinceMs = Date.parse(since);
559
+ if (!Number.isFinite(sinceMs)) return [];
560
+ return reviews.flatMap((review) => {
561
+ if (review.verdict.outcome !== "deny" || !review.requestedAt) return [];
562
+ if (Date.parse(review.requestedAt) < sinceMs) return [];
563
+ const shell = codexPlannedShellCommand(review.plannedAction);
564
+ if (!shell) return [];
565
+ return [{
566
+ ...shell,
567
+ offset: review.offset,
568
+ requestedAt: review.requestedAt,
569
+ plannedAction: review.plannedAction,
570
+ ...review.verdict.rationale ? { rationale: review.verdict.rationale } : {}
571
+ }];
572
+ });
573
+ };
574
+
391
575
  export {
392
576
  HOOK_FALLBACK_MAX,
393
577
  UNNAMED_ACTION,
@@ -396,6 +580,7 @@ export {
396
580
  parseApplyPatchOps,
397
581
  parseApplyPatchFiles,
398
582
  OPENCODE_TOOL_NAMES,
583
+ canonicalGateInputs,
399
584
  isToolResultError,
400
585
  toolResultErrorText,
401
586
  deriveReceiptMeta,
@@ -415,5 +600,9 @@ export {
415
600
  notificationAction,
416
601
  describeStopFailure,
417
602
  permissionDeniedAction,
418
- sessionIdentityOf
603
+ sessionIdentityOf,
604
+ readCodexTurnContext,
605
+ isCodexGuardianRolloutHead,
606
+ scanCodexGuardianReviews,
607
+ codexReviewerDenialsSince
419
608
  };
@@ -2,24 +2,24 @@ import {
2
2
  detectProviderLiveCapabilities,
3
3
  hasSpawnProvider,
4
4
  resolveControlPolicy
5
- } from "./chunk-54W7ELDQ.js";
5
+ } from "./chunk-TZHDKRMG.js";
6
6
  import {
7
7
  daemonHasRemoteProof,
8
8
  daemonServiceManagerAvailable,
9
9
  hasDaemonServiceDefinition,
10
10
  readDaemonNeedsLogin,
11
11
  readDaemonStatus
12
- } from "./chunk-I7LKI4WO.js";
12
+ } from "./chunk-DOD3A6EU.js";
13
13
  import {
14
14
  getMachineId
15
- } from "./chunk-E47WV7BJ.js";
15
+ } from "./chunk-WU6LSXJ6.js";
16
16
  import {
17
17
  getPackageVersion
18
18
  } from "./chunk-DINDL2CF.js";
19
19
  import {
20
20
  readControlMode,
21
21
  readKeySource
22
- } from "./chunk-VMQU7YEJ.js";
22
+ } from "./chunk-SJLGOVC6.js";
23
23
  import {
24
24
  getBaseUrl
25
25
  } from "./chunk-2UMNXADU.js";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  SETUP_FLAGS
3
- } from "./chunk-XZWUOCJZ.js";
3
+ } from "./chunk-LBNAGGLV.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-MC5WUOJJ.js";
17
+ } from "./chunk-OUAE3GKO.js";
18
18
 
19
19
  // src/cli/cowork.ts
20
20
  import { readFileSync } from "fs";
@@ -2,27 +2,25 @@ import {
2
2
  needsShell,
3
3
  spawnClaude
4
4
  } from "./chunk-XHKBHWLX.js";
5
- import {
6
- resolveGlobalBinary
7
- } from "./chunk-L4ZCFMWW.js";
8
5
  import {
9
6
  getMachineId
10
- } from "./chunk-E47WV7BJ.js";
7
+ } from "./chunk-WU6LSXJ6.js";
11
8
  import {
12
- pusharyDir
13
- } from "./chunk-NOVA7KZ2.js";
9
+ pusharyDir,
10
+ resolveGlobalBinary
11
+ } from "./chunk-XL3RKJYQ.js";
14
12
  import {
15
13
  getPackageVersion
16
14
  } from "./chunk-DINDL2CF.js";
17
15
  import {
18
16
  readControlMode
19
- } from "./chunk-VMQU7YEJ.js";
17
+ } from "./chunk-SJLGOVC6.js";
20
18
  import {
21
19
  readJsonSafe,
22
20
  writeFileAtomic,
23
21
  writeJsonAtomic,
24
22
  writeJsonAtomicDurable
25
- } from "./chunk-L7NOG547.js";
23
+ } from "./chunk-VZD2GEKB.js";
26
24
  import {
27
25
  getApiKey
28
26
  } from "./chunk-2UMNXADU.js";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  resolvePathBinary
3
- } from "./chunk-L4ZCFMWW.js";
3
+ } from "./chunk-XL3RKJYQ.js";
4
4
 
5
5
  // src/hermes-config.ts
6
6
  import { execFileSync } from "child_process";
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  HOOK_BUDGETS,
3
3
  OPENCODE_APPROVAL_EVENT
4
- } from "./chunk-MC5WUOJJ.js";
4
+ } from "./chunk-OUAE3GKO.js";
5
5
 
6
6
  // src/opencode-plugin.ts
7
7
  var OPENCODE_PLUGIN_MARKER = "pushary-opencode-plugin w1";
@@ -1,16 +1,16 @@
1
1
  import {
2
2
  NOTIFICATION_MATCHED_TYPES,
3
- POSTTOOLUSE_MIRRORED_TOOLS,
4
- PRETOOLUSE_PHONE_ANSWERED_TOOLS
5
- } from "./chunk-XUTSZYYJ.js";
3
+ POSTTOOLUSE_MIRRORED_TOOLS
4
+ } from "./chunk-OPR2BPD4.js";
6
5
  import {
7
- isProtectedByScope
8
- } from "./chunk-NOVA7KZ2.js";
6
+ isProtectedByScope,
7
+ registerHookEntry
8
+ } from "./chunk-XL3RKJYQ.js";
9
9
  import {
10
+ CLAUDE_PRETOOLUSE_MATCHER,
10
11
  HOOK_BUDGETS,
11
- HOOK_LOCATOR_BINARY,
12
- PRETOOLUSE_GATED_TOOLS
13
- } from "./chunk-MC5WUOJJ.js";
12
+ HOOK_LOCATOR_BINARY
13
+ } from "./chunk-OUAE3GKO.js";
14
14
 
15
15
  // src/gemini-config.ts
16
16
  var GEMINI_HOOK_BINARY = "pushary-gemini-hook";
@@ -52,8 +52,7 @@ var addGeminiHooks = (settings, command) => {
52
52
  const hooks = ensureRecord(settings, "hooks");
53
53
  for (const definition of GEMINI_HOOK_EVENTS) {
54
54
  const existing = Array.isArray(hooks[definition.event]) ? hooks[definition.event] : [];
55
- const entries = existing.filter((entry) => !isPusharyGeminiHook(entry));
56
- entries.push({
55
+ hooks[definition.event] = registerHookEntry(existing, isPusharyGeminiHook, {
57
56
  ...definition.matcher ? { matcher: definition.matcher } : {},
58
57
  hooks: [{
59
58
  name: "pushary",
@@ -62,7 +61,6 @@ var addGeminiHooks = (settings, command) => {
62
61
  timeout: definition.timeoutMs
63
62
  }]
64
63
  });
65
- hooks[definition.event] = entries;
66
64
  }
67
65
  };
68
66
  var removeGeminiHooks = (settings, scope) => {
@@ -128,12 +126,11 @@ var atLeast = (version, floor) => compareVersion(version, floor) >= 0;
128
126
  var formatVersion = (version) => version.join(".");
129
127
 
130
128
  // src/claude-events.ts
131
- var GATED_TOOLS = [...PRETOOLUSE_GATED_TOOLS, ...PRETOOLUSE_PHONE_ANSWERED_TOOLS].join("|");
132
129
  var MIRRORED_TOOLS = POSTTOOLUSE_MIRRORED_TOOLS.join("|");
133
130
  var ESTABLISHED = [1, 0, 0];
134
131
  var OBSERVED = [2, 0, 0];
135
132
  var CLAUDE_HOOK_EVENTS = [
136
- { event: "PreToolUse", binary: "pushary-hook", matcher: GATED_TOOLS, timeout: HOOK_BUDGETS.claude.budgetSeconds, delivery: "approval", since: ESTABLISHED },
133
+ { event: "PreToolUse", binary: "pushary-hook", matcher: CLAUDE_PRETOOLUSE_MATCHER, timeout: HOOK_BUDGETS.claude.budgetSeconds, delivery: "approval", since: ESTABLISHED },
137
134
  { event: "PostToolUse", binary: "pushary-post-hook", matcher: MIRRORED_TOOLS, timeout: 10, delivery: "telemetry", since: ESTABLISHED },
138
135
  { event: "Stop", binary: "pushary-stop-hook", timeout: 10, delivery: "telemetry", since: ESTABLISHED },
139
136
  { event: "UserPromptSubmit", binary: "pushary-prompt-hook", timeout: 10, delivery: "telemetry", since: ESTABLISHED },
@@ -142,7 +139,7 @@ var CLAUDE_HOOK_EVENTS = [
142
139
  { event: "SessionEnd", binary: "pushary-session-end-hook", timeout: 10, delivery: "telemetry", since: ESTABLISHED },
143
140
  { event: "StopFailure", binary: "pushary-stopfailure-hook", timeout: 10, delivery: "telemetry", since: ESTABLISHED },
144
141
  { event: "PermissionRequest", binary: "pushary-permission-hook", timeout: HOOK_BUDGETS.claude.budgetSeconds, delivery: "approval", since: ESTABLISHED },
145
- { event: "PermissionDenied", binary: "pushary-permission-denied-hook", timeout: 60, delivery: "telemetry", since: ESTABLISHED },
142
+ { event: "PermissionDenied", binary: "pushary-permission-denied-hook", timeout: HOOK_BUDGETS.claude.budgetSeconds, delivery: "approval", holds: true, since: ESTABLISHED },
146
143
  // Observability only. Each carries session shape the island cannot get any other
147
144
  // way, and none of them decides anything.
148
145
  { event: "SubagentStart", binary: "pushary-session-start-hook", timeout: 10, delivery: "telemetry", since: OBSERVED },
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  HOOK_LOCATOR_BINARY
3
- } from "./chunk-MC5WUOJJ.js";
3
+ } from "./chunk-OUAE3GKO.js";
4
4
 
5
5
  // src/diagnostics/bridge.ts
6
6
  import { execFileSync } from "child_process";
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  pusharyDir
3
- } from "./chunk-NOVA7KZ2.js";
3
+ } from "./chunk-XL3RKJYQ.js";
4
4
  import {
5
5
  readJsonSafe
6
- } from "./chunk-L7NOG547.js";
6
+ } from "./chunk-VZD2GEKB.js";
7
7
 
8
8
  // src/process-ownership.ts
9
9
  import { createHash, randomUUID } from "crypto";
@@ -1,16 +1,16 @@
1
1
  import {
2
2
  detectClaudeVersion
3
- } from "./chunk-4DUEIUUO.js";
3
+ } from "./chunk-K6CH5NVW.js";
4
4
  import {
5
5
  addClaudeMcpServer,
6
6
  addPusharyHooks,
7
7
  addPusharyToolPermissions
8
- } from "./chunk-ATKGKAP6.js";
8
+ } from "./chunk-QXIF6R4P.js";
9
9
  import {
10
10
  claudeWired,
11
11
  codexWired,
12
12
  geminiWired
13
- } from "./chunk-QD73WATQ.js";
13
+ } from "./chunk-6LRGZJFB.js";
14
14
  import {
15
15
  CLAUDE_HOOK_EVENTS,
16
16
  GEMINI_HOOK_BINARY,
@@ -21,14 +21,7 @@ import {
21
21
  needsReregistration,
22
22
  registrationSignatures,
23
23
  supportedClaudeEvents
24
- } from "./chunk-QV266EGS.js";
25
- import {
26
- guardBinary
27
- } from "./chunk-3AJIK5AO.js";
28
- import {
29
- resolveGlobalBinDir,
30
- resolveGlobalBinary
31
- } from "./chunk-L4ZCFMWW.js";
24
+ } from "./chunk-HFQ7WSHZ.js";
32
25
  import {
33
26
  CODEX_HOOK_BINARY,
34
27
  addCodexHookTrust,
@@ -38,16 +31,21 @@ import {
38
31
  codexConfigToml,
39
32
  codexHookPositions,
40
33
  codexHooksJson,
34
+ codexVacatedIndexes,
35
+ dropVacatedCodexHookTrust,
36
+ guardBinary,
41
37
  hasCodexHooks,
38
+ resolveGlobalBinDir,
39
+ resolveGlobalBinary,
42
40
  vscodePluginMcp
43
- } from "./chunk-NOVA7KZ2.js";
41
+ } from "./chunk-XL3RKJYQ.js";
44
42
  import {
45
43
  readJsonSafe,
46
44
  writeJsonAtomic
47
- } from "./chunk-L7NOG547.js";
45
+ } from "./chunk-VZD2GEKB.js";
48
46
  import {
49
47
  HOOK_LOCATOR_BINARY
50
- } from "./chunk-MC5WUOJJ.js";
48
+ } from "./chunk-OUAE3GKO.js";
51
49
 
52
50
  // src/reapply.ts
53
51
  import { join } from "path";
@@ -218,6 +216,7 @@ var reapplyCodexSettings = (apiKey, paths = {}) => {
218
216
  const command = guardBinary(resolveGlobalBinary(CODEX_HOOK_BINARY) ?? CODEX_HOOK_BINARY);
219
217
  let hooks = false;
220
218
  let mcp = false;
219
+ const vacated = codexVacatedIndexes(hooksConfig ?? void 0);
221
220
  if (hooksConfig != null) {
222
221
  try {
223
222
  addCodexHooks(hooksConfig, command);
@@ -228,7 +227,10 @@ var reapplyCodexSettings = (apiKey, paths = {}) => {
228
227
  }
229
228
  if (config != null) {
230
229
  try {
231
- if (hooks) addCodexHookTrust(config, hooksPath, command, codexHookPositions(hooksConfig ?? void 0));
230
+ if (hooks) {
231
+ dropVacatedCodexHookTrust(config, hooksPath, vacated);
232
+ addCodexHookTrust(config, hooksPath, command, codexHookPositions(hooksConfig ?? void 0));
233
+ }
232
234
  if (apiKey) {
233
235
  addCodexMcpServer(config, apiKey);
234
236
  mcp = true;
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  parseVersion
3
- } from "./chunk-QV266EGS.js";
3
+ } from "./chunk-HFQ7WSHZ.js";
4
4
 
5
5
  // src/setup/agent-probe.ts
6
6
  import { execSync } from "child_process";
@@ -6,7 +6,7 @@ import {
6
6
  } from "./chunk-7QLSKOSU.js";
7
7
  import {
8
8
  isValidApiKey
9
- } from "./chunk-MC5WUOJJ.js";
9
+ } from "./chunk-OUAE3GKO.js";
10
10
 
11
11
  // src/setup/options.ts
12
12
  var AGENT_CHOICES = ["claude_code", "codex", "gemini_cli", "hermes", "cursor", "vscode", "opencode", "custom"];