@pushary/agent-hooks 0.89.12 → 0.89.13

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 (71) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/README.md +1 -0
  3. package/dist/bin/pushary-bell-hook.js +4 -4
  4. package/dist/bin/pushary-bell.js +7 -7
  5. package/dist/bin/pushary-claude.js +133 -28
  6. package/dist/bin/pushary-clean.js +22 -13
  7. package/dist/bin/pushary-codex-bridge.js +9 -9
  8. package/dist/bin/pushary-codex-hook.js +8 -8
  9. package/dist/bin/pushary-codex.js +6 -6
  10. package/dist/bin/pushary-connect.js +8 -8
  11. package/dist/bin/pushary-daemon.js +15 -15
  12. package/dist/bin/pushary-disconnect.js +7 -7
  13. package/dist/bin/pushary-doctor.js +18 -18
  14. package/dist/bin/pushary-elicitation-hook.js +3 -3
  15. package/dist/bin/pushary-gemini-bridge.js +9 -9
  16. package/dist/bin/pushary-gemini-hook.js +7 -7
  17. package/dist/bin/pushary-hook.js +12 -12
  18. package/dist/bin/pushary-login.js +6 -6
  19. package/dist/bin/pushary-logout.js +6 -6
  20. package/dist/bin/pushary-mode.js +5 -5
  21. package/dist/bin/pushary-notification-hook.js +6 -6
  22. package/dist/bin/pushary-opencode-hook.js +7 -7
  23. package/dist/bin/pushary-permission-denied-hook.js +10 -10
  24. package/dist/bin/pushary-permission-hook.js +10 -10
  25. package/dist/bin/pushary-post-hook.js +6 -6
  26. package/dist/bin/pushary-prompt-hook.js +6 -6
  27. package/dist/bin/pushary-session-end-hook.js +6 -6
  28. package/dist/bin/pushary-session-start-hook.js +6 -6
  29. package/dist/bin/pushary-setup.js +19 -19
  30. package/dist/bin/pushary-stats.js +4 -4
  31. package/dist/bin/pushary-status.js +10 -10
  32. package/dist/bin/pushary-stop-hook.js +6 -6
  33. package/dist/bin/pushary-stopfailure-hook.js +6 -6
  34. package/dist/bin/pushary-suggestions.js +2 -2
  35. package/dist/bin/pushary-transcripts.d.ts +1 -0
  36. package/dist/bin/pushary-transcripts.js +79 -0
  37. package/dist/bin/pushary-upgrade.js +14 -14
  38. package/dist/bin/pushary-wait.js +5 -5
  39. package/dist/bin/pushary.js +3 -2
  40. package/dist/{chunk-OVEGEQSB.js → chunk-3YGGEDJK.js} +1 -1
  41. package/dist/{chunk-WDVQ6O4C.js → chunk-4IPSMEGT.js} +67 -5
  42. package/dist/{chunk-OCMOTJTW.js → chunk-5MNTWAAJ.js} +7 -7
  43. package/dist/{chunk-NBRM3FMD.js → chunk-5YEF55AX.js} +1 -1
  44. package/dist/{chunk-PS7DQ2SU.js → chunk-7JZCADBR.js} +2 -2
  45. package/dist/{chunk-XREW7RQ3.js → chunk-ANMHIZZY.js} +1 -1
  46. package/dist/{chunk-KVHP5ZME.js → chunk-B2HND22S.js} +1 -1
  47. package/dist/{chunk-VPLQFXCH.js → chunk-C56VHSXU.js} +1 -1
  48. package/dist/{chunk-6GU55AQQ.js → chunk-CBGGUEKE.js} +1 -1
  49. package/dist/{chunk-FCUUVHTS.js → chunk-FOGW6O2Y.js} +1 -1
  50. package/dist/{chunk-RLQWNEPM.js → chunk-GMJDQDXU.js} +14 -0
  51. package/dist/{chunk-5NDB3YOE.js → chunk-JTBDBKHU.js} +1 -1
  52. package/dist/{chunk-6V7ZDH3A.js → chunk-JVLB5CAJ.js} +3 -3
  53. package/dist/{chunk-MFNQ5IMW.js → chunk-KNLKHJZR.js} +1 -1
  54. package/dist/{chunk-JJV5Q4KG.js → chunk-M3UABYRT.js} +1 -1
  55. package/dist/{chunk-7HYJPOGG.js → chunk-MNTZKFZI.js} +4 -4
  56. package/dist/{chunk-NESKLV6R.js → chunk-MSTHT2M7.js} +3 -3
  57. package/dist/{chunk-JVZX5YRJ.js → chunk-N4QJ3CLX.js} +1 -1
  58. package/dist/{chunk-RNKPLUWC.js → chunk-NBDU3R3G.js} +1 -1
  59. package/dist/{chunk-OADGBOP7.js → chunk-OX35BSPV.js} +4 -4
  60. package/dist/{chunk-DHR4F2CR.js → chunk-RAEPAUNJ.js} +1 -1
  61. package/dist/{chunk-GKPURU7P.js → chunk-S4BLNILH.js} +1 -1
  62. package/dist/{chunk-3XSP5SML.js → chunk-VASZ6TWW.js} +2 -2
  63. package/dist/{chunk-OTPJ4FUR.js → chunk-WHDSJ6PD.js} +1 -1
  64. package/dist/chunk-YKLCWWEI.js +76 -0
  65. package/dist/{chunk-327YMKP5.js → chunk-YXT55PYF.js} +1 -1
  66. package/dist/{chunk-Y4WVZIEI.js → chunk-ZO43JTT3.js} +1 -1
  67. package/dist/{chunk-6VXNA5JB.js → chunk-ZP3SNUP7.js} +1 -1
  68. package/dist/{chunk-7DK3ZH3N.js → chunk-ZWEKONU6.js} +1 -1
  69. package/dist/src/index.js +10 -10
  70. package/package.json +3 -2
  71. /package/dist/{chunk-3MCEES6Z.js → chunk-QINYXLA5.js} +0 -0
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  redactSecrets
3
- } from "./chunk-3MCEES6Z.js";
3
+ } from "./chunk-QINYXLA5.js";
4
4
 
