@pushary/agent-hooks 0.95.8 → 0.97.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 +17 -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 +34 -38
  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-RE25RJ5A.js → chunk-3SNTEHOH.js} +4 -4
  39. package/dist/{chunk-R3OZ5ISU.js → chunk-4AXWBCCO.js} +4 -4
  40. package/dist/{chunk-XEVOPOKT.js → chunk-5ODZEFP3.js} +1 -1
  41. package/dist/{chunk-NOVA7KZ2.js → chunk-67RNDRKL.js} +260 -32
  42. package/dist/{chunk-3YFJGKJQ.js → chunk-6VXZKOBB.js} +8 -8
  43. package/dist/{chunk-ATKGKAP6.js → chunk-6WJKYD3Q.js} +8 -8
  44. package/dist/{chunk-EMGGUO2Q.js → chunk-A4AIISZZ.js} +1 -1
  45. package/dist/{chunk-N6FAXQDD.js → chunk-AXSDMDJI.js} +1 -1
  46. package/dist/{chunk-XO3JR3OP.js → chunk-B3557BDL.js} +2 -2
  47. package/dist/{chunk-QJPYJVL3.js → chunk-BYH75I7Y.js} +1 -1
  48. package/dist/{chunk-VERXHIQF.js → chunk-C2FYSFTQ.js} +1 -1
  49. package/dist/{chunk-SKPXQRKN.js → chunk-CDSA3NTB.js} +1 -1
  50. package/dist/{chunk-HUGIKB5O.js → chunk-DGUMR5LZ.js} +6 -6
  51. package/dist/{chunk-54W7ELDQ.js → chunk-DNLFSRCF.js} +2 -2
  52. package/dist/{chunk-R6LBGKWQ.js → chunk-DOKFLZWL.js} +10 -14
  53. package/dist/{chunk-U5RIXR7I.js → chunk-EHQC3GWL.js} +1 -1
  54. package/dist/{chunk-QD73WATQ.js → chunk-FU5SQKAY.js} +15 -4
  55. package/dist/{chunk-TY6IJY6I.js → chunk-GGYH6LIE.js} +1 -1
  56. package/dist/{chunk-XZWUOCJZ.js → chunk-H4VNAOCG.js} +1 -1
  57. package/dist/{chunk-MC5WUOJJ.js → chunk-HUZW6SEO.js} +75 -1
  58. package/dist/{chunk-QV266EGS.js → chunk-IBRX7LRE.js} +8 -10
  59. package/dist/{chunk-UKMLSQXA.js → chunk-ICUBJNFL.js} +2 -2
  60. package/dist/{chunk-E47WV7BJ.js → chunk-IZKCA35M.js} +2 -2
  61. package/dist/{chunk-F4TMY6XI.js → chunk-K7MLXXPY.js} +2 -2
  62. package/dist/{chunk-Y6SCMXR6.js → chunk-KDPYXMUI.js} +2 -2
  63. package/dist/{chunk-XS33QBJA.js → chunk-LGXUGYUH.js} +50 -1
  64. package/dist/{chunk-I7LKI4WO.js → chunk-LYWTLNMG.js} +6 -8
  65. package/dist/chunk-OYTOLQ3N.js +15 -0
  66. package/dist/{chunk-XUTSZYYJ.js → chunk-QFAZ6QCT.js} +1 -4
  67. package/dist/{chunk-FRXZMCVA.js → chunk-ROTIPRM4.js} +1 -1
  68. package/dist/{chunk-BQL7UDIB.js → chunk-SW37HB27.js} +1 -1
  69. package/dist/{chunk-Z4JVEL7R.js → chunk-VCKQ45QA.js} +191 -2
  70. package/dist/{chunk-SDNTRS76.js → chunk-VDTC5DEM.js} +1 -1
  71. package/dist/{chunk-VMQU7YEJ.js → chunk-VF7YOT2W.js} +1 -1
  72. package/dist/{chunk-5GKNAQTG.js → chunk-VOC55PQV.js} +1 -1
  73. package/dist/{chunk-L7NOG547.js → chunk-VTUN7WSG.js} +1 -1
  74. package/dist/{chunk-GOPIMNVC.js → chunk-WIKEADOS.js} +1 -1
  75. package/dist/chunk-XDMHLQMH.js +202 -0
  76. package/dist/{chunk-4DUEIUUO.js → chunk-YRXVC4HL.js} +1 -1
  77. package/dist/{chunk-A22VQ5ZL.js → chunk-YX3U2CLP.js} +111 -55
  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
