pushary 1.9.88 → 1.9.90

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 (126) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/dist/{api-BE4B7HBE.js → api-ORGHXO4N.js} +10 -10
  3. package/dist/bin/pushary-bell-hook.js +26 -26
  4. package/dist/bin/pushary-bell.js +10 -10
  5. package/dist/bin/pushary-claude.js +27 -27
  6. package/dist/bin/pushary-clean.js +34 -34
  7. package/dist/bin/pushary-codex-bridge.js +21 -21
  8. package/dist/bin/pushary-codex-hook.js +31 -31
  9. package/dist/bin/pushary-codex-session.js +8 -8
  10. package/dist/bin/pushary-codex.js +14 -14
  11. package/dist/bin/pushary-connect.js +27 -27
  12. package/dist/bin/pushary-cowork.js +5 -5
  13. package/dist/bin/pushary-daemon.js +39 -39
  14. package/dist/bin/pushary-disconnect.js +26 -26
  15. package/dist/bin/pushary-display-hook.js +17 -17
  16. package/dist/bin/pushary-doctor.js +42 -42
  17. package/dist/bin/pushary-elicitation-hook.js +26 -26
  18. package/dist/bin/pushary-gemini-bridge.js +15 -15
  19. package/dist/bin/pushary-gemini-hook.js +28 -28
  20. package/dist/bin/pushary-hook.js +30 -30
  21. package/dist/bin/pushary-login.js +20 -20
  22. package/dist/bin/pushary-logout.js +25 -25
  23. package/dist/bin/pushary-mcp.js +11 -11
  24. package/dist/bin/pushary-mode.js +15 -15
  25. package/dist/bin/pushary-notification-hook.js +25 -25
  26. package/dist/bin/pushary-opencode-hook.js +27 -27
  27. package/dist/bin/pushary-permission-denied-hook.js +30 -30
  28. package/dist/bin/pushary-permission-hook.js +30 -30
  29. package/dist/bin/pushary-post-hook.js +25 -25
  30. package/dist/bin/pushary-prompt-hook.js +25 -25
  31. package/dist/bin/pushary-session-end-hook.js +27 -27
  32. package/dist/bin/pushary-session-start-hook.js +35 -35
  33. package/dist/bin/pushary-setup.js +48 -48
  34. package/dist/bin/pushary-stats.js +5 -5
  35. package/dist/bin/pushary-status.js +32 -32
  36. package/dist/bin/pushary-stop-hook.js +25 -25
  37. package/dist/bin/pushary-stopfailure-hook.js +25 -25
  38. package/dist/bin/pushary-suggestions.js +15 -15
  39. package/dist/bin/pushary-transcript-register.js +18 -18
  40. package/dist/bin/pushary-transcripts.js +59 -30
  41. package/dist/bin/pushary-upgrade.js +40 -40
  42. package/dist/bin/pushary-wait.js +15 -15
  43. package/dist/bin/pushary.js +7 -7
  44. package/dist/{chunk-N7OMDSXK.js → chunk-2EOTXWFK.js} +3 -3
  45. package/dist/{chunk-3BG5S7MB.js → chunk-2RUJIPNN.js} +2 -2
  46. package/dist/{chunk-Z2LYELNQ.js → chunk-2U6FPMXG.js} +3 -3
  47. package/dist/{chunk-NP65LEAY.js → chunk-32F3BWRY.js} +1 -1
  48. package/dist/{chunk-2NJNC26C.js → chunk-3EVOYLXQ.js} +5 -5
  49. package/dist/{chunk-QQCGYHYG.js → chunk-3N4GETIW.js} +2 -2
  50. package/dist/{chunk-FRUKY6KV.js → chunk-4V25M4X5.js} +2 -2
  51. package/dist/{chunk-JQMOQLM2.js → chunk-53YQO4HL.js} +151 -93
  52. package/dist/{chunk-6RFDH4JU.js → chunk-5DTOMJVD.js} +2 -2
  53. package/dist/{chunk-3ZE7VQOU.js → chunk-6B4O7XNC.js} +6 -6
  54. package/dist/{chunk-P4W5TP2X.js → chunk-6LJOCQBB.js} +1 -1
  55. package/dist/{chunk-CAXE3TYX.js → chunk-6P4II3ZL.js} +5 -5
  56. package/dist/{chunk-JOULYVW6.js → chunk-72UCLJJW.js} +1 -1
  57. package/dist/{chunk-LHXJYSNX.js → chunk-7BEQB3X4.js} +1 -1
  58. package/dist/{chunk-NOGDU4XS.js → chunk-7EGNVZLK.js} +2 -2
  59. package/dist/{chunk-I6MNMAS7.js → chunk-7MN3KPHM.js} +38 -38
  60. package/dist/{chunk-RKX2ZOIG.js → chunk-AF3OHGKE.js} +1 -1
  61. package/dist/{chunk-O53SJBBB.js → chunk-ALT4KW5M.js} +5 -5
  62. package/dist/{chunk-D7ZJ3AUK.js → chunk-ALTGRWSO.js} +1 -1
  63. package/dist/{chunk-6GDZ4RYR.js → chunk-APT7KJ5I.js} +6 -6
  64. package/dist/{chunk-33RQ7I3K.js → chunk-ASTOMXHJ.js} +1 -1
  65. package/dist/{chunk-BSTZ66FA.js → chunk-CDBGZWPY.js} +3 -3
  66. package/dist/{chunk-M4UXDYNJ.js → chunk-CE2ISNPN.js} +5 -5
  67. package/dist/{chunk-BC2HT6JP.js → chunk-CNFM6JEV.js} +2 -2
  68. package/dist/{chunk-4C7OJLSL.js → chunk-DADN6GJO.js} +4 -4
  69. package/dist/{chunk-RPXPRBBA.js → chunk-E3EJARHF.js} +1 -1
  70. package/dist/{chunk-KWWHFTNJ.js → chunk-E4XGV5UG.js} +3 -2
  71. package/dist/{chunk-JQPY2B2H.js → chunk-EFQQLEWW.js} +4 -4
  72. package/dist/{chunk-TXJ65MIV.js → chunk-FAM2TLFC.js} +1 -1
  73. package/dist/{chunk-3XJL2M7H.js → chunk-FSJYYZ3G.js} +5 -5
  74. package/dist/{chunk-4Z7MR5FE.js → chunk-G7GVI5RL.js} +3 -3
  75. package/dist/{chunk-DWUD47H5.js → chunk-G7MVZHMM.js} +2 -2
  76. package/dist/{chunk-FK2EK27I.js → chunk-GKDVI6WX.js} +3 -3
  77. package/dist/{chunk-LXFBSMAE.js → chunk-HKGGENVU.js} +6 -6
  78. package/dist/{chunk-FUVVGIXZ.js → chunk-HT5WOP2M.js} +6 -6
  79. package/dist/{chunk-LH3SNS6H.js → chunk-I567L2KG.js} +10 -10
  80. package/dist/{chunk-QOMIFKQL.js → chunk-I7UZODYO.js} +3 -3
  81. package/dist/{chunk-DHVVEH2K.js → chunk-INNH7EUK.js} +10 -10
  82. package/dist/{chunk-4TT2ZFWZ.js → chunk-JJNO3ACZ.js} +3 -3
  83. package/dist/{chunk-GJXRYXMC.js → chunk-JQUMX7YZ.js} +2 -2
  84. package/dist/{chunk-B4NVYPX5.js → chunk-K2NIENKY.js} +1 -1
  85. package/dist/{chunk-OUIUIJV4.js → chunk-K6QLKUCT.js} +1 -1
  86. package/dist/{chunk-ZXDHPPGI.js → chunk-KT25MC57.js} +4 -4
  87. package/dist/{chunk-2HTE4ECJ.js → chunk-LGQQUQDZ.js} +4 -4
  88. package/dist/{chunk-DM5LNEGJ.js → chunk-LJFITB6T.js} +4 -4
  89. package/dist/{chunk-KXGKUV2F.js → chunk-MWXX34K4.js} +1 -1
  90. package/dist/{chunk-2JTWOTOC.js → chunk-NIZ6ITRH.js} +1 -1
  91. package/dist/{chunk-OBS7O22Q.js → chunk-NMDWW3EY.js} +3 -3
  92. package/dist/{chunk-ZEPS4KIK.js → chunk-NWSGMYX7.js} +4 -4
  93. package/dist/{chunk-TBPMI4LY.js → chunk-OEPP5VUW.js} +6 -6
  94. package/dist/{chunk-RX5FSSQK.js → chunk-OGR5WSHI.js} +7 -7
  95. package/dist/{chunk-FFIDXKQ7.js → chunk-OH67LB2U.js} +14 -14
  96. package/dist/{chunk-ZP5AB6R5.js → chunk-OODEIR3Z.js} +6 -6
  97. package/dist/{chunk-3C4UGMIV.js → chunk-P4OZVK2B.js} +4 -4
  98. package/dist/{chunk-A2F7T3IR.js → chunk-QCSPLJL6.js} +3 -3
  99. package/dist/{chunk-NWLGRSGE.js → chunk-QUKDNQ4L.js} +3 -3
  100. package/dist/{chunk-ZUIAABLS.js → chunk-SJCPI4WT.js} +1 -1
  101. package/dist/{chunk-IXOCJHJW.js → chunk-TOPXBWUN.js} +2 -2
  102. package/dist/{chunk-YN4IXOFK.js → chunk-TTXUWHBL.js} +1 -1
  103. package/dist/{chunk-MQKXQWXA.js → chunk-URJHH72Z.js} +1 -1
  104. package/dist/{chunk-STQEIEYJ.js → chunk-V4ZLZYQ4.js} +3 -3
  105. package/dist/{chunk-BOWXZWHG.js → chunk-VMUBIBHQ.js} +7 -7
  106. package/dist/{chunk-VIDB26GQ.js → chunk-VTF6EYVB.js} +5 -5
  107. package/dist/{chunk-PYI2EFRH.js → chunk-WGBW66IO.js} +5 -5
  108. package/dist/{chunk-PG5WFIHE.js → chunk-XBXBX7YF.js} +2 -2
  109. package/dist/{chunk-LAYKEYZE.js → chunk-XIPP777K.js} +3 -3
  110. package/dist/{chunk-4PS27SCG.js → chunk-XO3UJDJE.js} +26 -8
  111. package/dist/{chunk-WBZA72O5.js → chunk-XRSE2PAM.js} +3 -3
  112. package/dist/{chunk-OMLR56J5.js → chunk-Y5BPNOJI.js} +1 -1
  113. package/dist/{codex-app-cards-switch-XLSLVXDP.js → codex-app-cards-switch-UQ7TGFYV.js} +11 -11
  114. package/dist/{codex-desktop-launch-HRE6YP56.js → codex-desktop-launch-O5FWONZ7.js} +1 -1
  115. package/dist/{codex-session-launch-7RGYGW32.js → codex-session-launch-MTKJC2RH.js} +18 -18
  116. package/dist/{config-ZDOHQUNU.js → config-EGBMIS5K.js} +3 -3
  117. package/dist/{global-upgrade-VR76ZRWG.js → global-upgrade-QOAUUHZ6.js} +10 -10
  118. package/dist/{identity-NJAXA45Q.js → identity-Z5TRRQL5.js} +4 -4
  119. package/dist/{live-capabilities-5UTM55OC.js → live-capabilities-M4HOY7EM.js} +5 -5
  120. package/dist/{paths-BX4DXC3B.js → paths-ZOS6Y3PS.js} +3 -3
  121. package/dist/{reapply-PMCMNTED.js → reapply-PYDRWYKU.js} +28 -28
  122. package/dist/src/index.js +29 -29
  123. package/dist/{upgrade-lock-2G2S3FH7.js → upgrade-lock-QZOSOFK5.js} +4 -4
  124. package/dist/{usage-3EPWR2WQ.js → usage-4FVOG3QN.js} +3 -3
  125. package/package.json +2 -2
  126. /package/dist/{chunk-5TLDFPW6.js → chunk-VE333PL5.js} +0 -0