5
5
  // src/crypto.ts
6
6
  import nacl from "tweetnacl";
@@ -33,7 +33,67 @@ var encryptRecord = (plaintext, sessionKey) => {
33
33
  };
34
34
 
35
35
  // src/transcript-capture.ts
36
- import { createHash } from "crypto";
36
+ import { createHmac } from "crypto";
37
+
38
+ // src/transcript-budget.ts
39
+ var CIPHERTEXT_BUDGET_CHARS = 64 * 1024;
40
+ var SECRETBOX_OVERHEAD_BYTES = 24 + 16;
41
+ var MAX_PLAINTEXT_BYTES = Math.floor(CIPHERTEXT_BUDGET_CHARS / 4) * 3 - SECRETBOX_OVERHEAD_BYTES;
42
+ var MIN_KEEP_CHARS = 200;
43
+ var note = (dropped) => `
44
+ [Pushary truncated ${dropped} bytes to fit the transcript size limit]`;
45
+ var stringSlots = (value, out = []) => {
46
+ if (Array.isArray(value)) {
47
+ value.forEach((inner, index) => {
48
+ if (typeof inner === "string") {
49
+ out.push({ get: () => value[index], set: (v) => {
50
+ value[index] = v;
51
+ } });
52
+ } else {
53
+ stringSlots(inner, out);
54
+ }
55
+ });
56
+ } else if (value && typeof value === "object") {
57
+ const object = value;
58
+ for (const key of Object.keys(object)) {
59
+ const inner = object[key];
60
+ if (typeof inner === "string") {
61
+ out.push({ get: () => object[key], set: (v) => {
62
+ object[key] = v;
63
+ } });
64
+ } else {
65
+ stringSlots(inner, out);
66
+ }
67
+ }
68
+ }
69
+ return out;
70
+ };
71
+ var jsonBytes = (value) => Buffer.byteLength(JSON.stringify(value), "utf-8");
72
+ var fitWithinPlaintextBudget = (record, budget = MAX_PLAINTEXT_BYTES) => {
73
+ if (jsonBytes(record) <= budget) return false;
74
+ const slots = stringSlots(record).map((slot) => ({
75
+ slot,
76
+ originalBytes: Buffer.byteLength(slot.get(), "utf-8")
77
+ }));
78
+ if (slots.length === 0) return false;
79
+ let truncated = false;
80
+ for (let pass = 0; pass < 64 && jsonBytes(record) > budget; pass += 1) {
81
+ let worst = slots[0];
82
+ for (const candidate of slots) {
83
+ if (candidate.slot.get().length > worst.slot.get().length) worst = candidate;
84
+ }
85
+ const current = worst.slot.get();
86
+ const keep = Math.max(MIN_KEEP_CHARS, Math.floor(current.length / 2));
87
+ const kept = current.slice(0, keep);
88
+ const replacement = kept + note(worst.originalBytes - Buffer.byteLength(kept, "utf-8"));
89
+ if (replacement.length >= current.length) break;
90
+ worst.slot.set(replacement);
91
+ truncated = true;
92
+ }
93
+ return truncated;
94
+ };
95
+
96
+ // src/transcript-capture.ts
37
97
  var INTERNAL_TYPES = /* @__PURE__ */ new Set([
38
98
  "file-history-snapshot",
39
99
  "change",
@@ -52,7 +112,7 @@ var redactDeep = (value) => {
52
112
  }
53
113
  return value;
54
114
  };
55
- var localIdFor = (parsed, rawLine) => typeof parsed.uuid === "string" && parsed.uuid.length > 0 ? parsed.uuid : createHash("sha256").update(rawLine).digest("hex").slice(0, 32);
115
+ var localIdFor = (parsed, rawLine, sessionKey) => typeof parsed.uuid === "string" && parsed.uuid.length > 0 ? parsed.uuid : createHmac("sha256", sessionKey).update(rawLine).digest("hex").slice(0, 32);
56
116
  var buildTranscriptRecords = (lines, sessionKey, seen = /* @__PURE__ */ new Set()) => {
57
117
  const records = [];
58
118
  const nextSeen = new Set(seen);
@@ -67,10 +127,12 @@ var buildTranscriptRecords = (lines, sessionKey, seen = /* @__PURE__ */ new Set(
67
127
  }
68
128
  const type = typeof parsed.type === "string" ? parsed.type : "";
69
129
  if (INTERNAL_TYPES.has(type)) continue;
70
- const localId = localIdFor(parsed, trimmed);
130
+ const localId = localIdFor(parsed, trimmed, sessionKey);
71
131
  if (nextSeen.has(localId)) continue;
72
132
  nextSeen.add(localId);
73
- const content = encryptRecord(JSON.stringify(redactDeep(parsed)), sessionKey);
133
+ const redacted = redactDeep(parsed);
134
+ fitWithinPlaintextBudget(redacted);
135
+ const content = encryptRecord(JSON.stringify(redacted), sessionKey);
74
136
  records.push({ localId, content });
75
137
  }
76
138
  return { records, seen: nextSeen };
@@ -1,14 +1,14 @@
1
1
  import {
2
2
  PRETOOLUSE_HANDLED_TOOLS,
3
3
  PRETOOLUSE_PHONE_ANSWERED_TOOLS
4
- } from "./chunk-KVHP5ZME.js";
4
+ } from "./chunk-B2HND22S.js";
5
5
  import {
6
6
  defersToNativeMode
7
- } from "./chunk-GKPURU7P.js";
7
+ } from "./chunk-S4BLNILH.js";
8
8
  import {
9
9
  isGatingMoment,
10
10
  recordKeylessMoment
11
- } from "./chunk-FCUUVHTS.js";
11
+ } from "./chunk-FOGW6O2Y.js";
12
12
  import {
13
13
  denyReasonFrom,
14
14
  isDeferAnswer
@@ -20,7 +20,7 @@ import {
20
20
  readLastUserPrompt,
21
21
  repoKeyFor,
22
22
  throttlePass
23
- } from "./chunk-OADGBOP7.js";
23
+ } from "./chunk-OX35BSPV.js";
24
24
  import {
25
25
  DEFAULT_SESSION,
26
26
  askUser,
@@ -37,10 +37,10 @@ import {
37
37
  deriveToolTarget,
38
38
  describeToolCall,
39
39
  scopePathFor
40
- } from "./chunk-JVZX5YRJ.js";
40
+ } from "./chunk-N4QJ3CLX.js";
41
41
  import {
42
42
  getMachineId
43
- } from "./chunk-5NDB3YOE.js";
43
+ } from "./chunk-JTBDBKHU.js";
44
44
  import {
45
45
  getApiKey,
46
46
  getBaseUrl
@@ -55,7 +55,7 @@ import {
55
55
  parseAgentQuestionAnswers,
56
56
  parseAgentQuestions,
57
57
  resolveGate
58
- } from "./chunk-3MCEES6Z.js";
58
+ } from "./chunk-QINYXLA5.js";
59
59
 