@@ -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-HUZW6SEO.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
  };
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  describeKeyCheck,
3
3
  keyCheckFromResponse
4
- } from "./chunk-BQL7UDIB.js";
4
+ } from "./chunk-SW37HB27.js";
5
5
  import {
6
6
  createIo
7
7
  } from "./chunk-5RUIFDTP.js";
@@ -4,7 +4,7 @@ import {
4
4
  resolveInstallSourceFor,
5
5
  writeFileAtomic,
6
6
  writeJsonAtomic
7
- } from "./chunk-L7NOG547.js";
7
+ } from "./chunk-VTUN7WSG.js";
8
8
  import {
9
9
  configFilePath
10
10
  } from "./chunk-2UMNXADU.js";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  isSafeReadOnlyCommand
3
- } from "./chunk-MC5WUOJJ.js";
3
+ } from "./chunk-HUZW6SEO.js";
4
4
 
5
5
  // src/ledger.ts
6
6
  import { appendFileSync, existsSync, mkdirSync, readFileSync, renameSync, rmSync, statSync } from "fs";
@@ -3,7 +3,7 @@ import {
3
3
  } from "./chunk-2UMNXADU.js";
4
4
  import {
5
5
  normalizeInstallSource
6
- } from "./chunk-MC5WUOJJ.js";
6
+ } from "./chunk-HUZW6SEO.js";
7
7
 
8
8
  // src/cli/fsx.ts
9
9
  import { copyFileSync, existsSync, fsyncSync, mkdirSync, openSync, closeSync, readFileSync, renameSync, unlinkSync, writeFileSync } from "fs";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  guardBinary
3
- } from "./chunk-3AJIK5AO.js";
3
+ } from "./chunk-67RNDRKL.js";
4
4
 
5
5
  // src/bell/install.ts
6
6
  import { join } from "path";