@@ -1,13 +1,14 @@
1
1
  import {
2
2
  SESSION_HISTORY_RETENTION_DAYS,
3
+ readOwnerOnlyJson,
3
4
  savedTranscriptReadPositions
4
- } from "./chunk-I6MNMAS7.js";
5
+ } from "./chunk-7MN3KPHM.js";
5
6
  import {
6
7
  ensureDaemonRunning
7
- } from "./chunk-FUVVGIXZ.js";
8
+ } from "./chunk-HT5WOP2M.js";
8
9
  import {
9
10
  hookOwnerOf
10
- } from "./chunk-DM5LNEGJ.js";
11
+ } from "./chunk-LJFITB6T.js";
11
12
  import {
12
13
  buildTranscriptRecords,
13
14
  decodePublicKey,
@@ -18,33 +19,33 @@ import {
18
19
  recordSealerFor,
19
20
  uploadTranscriptRecords,
20
21
  wrapSessionKey
21
- } from "./chunk-4PS27SCG.js";
22
+ } from "./chunk-XO3UJDJE.js";
22
23
  import {
23
24
  external_exports
24
25
  } from "./chunk-XMSDA5WA.js";
25
26
  import {
26
27
  transcriptEnvironmentPreference,
27
28
  transcriptsEnabled
28
- } from "./chunk-JOULYVW6.js";
29
+ } from "./chunk-72UCLJJW.js";
29
30
  import {
30
31
  readTranscriptPreference
31
- } from "./chunk-2HTE4ECJ.js";
32
+ } from "./chunk-LGQQUQDZ.js";
32
33
  import {
33
34
  hookMeaning
34
- } from "./chunk-TXJ65MIV.js";
35
+ } from "./chunk-FAM2TLFC.js";
35
36
  import {
36
37
  claudeConfigDir,
37
38
  codexHome,
38
39
  pusharyDir
39
- } from "./chunk-NOGDU4XS.js";
40
+ } from "./chunk-7EGNVZLK.js";
40
41
  import {
41
42
  writeJsonAtomic,
42
43
  writeJsonAtomicDurable
43
- } from "./chunk-QOMIFKQL.js";
44
+ } from "./chunk-I7UZODYO.js";
44
45
  import {
45
46
  getApiKey,
46
47
  getBaseUrl
47
- } from "./chunk-NP65LEAY.js";
48
+ } from "./chunk-32F3BWRY.js";
48
49
  import {
49
50
  PENDING_COMMAND_PREFIX,
50
51
  SESSION_HISTORY_BACKFILL_LIMIT_BYTES,
@@ -52,33 +53,33 @@ import {
52
53
  isHookSource,
53
54
  isRecord,
54
55
  redactSecretsFromContent
55
- } from "./chunk-5TLDFPW6.js";
56
+ } from "./chunk-VE333PL5.js";
56
57
 