60
60
  // src/decision-episode.ts
61
61
  var EPISODE_PATH = "/api/agent/decision-episode";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  parseVersion
3
- } from "./chunk-6V7ZDH3A.js";
3
+ } from "./chunk-JVLB5CAJ.js";
4
4
 
5
5
  // src/setup/agent-probe.ts
6
6
  import { execSync } from "child_process";
@@ -7,10 +7,10 @@ import {
7
7
  } from "./chunk-L4ZCFMWW.js";
8
8
  import {
9
9
  getMachineId
10
- } from "./chunk-5NDB3YOE.js";
10
+ } from "./chunk-JTBDBKHU.js";
11
11
  import {
12
12
  pusharyDir
13
- } from "./chunk-OVEGEQSB.js";
13
+ } from "./chunk-3YGGEDJK.js";
14
14
  import {
15
15
  getPackageVersion
16
16
  } from "./chunk-DINDL2CF.js";
@@ -3,7 +3,7 @@ import {
3
3
  } from "./chunk-2UMNXADU.js";
4
4
  import {
5
5
  normalizeInstallSource
6
- } from "./chunk-3MCEES6Z.js";
6
+ } from "./chunk-QINYXLA5.js";
7
7
 
8
8
  // src/install-source.ts
9
9
  import { readFileSync } from "fs";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  ASKED_IN_EVERY_MODE_TOOLS