@@ -0,0 +1,202 @@
1
+ import {
2
+ canonicalGateInputs
3
+ } from "./chunk-VCKQ45QA.js";
4
+ import {
5
+ canonicalJson
6
+ } from "./chunk-67RNDRKL.js";
7
+ import {
8
+ writeFileAtomic
9
+ } from "./chunk-VTUN7WSG.js";
10
+ import {
11
+ BLOCKED_ACTION_GRANT_TTL_SECONDS,
12
+ blockedActionIdentity,
13
+ shouldDeferToNativeMode
14
+ } from "./chunk-HUZW6SEO.js";
15
+
16
+ // src/native-defer.ts
17
+ var nativeDeferOptions = () => {
18
+ const respect = process.env.PUSHARY_RESPECT_PERMISSION_MODE;
19
+ const bypass = process.env.PUSHARY_DEFER_BYPASS;
20
+ return {
21
+ respectPermissionMode: !(respect === "0" || respect === "false"),
22
+ deferBypass: bypass === "1" || bypass === "true"
23
+ };
24
+ };
25
+ var defersToNativeMode = (permissionMode, toolName) => shouldDeferToNativeMode(permissionMode, toolName, nativeDeferOptions());
26
+
27
+ // src/codex-call-decision.ts
28
+ import { createHash } from "crypto";
29
+ import { existsSync, mkdirSync, readFileSync, readdirSync, rmSync, statSync, writeFileSync } from "fs";
30
+ import { tmpdir, userInfo } from "os";
31
+ import { join } from "path";
32
+ var CODEX_DECISION_TTL_MS = 60 * 1e3;
33
+ var userScope = () => {
34
+ try {
35
+ const info = userInfo();
36
+ const id = typeof info.uid === "number" && info.uid >= 0 ? String(info.uid) : info.username;
37
+ return id.replace(/[^A-Za-z0-9_-]/g, "_").slice(0, 128) || "default";
38
+ } catch {
39
+ return "default";
40
+ }
41
+ };
42
+ var decisionDir = () => join(tmpdir(), `pushary-codex-decisions-${userScope()}`);
43
+ var decisionFile = (key) => join(decisionDir(), key);
44
+ var PRESENTATION_ONLY_INPUT_KEYS = ["description"];
45
+ var identifyingToolInput = (toolInput) => {
46
+ if (!toolInput) return {};
47
+ const identifying = {};
48
+ for (const [key, value] of Object.entries(toolInput)) {
49
+ if (PRESENTATION_ONLY_INPUT_KEYS.includes(key)) continue;
50
+ identifying[key] = value;
51
+ }
52
+ return identifying;
53
+ };
54
+ var codexCallKey = (identity) => {
55
+ if (!identity.sessionId || !identity.turnId || !identity.toolName) return null;
56
+ const material = canonicalJson([
57
+ identity.sessionId,
58
+ identity.turnId,
59
+ identity.toolName,
60
+ identifyingToolInput(identity.toolInput)
61
+ ]);
62
+ return createHash("sha256").update(material, "utf-8").digest("hex").slice(0, 32);
63
+ };
64
+ var parseDecision = (raw) => {
65
+ try {
66
+ const parsed = JSON.parse(raw);
67
+ if (parsed.kind === "approved") {
68
+ return typeof parsed.toolUseId === "string" && parsed.toolUseId ? { kind: "approved", toolUseId: parsed.toolUseId } : null;
69
+ }
70
+ if (parsed.kind === "handled") return { kind: "handled" };
71
+ if (parsed.kind === "denied") {
72
+ return { kind: "denied", reason: typeof parsed.reason === "string" ? parsed.reason : "" };
73
+ }
74
+ return null;
75
+ } catch {
76
+ return null;
77
+ }
78
+ };
79
+ var sweepExpired = (dir, nowMs) => {
80
+ try {
81
+ for (const name of readdirSync(dir)) {
82
+ const path = join(dir, name);
83
+ try {
84
+ if (nowMs - statSync(path).mtimeMs >= CODEX_DECISION_TTL_MS) rmSync(path, { force: true });
85
+ } catch {
86
+ continue;
87
+ }
88
+ }
89
+ } catch {
90
+ return;
91
+ }
92
+ };
93
+ var recordCodexCallDecision = (key, decision) => {
94
+ if (!key) return;
95
+ try {
96
+ const dir = decisionDir();
97
+ if (!existsSync(dir)) mkdirSync(dir, { recursive: true, mode: 448 });
98
+ else sweepExpired(dir, Date.now());
99
+ writeFileSync(decisionFile(key), JSON.stringify(decision), { encoding: "utf-8", mode: 384 });
100
+ } catch {
101
+ return;
102
+ }
103
+ };
104
+ var consumeCodexCallDecision = (key) => {
105
+ if (!key) return null;
106
+ const path = decisionFile(key);
107
+ try {
108
+ const fresh = Date.now() - statSync(path).mtimeMs < CODEX_DECISION_TTL_MS;
109
+ const raw = readFileSync(path, "utf-8");
110
+ rmSync(path, { force: true });
111
+ return fresh ? parseDecision(raw) : null;
112
+ } catch {
113
+ return null;
114
+ }
115
+ };
116
+
117
+ // src/blocked-action-hint.ts
118
+ import { createHash as createHash2 } from "crypto";
119
+ import { existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync2, readdirSync as readdirSync2, rmSync as rmSync2, statSync as statSync2 } from "fs";
120
+ import { tmpdir as tmpdir2 } from "os";
121
+ import { join as join2 } from "path";
122
+ var BLOCKED_ACTION_HINT_TTL_MS = BLOCKED_ACTION_GRANT_TTL_SECONDS * 1e3;
123
+ var FINGERPRINT = /^[0-9a-f]{64}$/;
124
+ var isFingerprint = (value) => typeof value === "string" && FINGERPRINT.test(value);
125
+ var blockedActionSubject = (source, sessionId, toolName, toolInput, cwd) => {
126
+ if (source !== "claude") return { source, sessionId, toolName, toolInput };
127
+ return cwd ? { source, sessionId, cwd, toolName, toolInput } : null;
128
+ };
129
+ var blockedActionFingerprint = ({ source = "claude", sessionId, toolName, toolInput, cwd }) => {
130
+ if (!sessionId || !toolInput) return null;
131
+ const canonical = canonicalGateInputs(source, toolName, [toolInput], cwd);
132
+ if (!canonical || canonical.toolInputs.length !== 1) return null;
133
+ const [canonicalInput] = canonical.toolInputs;
134
+ if (!canonicalInput) return null;
135
+ const subject = blockedActionSubject(source, sessionId, canonical.toolName, canonicalInput, cwd);
136
+ return subject ? createHash2("sha256").update(blockedActionIdentity(subject), "utf8").digest("hex") : null;
137
+ };
138
+ var hintDir = () => join2(tmpdir2(), `pushary-blocked-action-hints-${userScope()}`);
139
+ var hintFile = (fingerprint) => join2(hintDir(), fingerprint);
140
+ var isExpired = (mtimeMs, nowMs) => nowMs - mtimeMs >= BLOCKED_ACTION_HINT_TTL_MS;
141
+ var sweepExpired2 = (dir, nowMs) => {
142
+ try {
143
+ for (const name of readdirSync2(dir)) {
144
+ const path = join2(dir, name);
145
+ try {
146
+ if (isExpired(statSync2(path).mtimeMs, nowMs)) rmSync2(path, { force: true });
147
+ } catch {
148
+ continue;
149
+ }
150
+ }
151
+ } catch {
152
+ return;
153
+ }
154
+ };
155
+ var recordBlockedActionHint = (fingerprint) => {
156
+ if (!isFingerprint(fingerprint)) return false;
157
+ try {
158
+ const dir = hintDir();
159
+ if (!existsSync2(dir)) mkdirSync2(dir, { recursive: true, mode: 448 });
160
+ else sweepExpired2(dir, Date.now());
161
+ writeFileAtomic(hintFile(fingerprint), JSON.stringify({ fingerprint }), 384);
162
+ return true;
163
+ } catch {
164
+ return false;
165
+ }
166
+ };
167
+ var hasBlockedActionHint = (fingerprint) => {
168
+ if (!isFingerprint(fingerprint)) return false;
169
+ const path = hintFile(fingerprint);
170
+ try {
171
+ const stat = statSync2(path);
172
+ if (!stat.isFile()) return false;
173
+ if (isExpired(stat.mtimeMs, Date.now())) {
174
+ rmSync2(path, { force: true });
175
+ return false;
176
+ }
177
+ const parsed = JSON.parse(readFileSync2(path, "utf-8"));
178
+ return parsed?.fingerprint === fingerprint;
179
+ } catch {
180
+ return false;
181
+ }
182
+ };
183
+ var forgetBlockedActionHint = (fingerprint) => {
184
+ if (!isFingerprint(fingerprint)) return;
185
+ try {
186
+ rmSync2(hintFile(fingerprint), { force: true });
187
+ } catch {
188
+ return;
189
+ }
190
+ };
191
+
192
+ export {
193
+ defersToNativeMode,
194
+ userScope,
195
+ codexCallKey,
196
+ recordCodexCallDecision,
197
+ consumeCodexCallDecision,
198
+ blockedActionFingerprint,
199
+ recordBlockedActionHint,
200
+ hasBlockedActionHint,
201
+ forgetBlockedActionHint
202
+ };
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  parseVersion
3
- } from "./chunk-QV266EGS.js";
3
+ } from "./chunk-IBRX7LRE.js";
4
4
 
5
5
  // src/setup/agent-probe.ts
6
6
  import { execSync } from "child_process";