57
58
  // src/transcript-recipient-pin.ts
58
- import { existsSync, mkdirSync, readFileSync, rmSync } from "fs";
59
+ import { createHash } from "crypto";
60
+ import { mkdirSync, rmSync } from "fs";
59
61
  import { homedir } from "os";
60
62
  import { dirname, join } from "path";
61
- var pinFile = () => process.env.PUSHARY_RECIPIENT_PIN_FILE?.trim() || join(homedir(), ".pushary", "recipients.json");
63
+ var recipientPinFile = () => process.env.PUSHARY_RECIPIENT_PIN_FILE?.trim() || join(homedir(), ".pushary", "recipients.json");
62
64
  var asPin = (offer) => ({
63
65
  phonePublicKey: offer.phonePublicKey,
64
66
  providerKeyId: offer.provider?.keyId ?? null,
65
67
  providerPublicKey: offer.provider?.publicKey ?? null
66
68
  });
67
- var loadPin = (path = pinFile()) => {
68
- if (!existsSync(path)) return null;
69
+ var pinnedRecipientsSchema = external_exports.object({
70
+ phonePublicKey: external_exports.string().min(1).max(256),
71
+ providerKeyId: external_exports.string().min(1).max(256).nullable().default(null),
72
+ providerPublicKey: external_exports.string().min(1).max(256).nullable().default(null)
73
+ });
74
+ var loadPin = (path = recipientPinFile()) => {
69
75
  try {
70
- const raw = JSON.parse(readFileSync(path, "utf-8"));
71
- if (typeof raw.phonePublicKey !== "string" || raw.phonePublicKey.length === 0) return null;
72
- return {
73
- phonePublicKey: raw.phonePublicKey,
74
- providerKeyId: typeof raw.providerKeyId === "string" ? raw.providerKeyId : null,
75
- providerPublicKey: typeof raw.providerPublicKey === "string" ? raw.providerPublicKey : null
76
- };
77
- } catch {
78
- return null;
76
+ return pinnedRecipientsSchema.parse(readOwnerOnlyJson(path, 16384));
77
+ } catch (error) {
78
+ if (error instanceof Error && "code" in error && error.code === "ENOENT") return null;
79
+ throw new Error("Existing history trust could not be read. Nothing was replaced.");
79
80
  }
80
81
  };
81
- var savePin = (pin, path = pinFile()) => {
82
+ var savePin = (pin, path = recipientPinFile()) => {
82
83
  try {
83
84
  mkdirSync(dirname(path), { recursive: true, mode: 448 });
84
85
  writeJsonAtomicDurable(path, pin, 384);
@@ -89,7 +90,7 @@ var savePin = (pin, path = pinFile()) => {
89
90
  };
90
91
  var clearPin = () => {
91
92
  try {
92
- rmSync(pinFile(), { force: true });
93
+ rmSync(recipientPinFile(), { force: true });
93
94
  } catch {
94
95
  }
95
96
  };
@@ -102,28 +103,78 @@ var verifyRecipients = (offer, pinned) => {
102
103
  if (next.providerPublicKey !== pinned.providerPublicKey) changed.push("the compliance recovery key");
103
104
  return changed.length === 0 ? { kind: "match" } : { kind: "changed", changed };
104
105
  };
105
- var fingerprint = (publicKey) => {
106
- const hex = Buffer.from(publicKey, "base64").toString("hex").toUpperCase();
107
- return (hex.slice(0, 8).match(/.{2}/g) ?? []).join(" ");
108
- };
106
+ var fingerprint = (publicKey) => createHash("sha256").update(publicKey).digest("hex");
109
107
  var changedRecipientsMessage = (verdict, offer) => [
110
108
  "[pushary] Transcript sync stopped: the keys this machine seals transcripts to have changed.",
111
109
  ...verdict.changed.map((what) => `[pushary] changed: ${what}`),
112
110
  `[pushary] now offered: phone ${fingerprint(offer.phonePublicKey)}${offer.provider ? `, recovery ${offer.provider.keyId.slice(0, 8)}` : ", no recovery key"}`,
113
111
  "[pushary] If you reinstalled the app or rotated a key, this is expected. Compare the",
114
112
  "[pushary] fingerprint above with the one in the app under Settings, then run:",
115
- "[pushary] pushary transcripts trust --rotate",
113
+ "[pushary] pushary transcripts trust --fingerprint <fingerprint-from-phone-settings>",
116
114
  "[pushary] Until then nothing is uploaded, so no turn is sealed to a key you have not seen."
117
115
  ].join("\n");
118
116
 
117
+ // src/transcript-phone-trust.ts
118
+ import { createHash as createHash2 } from "crypto";
119
+ import { mkdirSync as mkdirSync2 } from "fs";
120
+ import { dirname as dirname2, join as join2 } from "path";
121
+ var approvedPhoneSchema = external_exports.object({ version: external_exports.literal(1), sourcePinHash: external_exports.string().regex(/^[a-f0-9]{64}$/), phonePublicKey: external_exports.string().min(1).max(256) });
122
+ var pinHash = (pin) => createHash2("sha256").update(JSON.stringify(pin)).digest("hex");
123
+ var approvalFile = (path, pin, publicKey) => join2(`${path}.trusted-phones`, `${pinHash(pin)}-${fingerprint(publicKey)}.json`);
124
+ var sameRecoveryRecipient = (offer, pin) => (offer.provider?.keyId ?? null) === pin.providerKeyId && (offer.provider?.publicKey ?? null) === pin.providerPublicKey;
125
+ var canonicalPublicKey = (key) => {
126
+ const bytes = Buffer.from(key, "base64");
127
+ return bytes.length === 32 && bytes.toString("base64") === key;
128
+ };
129
+ var phoneWasApproved = (offer, pin, path) => {
130
+ try {
131
+ const approval = approvedPhoneSchema.parse(readOwnerOnlyJson(approvalFile(path, pin, offer.phonePublicKey), 16384));
132
+ if (approval.sourcePinHash !== pinHash(pin) || approval.phonePublicKey !== offer.phonePublicKey) throw new Error("Mismatched approval");
133
+ return true;
134
+ } catch (error) {
135
+ if (error instanceof Error && "code" in error && error.code === "ENOENT") return false;
136
+ throw new Error("Saved phone verification could not be read. Nothing was replaced.");
137
+ }
138
+ };
139
+ var verifyTrustedRecipients = (offer, path = recipientPinFile()) => {
140
+ const pin = loadPin(path);
141
+ const verdict = verifyRecipients(offer, pin);
142
+ if (verdict.kind !== "changed" || !pin || !sameRecoveryRecipient(offer, pin)) return verdict;
143
+ return phoneWasApproved(offer, pin, path) ? { kind: "match" } : verdict;
144
+ };
145
+ var verifyAnchoredRecipients = (offer, path = recipientPinFile(), anchorPath) => {
146
+ const verdict = verifyTrustedRecipients(offer, path);
147
+ if (verdict.kind !== "first-use" || anchorPath === void 0 || loadPin(anchorPath) === null) return verdict;
148
+ const anchored = verifyTrustedRecipients(offer, anchorPath);
149
+ return anchored.kind === "match" ? verdict : anchored;
150
+ };
151
+ var assertVerifiedPhone = (offer, pin, expectedFingerprint) => {
152
+ if (!canonicalPublicKey(offer.phonePublicKey) || fingerprint(offer.phonePublicKey) !== expectedFingerprint.trim().toLowerCase()) {
153
+ throw new Error("The fingerprint does not match this phone. Nothing was changed.");
154
+ }
155
+ if (!pin) throw new Error("No existing history trust was found. Nothing was changed.");
156
+ if (!sameRecoveryRecipient(offer, pin)) throw new Error("The recovery recipient also changed. Phone verification cannot approve that change.");
157
+ };
158
+ var trustPhoneRecipient = (request) => {
159
+ const { offer, pinFile } = request;
160
+ const pin = loadPin(pinFile);
161
+ assertVerifiedPhone(offer, pin, request.expectedFingerprint);
162
+ if (!pin) throw new Error("No existing history trust was found.");
163
+ if (verifyTrustedRecipients(offer, pinFile).kind === "match") return "already_trusted";
164
+ const path = approvalFile(pinFile, pin, offer.phonePublicKey);
165
+ mkdirSync2(dirname2(path), { recursive: true, mode: 448 });
166
+ writeJsonAtomicDurable(path, { version: 1, sourcePinHash: pinHash(pin), phonePublicKey: offer.phonePublicKey }, 384);
167
+ return "trusted";
168
+ };
169
+
119
170
  // src/transcript-registration.ts
120
- import { createHash as createHash4 } from "crypto";
121
- import { lstatSync as lstatSync3, readFileSync as readFileSync7, readdirSync as readdirSync2, realpathSync, statSync as statSync3, unlinkSync as unlinkSync2 } from "fs";
122
- import { basename, dirname as dirname3, isAbsolute, join as join6, relative, resolve as resolve2, sep } from "path";
171
+ import { createHash as createHash6 } from "crypto";
172
+ import { lstatSync as lstatSync3, readFileSync as readFileSync6, readdirSync as readdirSync2, realpathSync, statSync as statSync3, unlinkSync as unlinkSync2 } from "fs";
173
+ import { basename, dirname as dirname4, isAbsolute, join as join7, relative, resolve as resolve2, sep } from "path";
123
174
 
124
175
  // src/claude-display-draft.ts
125
- import { closeSync, fstatSync, lstatSync, openSync, readFileSync as readFileSync2, readSync, unlinkSync } from "fs";
126
- import { join as join2 } from "path";
176
+ import { closeSync, fstatSync, lstatSync, openSync, readFileSync, readSync, unlinkSync } from "fs";
177
+ import { join as join3 } from "path";
127
178
 
128
179
  // src/transcript-redaction.ts
129
180
  var redactDraft = (text, complete = false) => {
@@ -176,14 +227,14 @@ var stateSchema = external_exports.object({
176
227
  startedAt: external_exports.number().int().positive(),
177
228
  updatedAt: external_exports.number().int().positive()
178
229
  });
179
- var fileFor = (id, directory) => join2(directory, `${id}.display`);
230
+ var fileFor = (id, directory) => join3(directory, `${id}.display`);
180
231
  var readState = (sessionId, runtime) => {
181
232
  if (!identifier.safeParse(sessionId).success) return null;
182
233
  try {
183
234
  const path = fileFor(sessionId, runtime.directory);
184
235
  const info = lstatSync(path);
185
236
  if (!info.isFile() || info.nlink !== 1 || info.size > MAX_TAIL || (info.mode & 511) !== 384) return null;
186
- const envelope = external_exports.object({ content: external_exports.string() }).parse(JSON.parse(readFileSync2(path, "utf8")));
237
+ const envelope = external_exports.object({ content: external_exports.string() }).parse(JSON.parse(readFileSync(path, "utf8")));
187
238
  const plain = decryptRecord(envelope.content, runtime.sessionKey);
188
239
  if (!plain) return null;
189
240
  const state = stateSchema.parse(JSON.parse(plain));
@@ -266,18 +317,18 @@ var readDisplayDraft = (sessionId, runtime) => {
266
317
  };
267
318
 
268
319
  // src/transcript-session-key.ts
269
- import { existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync3, readdirSync, rmSync as rmSync2, statSync } from "fs";
320
+ import { existsSync, mkdirSync as mkdirSync3, readFileSync as readFileSync2, readdirSync, rmSync as rmSync2, statSync } from "fs";
270
321
  import { homedir as homedir2 } from "os";
271
- import { join as join3 } from "path";
272
- var sessionKeyDirectory = () => process.env.PUSHARY_SESSION_KEY_DIR?.trim() || join3(homedir2(), ".pushary", "sessions");
322
+ import { join as join4 } from "path";
323
+ var sessionKeyDirectory = () => process.env.PUSHARY_SESSION_KEY_DIR?.trim() || join4(homedir2(), ".pushary", "sessions");
273
324
  var SESSION_DISPLAY_TTL_MS = 48 * 60 * 60 * 1e3;
274
325
  var SAFE_SESSION_ID = /^[A-Za-z0-9._-]{1,128}$/;
275
- var fileFor2 = (sessionId, directory) => SAFE_SESSION_ID.test(sessionId) ? join3(directory, `${sessionId}.json`) : null;
326
+ var fileFor2 = (sessionId, directory) => SAFE_SESSION_ID.test(sessionId) ? join4(directory, `${sessionId}.json`) : null;
276
327
  var loadSessionKey = (sessionId, directory = sessionKeyDirectory()) => {
277
328
  const path = fileFor2(sessionId, directory);
278
- if (!path || !existsSync2(path)) return null;
329
+ if (!path || !existsSync(path)) return null;
279
330
  try {
280
- const raw = JSON.parse(readFileSync3(path, "utf-8"));
331
+ const raw = JSON.parse(readFileSync2(path, "utf-8"));
281
332
  const key = typeof raw.sessionKey === "string" ? Buffer.from(raw.sessionKey, "base64") : null;
282
333
  if (!key || key.length !== 32) return null;
283
334
  return {
@@ -296,7 +347,7 @@ var saveSessionKey = (sessionId, value, directory = sessionKeyDirectory()) => {
296
347
  const path = fileFor2(sessionId, directory);
297
348
  if (!path) return false;
298
349
  try {
299
- mkdirSync2(directory, { recursive: true, mode: 448 });
350
+ mkdirSync3(directory, { recursive: true, mode: 448 });
300
351
  writeJsonAtomicDurable(path, {
301
352
  sessionKey: Buffer.from(value.sessionKey).toString("base64"),
302
353
  wrappedKey: value.wrappedKey,
@@ -311,12 +362,12 @@ var saveSessionKey = (sessionId, value, directory = sessionKeyDirectory()) => {
311
362
  }
312
363
  };
313
364
  var sweepStaleSessionDisplays = (now = Date.now(), dir = sessionKeyDirectory()) => {
314
- if (!existsSync2(dir)) return 0;
365
+ if (!existsSync(dir)) return 0;
315
366
  let removed = 0;
316
367
  try {
317
368
  for (const name of readdirSync(dir)) {
318
369
  if (!name.endsWith(".display")) continue;
319
- const path = join3(dir, name);
370
+ const path = join4(dir, name);
320
371
  try {
321
372
  if (now - statSync(path).mtimeMs <= SESSION_DISPLAY_TTL_MS) continue;
322
373
  rmSync2(path, { force: true });
@@ -426,9 +477,9 @@ var startDisplayPublisher = (options) => {
426
477
  };
427
478
 
428
479
  // src/transcript-reader.ts
429
- import { createHash as createHash3 } from "crypto";
430
- import { join as join4 } from "path";
431
- import { closeSync as closeSync3, fstatSync as fstatSync3, openSync as openSync3, readFileSync as readFileSync6, readSync as readSync2, statSync as statSync2 } from "fs";
480
+ import { createHash as createHash5 } from "crypto";
481
+ import { join as join5 } from "path";
482
+ import { closeSync as closeSync3, fstatSync as fstatSync3, openSync as openSync3, readFileSync as readFileSync5, readSync as readSync2, statSync as statSync2 } from "fs";
432
483
 
433
484
  // src/transcript-source.ts
434
485
  var normalizeTranscriptLines = (lines, source) => {
@@ -483,10 +534,10 @@ var transcriptLinesSince = (lines, oldestLineMs, dropCompactionSummaries) => lin
483
534
  });
484
535
 
485
536
  // src/transcript-snapshot-file.ts
486
- import { closeSync as closeSync2, fstatSync as fstatSync2, openSync as openSync2, readFileSync as readFileSync4 } from "fs";
537
+ import { closeSync as closeSync2, fstatSync as fstatSync2, openSync as openSync2, readFileSync as readFileSync3 } from "fs";
487
538
 
488
539
  // src/transcript-snapshot-source.ts
489
- import { createHash } from "crypto";
540
+ import { createHash as createHash3 } from "crypto";
490
541
  var identity = external_exports.string().min(1).max(256);
491
542
  var timestamp = external_exports.string().refine((value) => Number.isFinite(Date.parse(value)));
492
543
  var geminiTool = external_exports.object({ id: identity, name: external_exports.string(), args: external_exports.record(external_exports.string(), external_exports.unknown()), result: external_exports.unknown().optional(), status: external_exports.string() });
@@ -612,7 +663,7 @@ var parseCursorSnapshot = (text) => {
612
663
  const record = JSON.parse(line);
613
664
  if (isRecord(record) && (record.type === "metadata" || record.type === "turn_ended")) continue;
614
665
  const message = cursorMessage.parse(record);
615
- const fingerprint2 = createHash("sha256").update(canonicalJsonText(message)).digest("hex");
666
+ const fingerprint2 = createHash3("sha256").update(canonicalJsonText(message)).digest("hex");
616
667
  const occurrence = occurrences.get(fingerprint2) ?? 0;
617
668
  occurrences.set(fingerprint2, occurrence + 1);
618
669
  messages.push({ id: `cursor:${fingerprint2}:${occurrence}`, role: message.role, content: message.message.content });
@@ -648,7 +699,7 @@ var readTranscriptSnapshot = (path, source, sessionId) => {
648
699
  try {
649
700
  const before = fstatSync2(descriptor);
650
701
  if (!before.isFile() || before.size === 0 || before.size > MAXIMUM_SNAPSHOT_FILE_BYTES) throw new Error("History snapshot is empty or exceeds its size limit");
651
- const text = readFileSync4(descriptor, "utf8");
702
+ const text = readFileSync3(descriptor, "utf8");
652
703
  const after = fstatSync2(descriptor);
653
704
  if (before.size !== after.size || before.mtimeMs !== after.mtimeMs) throw new Error("History snapshot changed while reading");
654
705
  let messages;
@@ -670,11 +721,11 @@ var readTranscriptSnapshot = (path, source, sessionId) => {
670
721
  };
671
722
 
672
723
  // src/transcript-snapshot-journal.ts
673
- import { lstatSync as lstatSync2, mkdirSync as mkdirSync3, readFileSync as readFileSync5 } from "fs";
674
- import { dirname as dirname2 } from "path";
724
+ import { lstatSync as lstatSync2, mkdirSync as mkdirSync4, readFileSync as readFileSync4 } from "fs";
725
+ import { dirname as dirname3 } from "path";
675
726
 
676
727
  // src/transcript-projection.ts
677
- import { createHash as createHash2 } from "crypto";
728
+ import { createHash as createHash4 } from "crypto";
678
729
  var newestWithinUploadLimits = (messages, limits) => {
679
730
  let start = messages.length;
680
731
  let contentBytes = 0;
@@ -703,7 +754,7 @@ var projectTranscriptSnapshot = (previous, messages, older) => {
703
754
  const order = message.sourcePosition;
704
755
  if (!message.id || message.id.length > 256 || Object.hasOwn(entries, message.id)) throw new Error("Invalid or duplicate history message identity");
705
756
  const record = { timestamp: message.timestamp, observedAtMs: message.observedAtMs, historyMessage: { role: message.role, content: message.content } };
706
- const digest = createHash2("sha256").update(JSON.stringify(record)).digest("hex");
757
+ const digest = createHash4("sha256").update(JSON.stringify(record)).digest("hex");
707
758
  entries[message.id] = { digest, order };
708
759
  const earlier = previous?.entries[message.id];
709
760
  if (earlier?.digest === digest && earlier.order === order) continue;
@@ -748,7 +799,7 @@ var TranscriptSnapshotJournal = class {
748
799
  return;
749
800
  }
750
801
  if (!info.isFile() || info.nlink !== 1 || info.size > MAXIMUM_JOURNAL_BYTES || process.getuid && (info.uid !== process.getuid() || (info.mode & 511) !== 384)) throw new Error("Unsafe history checkpoint");
751
- const saved = journalSchema.parse(JSON.parse(readFileSync5(path, "utf8")));
802
+ const saved = journalSchema.parse(JSON.parse(readFileSync4(path, "utf8")));
752
803
  if (saved.binding !== binding) throw new Error("History checkpoint belongs to another source");
753
804
  this.current = saved.epochStartedAtMs === epochStartedAtMs ? saved : { ...saved, epochStartedAtMs, initialized: false, observedAtMs: {}, pending: null, excludedIds: [], projection: { revision: Math.max(saved.projection?.revision ?? 0, saved.pending?.next.revision ?? 0), entries: {} } };
754
805
  }
@@ -799,7 +850,7 @@ var TranscriptSnapshotJournal = class {
799
850
  }
800
851
  store(next) {
801
852
  if (Buffer.byteLength(JSON.stringify(next)) > MAXIMUM_JOURNAL_BYTES) throw new Error("History checkpoint exceeds its size limit");
802
- mkdirSync3(dirname2(this.path), { recursive: true, mode: 448 });
853
+ mkdirSync4(dirname3(this.path), { recursive: true, mode: 448 });
803
854
  writeJsonAtomicDurable(this.path, next, 384);
804
855
  this.current = journalSchema.parse(next);
805
856
  }
@@ -895,7 +946,7 @@ var appendOnlyReader = (path, source, epoch) => ({
895
946
  read(retentionDays, nowMs) {
896
947
  const version = sourceVersion(path);
897
948
  const read2 = epoch ? linesSeenDuringEpoch(epoch, path, source, nowMs, retentionDays) : null;
898
- const lines = read2?.lines ?? normalizeTranscriptLines(readFileSync6(path, "utf8").split("\n"), source);
949
+ const lines = read2?.lines ?? normalizeTranscriptLines(readFileSync5(path, "utf8").split("\n"), source);
899
950
  return {
900
951
  lines,
901
952
  acknowledge() {
@@ -911,8 +962,8 @@ var caughtUpReadStillCurrent = (caughtUp, version, nowMs) => caughtUp !== null &
911
962
  var UNCHANGED_SNAPSHOT_READ = { lines: [], acknowledge: () => "unchanged" };
912
963
  var snapshotReader = (options, source) => {
913
964
  const { path, epoch } = options;
914
- const binding = createHash3("sha256").update(JSON.stringify([options.baseUrl, options.apiKey, options.machineId, options.sessionId, source, path])).digest("hex");
915
- const journalPath = join4(options.keyDirectory, "snapshots", binding + ".json");
965
+ const binding = createHash5("sha256").update(JSON.stringify([options.baseUrl, options.apiKey, options.machineId, options.sessionId, source, path])).digest("hex");
966
+ const journalPath = join5(options.keyDirectory, "snapshots", binding + ".json");
916
967
  const epochStart = epoch?.startedAtMs ?? 0;
917
968
  const backfill = epoch ? epoch.backfill : SESSION_HISTORY_BACKFILL;
918
969
  let journal;
@@ -967,11 +1018,11 @@ var transcriptHealthReporter = (target) => {
967
1018
  };
968
1019
 
969
1020
  // src/transcript-sync.ts
970
- import { join as join5, resolve } from "path";
1021
+ import { join as join6, resolve } from "path";
971
1022
  var claudeTranscriptPath = (cwd, sessionId) => {
972
1023
  const projectId = resolve(cwd).replace(/[^a-zA-Z0-9-]/g, "-");
973
1024
  const configDir = claudeConfigDir();
974
- return join5(configDir, "projects", projectId, `${sessionId}.jsonl`);
1025
+ return join6(configDir, "projects", projectId, `${sessionId}.jsonl`);
975
1026
  };
976
1027
  var createSyncState = () => ({
977
1028
  sessionKey: null,
@@ -1102,7 +1153,7 @@ var startTranscriptSync = (opts) => {
1102
1153
  recipientFailure = reason;
1103
1154
  });
1104
1155
  if (!offer) return null;
1105
- const verdict = verifyRecipients(offer, loadPin(opts.storage?.recipientPinFile));
1156
+ const verdict = verifyAnchoredRecipients(offer, opts.storage?.recipientPinFile, opts.storage?.firstUseAnchorPinFile);
1106
1157
  if (verdict.kind === "first-use" && !savePin(verdict.pin, opts.storage?.recipientPinFile)) return null;
1107
1158
  if (verdict.kind === "changed") {
1108
1159
  recipientFailure = "phone_key_changed";
@@ -1168,13 +1219,14 @@ var startTranscriptSync = (opts) => {
1168
1219
  if (ending) opts.onDrained?.();
1169
1220
  return;
1170
1221
  }
1222
+ waitingForRecipients = true;
1171
1223
  const recipients = await fetchPinnedRecipients();
1172
1224
  if (!recipients) {
1173
- waitingForRecipients = true;
1174
1225
  if (recipientFailure === "off_for_workspace") passOverConversationWhileHistoryIsOff();
1175
1226
  await reportHealth(recipientFailure);
1176
1227
  return;
1177
1228
  }
1229
+ waitingForRecipients = false;
1178
1230
  if (reader.requiresRevisions && recipients.phoneReadsTranscriptRevisions !== true) {
1179
1231
  await reportHealth("phone_upgrade_required");
1180
1232
  return;
@@ -1253,15 +1305,15 @@ var storedRegistrationSchema = external_exports.object({
1253
1305
  endedAt: external_exports.number().positive().optional(),
1254
1306
  drained: external_exports.boolean().optional()
1255
1307
  });
1256
- var defaultRegistryDirectory = () => process.env.PUSHARY_TRANSCRIPT_REGISTRY_DIR || join6(pusharyDir(), "transcript-capture");
1308
+ var defaultRegistryDirectory = () => process.env.PUSHARY_TRANSCRIPT_REGISTRY_DIR || join7(pusharyDir(), "transcript-capture");
1257
1309
  var cliRegistry = (apiKey = getApiKey()) => ({ apiKey, baseUrl: getBaseUrl(), directory: defaultRegistryDirectory() });
1258
- var accountFor = (registry) => createHash4("sha256").update(`${registry.baseUrl}\0${registry.apiKey}`).digest("hex");
1310
+ var accountFor = (registry) => createHash6("sha256").update(`${registry.baseUrl}\0${registry.apiKey}`).digest("hex");
1259
1311
  var TTL = 48 * 60 * 60 * 1e3;
1260
- var registeredReadPositionsFile = (registry) => join6(registry.directory, "read-positions", `${accountFor(registry)}.json`);
1312
+ var registeredReadPositionsFile = (registry) => join7(registry.directory, "read-positions", `${accountFor(registry)}.json`);
1261
1313
  var SNAPSHOT_KEYS_DIRECTORY_NAME = "snapshot-keys";
1262
1314
  var SNAPSHOT_RECIPIENT_PIN_FILE_NAME = "recipients.json";
1263
- var snapshotKeyDirectory = (registry) => join6(registry.directory, SNAPSHOT_KEYS_DIRECTORY_NAME, accountFor(registry));
1264
- var snapshotRecipientPinFile = (registry) => join6(snapshotKeyDirectory(registry), SNAPSHOT_RECIPIENT_PIN_FILE_NAME);
1315
+ var snapshotKeyDirectory = (registry) => join7(registry.directory, SNAPSHOT_KEYS_DIRECTORY_NAME, accountFor(registry));
1316
+ var snapshotRecipientPinFile = (registry = cliRegistry()) => join7(snapshotKeyDirectory(registry), SNAPSHOT_RECIPIENT_PIN_FILE_NAME);
1265
1317
  var entryAt = (path) => {
1266
1318
  try {
1267
1319
  return lstatSync3(path);
@@ -1276,18 +1328,18 @@ var removedPinFile = (path) => {
1276
1328
  return true;
1277
1329
  };
1278
1330
  var clearSnapshotRecipientPins = (registryDirectory = defaultRegistryDirectory()) => {
1279
- const accounts = join6(registryDirectory, SNAPSHOT_KEYS_DIRECTORY_NAME);
1331
+ const accounts = join7(registryDirectory, SNAPSHOT_KEYS_DIRECTORY_NAME);
1280
1332
  if (!entryAt(accounts)?.isDirectory()) return false;
1281
- return readdirSync2(accounts, { withFileTypes: true }).filter((account) => account.isDirectory()).map((account) => removedPinFile(join6(accounts, account.name, SNAPSHOT_RECIPIENT_PIN_FILE_NAME))).includes(true);
1333
+ return readdirSync2(accounts, { withFileTypes: true }).filter((account) => account.isDirectory()).map((account) => removedPinFile(join7(accounts, account.name, SNAPSHOT_RECIPIENT_PIN_FILE_NAME))).includes(true);
1282
1334
  };
1283
1335
  var read = (path) => {
1284
1336
  if (statSync3(path).size > 16384) throw new Error("History registration exceeds its size limit");
1285
- return storedRegistrationSchema.parse(JSON.parse(readFileSync7(path, "utf8")));
1337
+ return storedRegistrationSchema.parse(JSON.parse(readFileSync6(path, "utf8")));
1286
1338
  };
1287
1339
  var consent = (record) => record.consentOverride ?? readTranscriptPreference() ?? true;
1288
1340
  var withoutTrailingSlash = (url) => url.replace(/\/+$/, "");
1289
1341
  var sameServer = (url, registry) => withoutTrailingSlash(url) === withoutTrailingSlash(registry.baseUrl);
1290
- var fileFor3 = (registry, sessionId) => join6(registry.directory, `${createHash4("sha256").update(accountFor(registry) + sessionId).digest("hex")}.json`);
1342
+ var fileFor3 = (registry, sessionId) => join7(registry.directory, `${createHash6("sha256").update(accountFor(registry) + sessionId).digest("hex")}.json`);
1291
1343
  var permitted = (registry, raw) => raw.account === accountFor(registry) && consent(raw) && !raw.drained && (raw.source !== "claude" || !!raw.expectedSiteId || !macOwnsClaudeHooks(raw.cwd)) && (!raw.expectedSiteId || raw.expectedSiteId === raw.verifiedSiteId && !!raw.expectedBaseUrl && sameServer(raw.expectedBaseUrl, registry));
1292
1344
  var registeredTranscriptEnabled = (apiKey, sessionId, registry = cliRegistry(apiKey)) => {
1293
1345
  try {
@@ -1297,12 +1349,12 @@ var registeredTranscriptEnabled = (apiKey, sessionId, registry = cliRegistry(api
1297
1349
  }
1298
1350
  };
1299
1351
  var macOwnsClaudeHooks = (cwd) => [
1300
- join6(claudeConfigDir(), "settings.json"),
1301
- join6(cwd, ".claude", "settings.json"),
1302
- join6(cwd, ".claude", "settings.local.json")
1352
+ join7(claudeConfigDir(), "settings.json"),
1353
+ join7(cwd, ".claude", "settings.json"),
1354
+ join7(cwd, ".claude", "settings.local.json")
1303
1355
  ].some((path) => {
1304
1356
  try {
1305
- return hookOwnerOf(JSON.parse(readFileSync7(path, "utf8"))) === "app";
1357
+ return hookOwnerOf(JSON.parse(readFileSync6(path, "utf8"))) === "app";
1306
1358
  } catch {
1307
1359
  return false;
1308
1360
  }
@@ -1310,12 +1362,12 @@ var macOwnsClaudeHooks = (cwd) => [
1310
1362
  var TRANSCRIPT_ROOT_NAME = { claude: "projects", codex: "sessions", gemini: "tmp", cursor: "projects", hermes: "hermes-history" };
1311
1363
  var transcriptRoots = (source) => {
1312
1364
  const home = homedir3();
1313
- if (source === "codex") return [join6(codexHome(), TRANSCRIPT_ROOT_NAME.codex)];
1314
- if (source === "cursor") return [join6(home, ".cursor", TRANSCRIPT_ROOT_NAME.cursor)];
1315
- if (source === "hermes") return [join6(home, ".pushary", TRANSCRIPT_ROOT_NAME.hermes)];
1316
- if (source === "gemini") return [join6(process.env.GEMINI_CLI_HOME || home, ".gemini", "tmp"), join6(home, ".cache", ".gemini", "tmp")];
1365
+ if (source === "codex") return [join7(codexHome(), TRANSCRIPT_ROOT_NAME.codex)];
1366
+ if (source === "cursor") return [join7(home, ".cursor", TRANSCRIPT_ROOT_NAME.cursor)];
1367
+ if (source === "hermes") return [join7(home, ".pushary", TRANSCRIPT_ROOT_NAME.hermes)];
1368
+ if (source === "gemini") return [join7(process.env.GEMINI_CLI_HOME || home, ".gemini", "tmp"), join7(home, ".cache", ".gemini", "tmp")];
1317
1369
  const configured = process.env.CLAUDE_CONFIG_DIR;
1318
- return [join6(configured || join6(home, ".claude"), TRANSCRIPT_ROOT_NAME.claude), join6(home, ".claude", TRANSCRIPT_ROOT_NAME.claude)];
1370
+ return [join7(configured || join7(home, ".claude"), TRANSCRIPT_ROOT_NAME.claude), join7(home, ".claude", TRANSCRIPT_ROOT_NAME.claude)];
1319
1371
  };
1320
1372
  var isAgentTranscriptRoot = (root, source) => isAbsolute(root) && resolve2(root) === root && basename(root) === TRANSCRIPT_ROOT_NAME[source];
1321
1373
  var acceptedTranscriptRoots = (source, registeredRoot) => typeof registeredRoot === "string" && isAgentTranscriptRoot(registeredRoot, source) ? [...transcriptRoots(source), registeredRoot] : transcriptRoots(source);
@@ -1324,8 +1376,8 @@ var resolvedSourcePath = (path) => {
1324
1376
  try {
1325
1377
  return realpathSync(absolute);
1326
1378
  } catch {
1327
- const parent = dirname3(absolute);
1328
- return parent === absolute ? absolute : join6(resolvedSourcePath(parent), basename(absolute));
1379
+ const parent = dirname4(absolute);
1380
+ return parent === absolute ? absolute : join7(resolvedSourcePath(parent), basename(absolute));
1329
1381
  }
1330
1382
  };
1331
1383
  var transcriptRootContaining = (candidate, roots) => {
@@ -1345,7 +1397,7 @@ var captureRegistration = (source, value, registeredRoot) => {
1345
1397
  const segments = relative(root, path).split(sep);
1346
1398
  if (source === "hermes" && (segments.length !== 2 || !/^[a-f0-9]{64}$/.test(segments[0] ?? "") || basename(path) !== `${sessionId}.json`)) return null;
1347
1399
  if (source === "gemini" && (segments.length !== 3 || segments[1] !== "chats" || !basename(path).startsWith("session-"))) return null;
1348
- if (source === "cursor" && (segments[1] !== "agent-transcripts" || basename(path) !== `${sessionId}.jsonl` || !(segments.length === 3 || segments.length === 4 && basename(dirname3(path)) === sessionId))) return null;
1400
+ if (source === "cursor" && (segments[1] !== "agent-transcripts" || basename(path) !== `${sessionId}.jsonl` || !(segments.length === 3 || segments.length === 4 && basename(dirname4(path)) === sessionId))) return null;
1349
1401
  return { sessionId, cwd: input.cwd, path, source, root };
1350
1402
  };
1351
1403
  var SESSION_END_EVENT = "SessionEnd";
@@ -1419,7 +1471,7 @@ var startRegisteredTranscripts = (apiKey, machineId, options = {}) => {
1419
1471
  try {
1420
1472
  let unreadable = 0;
1421
1473
  const files = readdirSync2(registry.directory).filter((name) => name.endsWith(".json")).flatMap((name) => {
1422
- const path = join6(registry.directory, name);
1474
+ const path = join7(registry.directory, name);
1423
1475
  try {
1424
1476
  const time = statSync3(path).mtimeMs;
1425
1477
  if (Date.now() - time > TTL) {
@@ -1470,7 +1522,8 @@ var startRegisteredTranscripts = (apiKey, machineId, options = {}) => {
1470
1522
  const current = () => read(registration.registrationPath);
1471
1523
  const keyStorage = storage ?? (registration.source === "claude" || registration.source === "codex" ? void 0 : {
1472
1524
  sessionKeyDirectory: snapshotKeyDirectory(registry),
1473
- recipientPinFile: snapshotRecipientPinFile(registry)
1525
+ recipientPinFile: snapshotRecipientPinFile(registry),
1526
+ firstUseAnchorPinFile: recipientPinFile()
1474
1527
  });
1475
1528
  active.set(id, { path: registration.path, stop: startTranscriptSync({
1476
1529
  apiKey,
@@ -1517,15 +1570,20 @@ var startRegisteredTranscripts = (apiKey, machineId, options = {}) => {
1517
1570
  };
1518
1571
 
1519
1572
  export {
1573
+ recipientPinFile,
1574
+ loadPin,
1575
+ clearPin,
1576
+ fingerprint,
1577
+ verifyTrustedRecipients,
1578
+ assertVerifiedPhone,
1579
+ trustPhoneRecipient,
1520
1580
  redactDraft,
1521
1581
  captureDisplayDraft,
1522
1582
  sessionKeyDirectory,
1523
1583
  loadSessionKey,
1524
1584
  startDraftPublisher,
1525
- loadPin,
1526
- clearPin,
1527
- fingerprint,
1528
1585
  claudeTranscriptPath,
1586
+ snapshotRecipientPinFile,
1529
1587
  clearSnapshotRecipientPins,
1530
1588
  registeredTranscriptEnabled,
1531
1589
  captureRegistration,
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  absoluteUrl
3
- } from "./chunk-4TT2ZFWZ.js";
3
+ } from "./chunk-JJNO3ACZ.js";
4
4
  import {
5
5
  KEY_SCOPE
6
- } from "./chunk-5TLDFPW6.js";
6
+ } from "./chunk-VE333PL5.js";
7
7
 
8
8
  // src/cli/approval-settings-pages.ts
9
9
  var API_KEYS_PATH = "/dashboard/agent/settings";
@@ -4,7 +4,7 @@ import {
4
4
  postLiveSession,
5
5
  postLiveSessionUntilAccepted,
6
6
  startLiveSessionHeartbeat
7
- } from "./chunk-P4W5TP2X.js";
7
+ } from "./chunk-6LJOCQBB.js";
8
8
  import {
9
9
  CommandReceipts,
10
10
  controllerCommandSchema
@@ -14,24 +14,24 @@ import {
14
14
  describeIdleWindow,
15
15
  idleDeadlinePassed,
16
16
  startProcessOwnership
17
- } from "./chunk-OBS7O22Q.js";
17
+ } from "./chunk-NMDWW3EY.js";
18
18
  import {
19
19
  spawnClaude
20
20
  } from "./chunk-XHKBHWLX.js";
21
21
  import {
22
22
  repoKeyFor
23
- } from "./chunk-OUIUIJV4.js";
23
+ } from "./chunk-K6QLKUCT.js";
24
24
  import {
25
25
  isAskUserResponse,
26
26
  isDecisionStopped,
27
27
  isWaitForAnswerResponse,
28
28
  withdrawQuestion,
29
29
  writeDegraded
30
- } from "./chunk-3XJL2M7H.js";
30
+ } from "./chunk-FSJYYZ3G.js";
31
31
  import {
32
32
  callMcpTool,
33
33
  isRetryable
34
- } from "./chunk-ZEPS4KIK.js";
34
+ } from "./chunk-NWSGMYX7.js";
35
35
  import {
36
36
  AGENT_QUESTION_DESCRIPTION_MAX,
37
37
  AGENT_QUESTION_HEADER_MAX,
@@ -52,7 +52,7 @@ import {
52
52
  parseCodexQuestions,
53
53
  planElicitationAsk,
54
54
  readElicitationFields
55
- } from "./chunk-5TLDFPW6.js";
55
+ } from "./chunk-VE333PL5.js";
56
56
 
57
57
  // src/codex-question-owner.ts
58
58
  import { createHash } from "crypto";
@@ -7,7 +7,7 @@ import {
7
7
  recordSealerFor,
8
8
  uploadTranscriptRecords,
9
9
  wrapSessionKey
10
- } from "./chunk-4PS27SCG.js";
10
+ } from "./chunk-XO3UJDJE.js";
11
11
 
12
12
  // src/live-session.ts
13
13
  var MAX_TRANSCRIPT_QUEUE = 100;
@@ -4,13 +4,13 @@ import {
4
4
  configFailureKind,
5
5
  retryAgentCommand,
6
6
  writeSetupConfigFile
7
- } from "./chunk-N7OMDSXK.js";
7
+ } from "./chunk-2EOTXWFK.js";
8
8
  import {
9
9
  MCP_BRIDGE_BINARY
10
- } from "./chunk-QQCGYHYG.js";
10
+ } from "./chunk-3N4GETIW.js";
11
11
  import {
12
12
  KEY_FILE_MODE
13
- } from "./chunk-2HTE4ECJ.js";
13
+ } from "./chunk-LGQQUQDZ.js";
14
14
  import {
15
15
  CLINE_SERVER_NAME_TAKEN,
16
16
  CLINE_SETTINGS_REFUSALS,
@@ -32,13 +32,13 @@ import {
32
32
  withClinePusharyServer,
33
33
  withRefreshedClineKey,
34
34
  withoutClinePusharyServer
35
- } from "./chunk-NOGDU4XS.js";
35
+ } from "./chunk-7EGNVZLK.js";
36
36
  import {
37
37
  readJsonSafe,
38
38
  resolveGlobalBinary,
39
39
  writeFileAtomic,
40
40
  writeJsonAtomic
41
- } from "./chunk-QOMIFKQL.js";
41
+ } from "./chunk-I7UZODYO.js";
42
42
 
43
43
  // src/fx-config.ts
44
44
  var FX_SERVER_NAME = "pushary";