3
- } from "./chunk-3MCEES6Z.js";
3
+ } from "./chunk-QINYXLA5.js";
4
4
 
5
5
  // src/claude-tools.ts
6
6
  var PRETOOLUSE_GATED_TOOLS = [
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  readConfigFileKey
3
- } from "./chunk-RNKPLUWC.js";
3
+ } from "./chunk-NBDU3R3G.js";
4
4
 
5
5
  // src/bell/fleet.ts
6
6
  import { closeSync, constants, futimesSync, lstatSync, mkdirSync, openSync, readdirSync, unlinkSync } from "fs";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  pusharyDir
3
- } from "./chunk-OVEGEQSB.js";
3
+ } from "./chunk-3YGGEDJK.js";
4
4
  import {
5
5
  readJsonSafe
6
6
  } from "./chunk-4LL6LG5X.js";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  isSafeReadOnlyCommand
3
- } from "./chunk-3MCEES6Z.js";
3
+ } from "./chunk-QINYXLA5.js";
4
4
 
5
5
  // src/ledger.ts
6
6
  import { appendFileSync, existsSync, mkdirSync, readFileSync, renameSync, rmSync, statSync } from "fs";
@@ -69,6 +69,16 @@ var COMMAND_LIST = [
69
69
  name: "hook",
70
70
  module: "pushary-hook",
71
71
  summary: "Run as a PreToolUse hook (reads stdin, writes stdout)"
72
+ },
73
+ {
74
+ name: "transcripts",
75
+ module: "pushary-transcripts",
76
+ summary: "Show or re-trust the keys this machine seals transcripts to",
77
+ detail: [
78
+ "Sync stops if the phone key or the compliance recovery key changes, because a",
79
+ "change is either your reinstall or a server handing itself a copy. Compare the",
80
+ "fingerprint with the app under Settings, then accept it with --rotate."
81
+ ]
72
82
  }
73
83
  ];
74
84
  var COMMANDS = COMMAND_LIST;
@@ -79,6 +89,10 @@ var COMMAND_OPTIONS = {
79
89
  ["--dry-run", "List everything it would remove and change nothing"],
80
90
  ["--yes", "Remove everything without asking"]
81
91
  ],
92
+ transcripts: [
93
+ ["status", "Show the pinned keys and their fingerprints (default)"],
94
+ ["trust --rotate", "Accept the keys the server is offering now and resume syncing"]
95
+ ],
82
96
  mode: [
83
97
  ["<mode>", "One of: push_only, push_first, terminal_only, notify_only"],
84
98
  ["status", "Show the current override"],
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  pusharyDir
3
- } from "./chunk-OVEGEQSB.js";
3
+ } from "./chunk-3YGGEDJK.js";
4
4
  import {
5
5
  writeFileAtomic
6
6
  } from "./chunk-4LL6LG5X.js";
@@ -1,17 +1,17 @@
1
1
  import {
2
2
  PRETOOLUSE_GATED_TOOLS,
3
3
  PRETOOLUSE_PHONE_ANSWERED_TOOLS
4
- } from "./chunk-KVHP5ZME.js";
4
+ } from "./chunk-B2HND22S.js";
5
5
  import {
6
6
  guardBinary
7
7
  } from "./chunk-VF3RTVWY.js";
8
8
  import {
9
9
  isProtectedByScope
10
- } from "./chunk-OVEGEQSB.js";
10
+ } from "./chunk-3YGGEDJK.js";
11
11
  import {
12
12
  HOOK_BUDGETS,
13
13
  HOOK_LOCATOR_BINARY
14
- } from "./chunk-3MCEES6Z.js";
14
+ } from "./chunk-QINYXLA5.js";
15
15
 
16
16
  // src/claude-config.ts
17
17
  import { join } from "path";
@@ -11,7 +11,7 @@ import {
11
11
  } from "./chunk-BSZYIAZL.js";
12
12
  import {
13
13
  reportedInstallSource
14
- } from "./chunk-XREW7RQ3.js";
14
+ } from "./chunk-ANMHIZZY.js";
15
15
 
16
16
  // src/reach.ts
17
17
  var reachVerdict = (channels) => {
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  readTranscriptPreference
3
- } from "./chunk-RNKPLUWC.js";
3
+ } from "./chunk-NBDU3R3G.js";
4
4
 
5
5
  // src/transcript-setting.ts
6
6
  var fromEnv = () => {
@@ -1,11 +1,11 @@
1
1
  import {
2
2
  detectClaudeVersion
3
- } from "./chunk-NBRM3FMD.js";
3
+ } from "./chunk-5YEF55AX.js";
4
4
  import {
5
5
  claudeWired,
6
6
  codexWired,
7
7
  geminiWired
8
- } from "./chunk-NESKLV6R.js";
8
+ } from "./chunk-MSTHT2M7.js";
9
9
  import {
10
10
  GEMINI_HOOK_BINARY,
11
11
  addClaudeMcpServer,
@@ -14,7 +14,7 @@ import {
14
14
  addPusharyHooks,
15
15
  addPusharyToolPermissions,
16
16
  hasGeminiHooks
17
- } from "./chunk-6V7ZDH3A.js";
17
+ } from "./chunk-JVLB5CAJ.js";
18
18
  import {
19
19
  guardBinary
20
20
  } from "./chunk-VF3RTVWY.js";
@@ -32,7 +32,7 @@ import {
32
32
  codexHooksJson,
33
33
  hasCodexHooks,
34
34
  vscodePluginMcp
35
- } from "./chunk-OVEGEQSB.js";
35
+ } from "./chunk-3YGGEDJK.js";
36
36
  import {
37
37
  readJsonSafe
38
38
  } from "./chunk-4LL6LG5X.js";
@@ -1,12 +1,12 @@
1
1
  import {
2
2
  hasGeminiHooks
3
- } from "./chunk-6V7ZDH3A.js";
3
+ } from "./chunk-JVLB5CAJ.js";
4
4
  import {
5
5
  hasCodexHooks
6
- } from "./chunk-OVEGEQSB.js";
6
+ } from "./chunk-3YGGEDJK.js";
7
7
  import {
8
8
  HOOK_LOCATOR_BINARY
9
- } from "./chunk-3MCEES6Z.js";
9
+ } from "./chunk-QINYXLA5.js";
10
10
 
11
11
  // src/diagnostics/wiring.ts
12
12
  var record = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : null;
@@ -5,7 +5,7 @@ import {
5
5
  FILE_TARGET_TOOLS,
6
6
  redactSecrets,
7
7
  redactSecretsDeep
8
- } from "./chunk-3MCEES6Z.js";
8
+ } from "./chunk-QINYXLA5.js";
9
9
 
10
10
  // ../hook-mapping/src/describe.ts
11
11
  import { isAbsolute, relative, resolve } from "path";
@@ -3,7 +3,7 @@ import {
3
3
  } from "./chunk-BC3VCZ3E.js";
4
4
  import {
5
5
  resolveInstallSourceFor
6
- } from "./chunk-XREW7RQ3.js";
6
+ } from "./chunk-ANMHIZZY.js";
7
7
  import {
8
8
  backupFile,
9
9
  readJsonSafe,
@@ -26,16 +26,16 @@ import {
26
26
  summarizeStop,
27
27
  taskTitleFrom,
28
28
  toolResultErrorText
29
- } from "./chunk-JVZX5YRJ.js";
29
+ } from "./chunk-N4QJ3CLX.js";
30
30
  import {
31
31
  getMachineId
32
- } from "./chunk-5NDB3YOE.js";
32
+ } from "./chunk-JTBDBKHU.js";
33
33
  import {
34
34
  withRetry
35
35
  } from "./chunk-BSZYIAZL.js";
36
36
  import {
37
37
  reportedInstallSource
38
- } from "./chunk-XREW7RQ3.js";
38
+ } from "./chunk-ANMHIZZY.js";
39
39
  import {
40
40
  getApiKey,
41
41
  getBaseUrl
@@ -51,7 +51,7 @@ import {
51
51
  pendingCommandEvents,
52
52
  resolveAutoResolveOrigin,
53
53
  resolvePolicy
54
- } from "./chunk-3MCEES6Z.js";
54
+ } from "./chunk-QINYXLA5.js";
55
55
 
56
56
  // src/policy.ts
57
57
  import { createHash } from "crypto";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  getMachineId
3
- } from "./chunk-5NDB3YOE.js";
3
+ } from "./chunk-JTBDBKHU.js";
4
4
  import {
5
5
  getPackageVersion
6
6
  } from "./chunk-DINDL2CF.js";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  shouldDeferToNativeMode
3
- } from "./chunk-3MCEES6Z.js";
3
+ } from "./chunk-QINYXLA5.js";
4
4
 
5
5
  // src/native-defer.ts
6
6
  var nativeDeferOptions = () => {
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  openCodeConfigDirFrom
3
- } from "./chunk-7DK3ZH3N.js";
3
+ } from "./chunk-ZWEKONU6.js";
4
4
  import {
5
5
  codexHomeFrom
6
- } from "./chunk-OVEGEQSB.js";
6
+ } from "./chunk-3YGGEDJK.js";
7
7
 
8
8
  // src/vscode-config.ts
9
9
  import { homedir } from "os";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  renderCommandHelp
3
- } from "./chunk-RLQWNEPM.js";
3
+ } from "./chunk-GMJDQDXU.js";
4
4
  import {
5
5
  getPackageVersion
6
6
  } from "./chunk-DINDL2CF.js";
@@ -0,0 +1,76 @@
1
+ import {
2
+ writeJsonAtomicDurable
3
+ } from "./chunk-4LL6LG5X.js";
4
+
5
+ // src/transcript-recipient-pin.ts
6
+ import { existsSync, mkdirSync, readFileSync, rmSync } from "fs";
7
+ import { homedir } from "os";
8
+ import { dirname, join } from "path";
9
+ var pinFile = () => process.env.PUSHARY_RECIPIENT_PIN_FILE?.trim() || join(homedir(), ".pushary", "recipients.json");
10
+ var asPin = (offer) => ({
11
+ phonePublicKey: offer.phonePublicKey,
12
+ providerKeyId: offer.provider?.keyId ?? null,
13
+ providerPublicKey: offer.provider?.publicKey ?? null
14
+ });
15
+ var loadPin = () => {
16
+ const path = pinFile();
17
+ if (!existsSync(path)) return null;
18
+ try {
19
+ const raw = JSON.parse(readFileSync(path, "utf-8"));
20
+ if (typeof raw.phonePublicKey !== "string" || raw.phonePublicKey.length === 0) return null;
21
+ return {
22
+ phonePublicKey: raw.phonePublicKey,
23
+ providerKeyId: typeof raw.providerKeyId === "string" ? raw.providerKeyId : null,
24
+ providerPublicKey: typeof raw.providerPublicKey === "string" ? raw.providerPublicKey : null
25
+ };
26
+ } catch {
27
+ return null;
28
+ }
29
+ };
30
+ var savePin = (pin) => {
31
+ const path = pinFile();
32
+ try {
33
+ mkdirSync(dirname(path), { recursive: true, mode: 448 });
34
+ writeJsonAtomicDurable(path, pin, 384);
35
+ return true;
36
+ } catch {
37
+ return false;
38
+ }
39
+ };
40
+ var clearPin = () => {
41
+ try {
42
+ rmSync(pinFile(), { force: true });
43
+ } catch {
44
+ }
45
+ };
46
+ var verifyRecipients = (offer, pinned) => {
47
+ if (!pinned) return { kind: "first-use", pin: asPin(offer) };
48
+ const next = asPin(offer);
49
+ const changed = [];
50
+ if (next.phonePublicKey !== pinned.phonePublicKey) changed.push("the phone key this machine seals to");
51
+ if (next.providerKeyId !== pinned.providerKeyId) changed.push("the compliance recovery key id");
52
+ if (next.providerPublicKey !== pinned.providerPublicKey) changed.push("the compliance recovery key");
53
+ return changed.length === 0 ? { kind: "match" } : { kind: "changed", changed };
54
+ };
55
+ var fingerprint = (publicKey) => {
56
+ const hex = Buffer.from(publicKey, "base64").toString("hex").toUpperCase();
57
+ return (hex.slice(0, 8).match(/.{2}/g) ?? []).join(" ");
58
+ };
59
+ var changedRecipientsMessage = (verdict, offer) => [
60
+ "[pushary] Transcript sync stopped: the keys this machine seals transcripts to have changed.",
61
+ ...verdict.changed.map((what) => `[pushary] changed: ${what}`),
62
+ `[pushary] now offered: phone ${fingerprint(offer.phonePublicKey)}${offer.provider ? `, recovery ${offer.provider.keyId.slice(0, 8)}` : ", no recovery key"}`,
63
+ "[pushary] If you reinstalled the app or rotated a key, this is expected. Compare the",
64
+ "[pushary] fingerprint above with the one in the app under Settings, then run:",
65
+ "[pushary] pushary transcripts trust --rotate",
66
+ "[pushary] Until then nothing is uploaded, so no turn is sealed to a key you have not seen."
67
+ ].join("\n");
68
+
69
+ export {
70
+ loadPin,
71
+ savePin,
72
+ clearPin,
73
+ verifyRecipients,
74
+ fingerprint,
75
+ changedRecipientsMessage
76
+ };
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  describeKeyCheck,
3
3
  keyCheckFromResponse
4
- } from "./chunk-Y4WVZIEI.js";
4
+ } from "./chunk-ZO43JTT3.js";
5
5
  import {
6
6
  createIo
7
7
  } from "./chunk-5RUIFDTP.js";
@@ -12,7 +12,7 @@ import {
12
12
  } from "./chunk-2UMNXADU.js";
13
13
  import {
14
14
  isValidApiKey
15
- } from "./chunk-3MCEES6Z.js";
15
+ } from "./chunk-QINYXLA5.js";
16
16
 
17
17
  // src/session.ts
18
18
  var IDENTITY_PATH = "/api/v1/server/identity";
@@ -3,7 +3,7 @@ import {
3
3
  decodePublicKey,
4
4
  generateSessionKey,
5
5
  wrapSessionKey
6
- } from "./chunk-WDVQ6O4C.js";
6
+ } from "./chunk-4IPSMEGT.js";
7
7
  import {
8
8
  fetchTranscriptRecipients,
9
9
  uploadTranscriptRecords
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  HOOK_BUDGETS,
3
3
  OPENCODE_APPROVAL_EVENT
4
- } from "./chunk-3MCEES6Z.js";
4
+ } from "./chunk-QINYXLA5.js";
5
5
 
6
6
  // src/opencode-plugin.ts
7
7
  var OPENCODE_PLUGIN_MARKER = "pushary-opencode-plugin w1";
package/dist/src/index.js CHANGED
@@ -1,12 +1,12 @@
1
1
  import {
2
2
  handlePreToolUse
3
- } from "../chunk-OCMOTJTW.js";
4
- import "../chunk-KVHP5ZME.js";
3
+ } from "../chunk-5MNTWAAJ.js";
4
+ import "../chunk-B2HND22S.js";
5
5
  import {
6
6
  fetchAccountPublicKey
7
7
  } from "../chunk-QZCFVWA7.js";
8
- import "../chunk-GKPURU7P.js";
9
- import "../chunk-FCUUVHTS.js";
8
+ import "../chunk-S4BLNILH.js";
9
+ import "../chunk-FOGW6O2Y.js";
10
10
  import "../chunk-YHG74UFF.js";
11
11
  import {
12
12
  fetchModeOverride,
@@ -16,20 +16,20 @@ import {
16
16
  handlePostToolUse,
17
17
  handleStop,
18
18
  reportEvent
19
- } from "../chunk-OADGBOP7.js";
19
+ } from "../chunk-OX35BSPV.js";
20
20
  import {
21
21
  askUser,
22
22
  cancelQuestion,
23
23
  waitForAnswer
24
24
  } from "../chunk-JHJHCMAF.js";
25
- import "../chunk-JVZX5YRJ.js";
26
- import "../chunk-5NDB3YOE.js";
27
- import "../chunk-OVEGEQSB.js";
25
+ import "../chunk-N4QJ3CLX.js";
26
+ import "../chunk-JTBDBKHU.js";
27
+ import "../chunk-3YGGEDJK.js";
28
28
  import "../chunk-BSZYIAZL.js";
29
29
  import "../chunk-SAF6HGAA.js";
30
30
  import "../chunk-7QLSKOSU.js";
31
31
  import "../chunk-KZERVKTD.js";
32
- import "../chunk-XREW7RQ3.js";
32
+ import "../chunk-ANMHIZZY.js";
33
33
  import "../chunk-4LL6LG5X.js";
34
34
  import {
35
35
  getApiKey,
@@ -37,7 +37,7 @@ import {
37
37
  } from "../chunk-2UMNXADU.js";
38
38
  import {
39
39
  resolvePolicy
40
- } from "../chunk-3MCEES6Z.js";
40
+ } from "../chunk-QINYXLA5.js";
41
41
  export {
42
42
  askUser,
43
43
  cancelQuestion,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pushary/agent-hooks",
3
- "version": "0.89.12",
3
+ "version": "0.89.13",
4
4
  "description": "Permission hooks for AI coding agents: route tool approvals through Pushary push notifications",
5
5
  "keywords": [
6
6
  "pushary",
@@ -72,7 +72,8 @@
72
72
  "pushary-upgrade": "./dist/bin/pushary-upgrade.js",
73
73
  "pushary-bell": "./dist/bin/pushary-bell.js",
74
74
  "pushary-bell-hook": "./dist/bin/pushary-bell-hook.js",
75
- "pushary-disconnect": "./dist/bin/pushary-disconnect.js"
75
+ "pushary-disconnect": "./dist/bin/pushary-disconnect.js",
76
+ "pushary-transcripts": "./dist/bin/pushary-transcripts.js"
76
77
  },
77
78
  "files": [
78
79
  "dist",
File without changes