pushary 1.9.87 → 1.9.89

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 +13 -0
  2. package/data/SKILL.md +35 -24
  3. package/data/cowork/SKILL.md +4 -4
  4. package/data/cursor-plugin/.cursor-plugin/plugin.json +1 -1
  5. package/data/cursor-plugin/CHANGELOG.md +8 -0
  6. package/data/cursor-plugin/skills/pushary/SKILL.md +34 -23
  7. package/data/vscode-plugin/.claude-plugin/plugin.json +1 -1
  8. package/data/vscode-plugin/CHANGELOG.md +8 -0
  9. package/data/vscode-plugin/skills/pushary/SKILL.md +34 -23
  10. package/dist/{api-ZDMC22LN.js → api-RMTM2T46.js} +9 -9
  11. package/dist/bin/pushary-bell-hook.js +22 -22
  12. package/dist/bin/pushary-bell.js +9 -9
  13. package/dist/bin/pushary-claude.js +23 -23
  14. package/dist/bin/pushary-clean.js +30 -30
  15. package/dist/bin/pushary-codex-bridge.js +19 -19
  16. package/dist/bin/pushary-codex-hook.js +25 -25
  17. package/dist/bin/pushary-codex-session.js +8 -8
  18. package/dist/bin/pushary-codex.js +11 -11
  19. package/dist/bin/pushary-connect.js +23 -23
  20. package/dist/bin/pushary-cowork.js +3 -3
  21. package/dist/bin/pushary-daemon.js +35 -35
  22. package/dist/bin/pushary-disconnect.js +22 -22
  23. package/dist/bin/pushary-display-hook.js +14 -14
  24. package/dist/bin/pushary-doctor.js +36 -36
  25. package/dist/bin/pushary-elicitation-hook.js +22 -22
  26. package/dist/bin/pushary-gemini-bridge.js +14 -14
  27. package/dist/bin/pushary-gemini-hook.js +23 -23
  28. package/dist/bin/pushary-hook.js +25 -25
  29. package/dist/bin/pushary-login.js +18 -18
  30. package/dist/bin/pushary-logout.js +20 -20
  31. package/dist/bin/pushary-mcp.js +10 -10
  32. package/dist/bin/pushary-mode.js +13 -13
  33. package/dist/bin/pushary-notification-hook.js +21 -21
  34. package/dist/bin/pushary-opencode-hook.js +22 -22
  35. package/dist/bin/pushary-permission-denied-hook.js +24 -24
  36. package/dist/bin/pushary-permission-hook.js +24 -24
  37. package/dist/bin/pushary-post-hook.js +21 -21
  38. package/dist/bin/pushary-prompt-hook.js +21 -21
  39. package/dist/bin/pushary-session-end-hook.js +23 -23
  40. package/dist/bin/pushary-session-start-hook.js +31 -31
  41. package/dist/bin/pushary-setup.js +43 -43
  42. package/dist/bin/pushary-stats.js +3 -3
  43. package/dist/bin/pushary-status.js +27 -27
  44. package/dist/bin/pushary-stop-hook.js +21 -21
  45. package/dist/bin/pushary-stopfailure-hook.js +21 -21
  46. package/dist/bin/pushary-suggestions.js +13 -13
  47. package/dist/bin/pushary-transcript-register.js +15 -15
  48. package/dist/bin/pushary-transcripts.js +56 -27
  49. package/dist/bin/pushary-upgrade.js +36 -36
  50. package/dist/bin/pushary-wait.js +13 -13
  51. package/dist/bin/pushary.js +6 -6
  52. package/dist/{chunk-LXTTFH4P.js → chunk-2UQDRCEI.js} +5 -5
  53. package/dist/{chunk-7SY2F6JA.js → chunk-2WRQQTOZ.js} +2 -2
  54. package/dist/{chunk-FQ2EBMDA.js → chunk-3YN7TPIQ.js} +6 -6
  55. package/dist/{chunk-E5DZ74QT.js → chunk-4D6PR2MK.js} +5 -5
  56. package/dist/{chunk-VNROKR2K.js → chunk-53CFDLGE.js} +2 -2
  57. package/dist/{chunk-K4I2LESS.js → chunk-54RUDSDE.js} +2 -2
  58. package/dist/{chunk-VADZ6C26.js → chunk-55K5ST53.js} +2 -2
  59. package/dist/{chunk-CT2HRZLL.js → chunk-662O4JBY.js} +3 -3
  60. package/dist/{chunk-4RZXECJS.js → chunk-CV3ALIG7.js} +7 -7
  61. package/dist/{chunk-42KJLBX2.js → chunk-DBWFWMHB.js} +6 -6
  62. package/dist/{chunk-ZLO3K5TP.js → chunk-F6WJEBBK.js} +4 -4
  63. package/dist/{chunk-XPE745FL.js → chunk-F7FED2QT.js} +5 -5
  64. package/dist/{chunk-GPP7AUCM.js → chunk-FMOIAZXN.js} +5 -5
  65. package/dist/{chunk-GVMOTYFU.js → chunk-G3GSI7E6.js} +2 -2
  66. package/dist/{chunk-B34K6NXW.js → chunk-H4CKXYWR.js} +2 -2
  67. package/dist/{chunk-JILOKJ3W.js → chunk-IBYD6P5T.js} +141 -90
  68. package/dist/{chunk-STQEIEYJ.js → chunk-IJKSZTFR.js} +1 -1
  69. package/dist/{chunk-IOJZPPDB.js → chunk-ILBVL464.js} +1 -1
  70. package/dist/{chunk-YLWPJ2MP.js → chunk-JBR2FQKG.js} +2 -2
  71. package/dist/{chunk-S33CCO4J.js → chunk-JVWD6UWG.js} +1 -1
  72. package/dist/{chunk-NP65LEAY.js → chunk-L4FQ3I7N.js} +1 -1
  73. package/dist/{chunk-7TZDVGMC.js → chunk-LC4OFGTU.js} +1 -1
  74. package/dist/{chunk-P4W5TP2X.js → chunk-LVOBBWSN.js} +1 -1
  75. package/dist/{chunk-NFQCFVKJ.js → chunk-M7MVHBKI.js} +5 -5
  76. package/dist/{chunk-GAYYYXMU.js → chunk-MV34QALO.js} +1 -1
  77. package/dist/{chunk-K34J2V5G.js → chunk-N574OILO.js} +2 -2
  78. package/dist/{chunk-MY6CX7WE.js → chunk-OCRNH4YT.js} +4 -4
  79. package/dist/{chunk-GVZAEST7.js → chunk-OMS5V4ER.js} +3 -3
  80. package/dist/{chunk-TQ43EX2E.js → chunk-P6EUHG5X.js} +3 -3
  81. package/dist/{chunk-XGW7J7KT.js → chunk-PF2COPAW.js} +2 -2
  82. package/dist/{chunk-UDYWZ4DI.js → chunk-PPJSAS57.js} +2 -2
  83. package/dist/{chunk-QN2EFKXV.js → chunk-R3LNXXPV.js} +2 -2
  84. package/dist/{chunk-RPXPRBBA.js → chunk-R5F2A2IT.js} +1 -1
  85. package/dist/{chunk-KWWHFTNJ.js → chunk-RC65MCPA.js} +2 -1
  86. package/dist/{chunk-2JTWOTOC.js → chunk-RJXWGSXY.js} +1 -1
  87. package/dist/{chunk-NW3GZBQA.js → chunk-RQGGP52F.js} +4 -4
  88. package/dist/{chunk-YRQWA4ZS.js → chunk-SDRHH6LW.js} +1 -1
  89. package/dist/{chunk-7AH6F44G.js → chunk-SOBEBLLW.js} +4 -4
  90. package/dist/{chunk-DWUD47H5.js → chunk-SUEB56Y4.js} +1 -1
  91. package/dist/{chunk-4VHT5SWU.js → chunk-SXELNIYK.js} +3 -3
  92. package/dist/{chunk-RHDHLXUK.js → chunk-TBKO5EOS.js} +3 -3
  93. package/dist/{chunk-VNYSGRCL.js → chunk-TEFWZEL2.js} +9 -9
  94. package/dist/{chunk-3ZWJBPQ5.js → chunk-TXDXHR2G.js} +1 -1
  95. package/dist/{chunk-UOM5SUWJ.js → chunk-U4DG4RC4.js} +2 -2
  96. package/dist/{chunk-5YDIHB67.js → chunk-UEWTZZY4.js} +3 -3
  97. package/dist/{chunk-R5SANCZC.js → chunk-UYKOZLKX.js} +4 -4
  98. package/dist/{chunk-VU5PKE2N.js → chunk-UYVCRG3J.js} +38 -38
  99. package/dist/{chunk-4PS27SCG.js → chunk-VHO3HV7P.js} +25 -7
  100. package/dist/{chunk-XMRCP7TM.js → chunk-VHQCMNIO.js} +2 -2
  101. package/dist/{chunk-D766FEAS.js → chunk-WNW2HFNT.js} +3 -3
  102. package/dist/{chunk-7LRY76MV.js → chunk-X5MW6LB5.js} +5 -5
  103. package/dist/{chunk-6OVWYAET.js → chunk-XGVKU7PM.js} +4 -4
  104. package/dist/{chunk-UAB3K3TA.js → chunk-XLR4QO4R.js} +3 -3
  105. package/dist/{chunk-FE3I7SCA.js → chunk-XZYUFVUW.js} +5 -5
  106. package/dist/{chunk-WJ2APMU5.js → chunk-Y7FF5WKE.js} +4 -4
  107. package/dist/{chunk-IXUJCONI.js → chunk-YCPUVARV.js} +2 -2
  108. package/dist/{chunk-RKN7BJRQ.js → chunk-YFNUFWSK.js} +1 -1
  109. package/dist/{chunk-NFKB7ZHA.js → chunk-YH2ZWZSG.js} +2 -2
  110. package/dist/{chunk-M66W7UD7.js → chunk-YKA6HMYO.js} +2 -2
  111. package/dist/{chunk-X5INK4OE.js → chunk-YQYCQ2KY.js} +10 -10
  112. package/dist/{chunk-SLMCMHVY.js → chunk-ZOKA75MZ.js} +2 -2
  113. package/dist/{chunk-2CJNGJQS.js → chunk-ZQMR6OMT.js} +3 -3
  114. package/dist/{codex-app-cards-switch-ARYZG5DV.js → codex-app-cards-switch-BSMP3FMX.js} +10 -10
  115. package/dist/{codex-desktop-launch-6P2IXUIZ.js → codex-desktop-launch-LZL2LBBZ.js} +1 -1
  116. package/dist/{codex-session-launch-WUZSHGSI.js → codex-session-launch-LDY5BQCQ.js} +16 -16
  117. package/dist/{config-ZDOHQUNU.js → config-RTZAI4JI.js} +2 -2
  118. package/dist/{global-upgrade-QGHGUP5V.js → global-upgrade-Y6AEYIJ5.js} +9 -9
  119. package/dist/{identity-WRUYTQ4O.js → identity-HKL7Z55E.js} +3 -3
  120. package/dist/{live-capabilities-AJJILIIZ.js → live-capabilities-BUP4EEKC.js} +4 -4
  121. package/dist/{paths-Q5A6GBRA.js → paths-P5K3AYLX.js} +2 -2
  122. package/dist/{reapply-BMNUBJ3Z.js → reapply-DN4VZ54U.js} +24 -24
  123. package/dist/src/index.js +24 -24
  124. package/dist/{upgrade-lock-NGCFJRRN.js → upgrade-lock-FW74BNQL.js} +3 -3
  125. package/dist/{usage-3EPWR2WQ.js → usage-J2D7WYM4.js} +2 -2
  126. package/package.json +1 -1
@@ -1,13 +1,14 @@
1
1
  import {
2
2
  SESSION_HISTORY_RETENTION_DAYS,
3
+ readOwnerOnlyJson,
3
4
  savedTranscriptReadPositions
4
- } from "./chunk-VU5PKE2N.js";
5
+ } from "./chunk-UYVCRG3J.js";
5
6
  import {
6
7
  ensureDaemonRunning
7
- } from "./chunk-FE3I7SCA.js";
8
+ } from "./chunk-XZYUFVUW.js";
8
9
  import {
9
10
  hookOwnerOf
10
- } from "./chunk-K4I2LESS.js";
11
+ } from "./chunk-54RUDSDE.js";
11
12
  import {
12
13
  buildTranscriptRecords,
13
14
  decodePublicKey,
@@ -18,17 +19,17 @@ import {
18
19
  recordSealerFor,
19
20
  uploadTranscriptRecords,
20
21
  wrapSessionKey
21
- } from "./chunk-4PS27SCG.js";
22
+ } from "./chunk-VHO3HV7P.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-IOJZPPDB.js";
29
+ } from "./chunk-ILBVL464.js";
29
30
  import {
30
31
  readTranscriptPreference
31
- } from "./chunk-TQ43EX2E.js";
32
+ } from "./chunk-P6EUHG5X.js";
32
33
  import {
33
34
  hookMeaning
34
35
  } from "./chunk-TXJ65MIV.js";
@@ -36,15 +37,15 @@ import {
36
37
  claudeConfigDir,
37
38
  codexHome,
38
39
  pusharyDir
39
- } from "./chunk-YLWPJ2MP.js";
40
+ } from "./chunk-JBR2FQKG.js";
40
41
  import {
41
42
  writeJsonAtomic,
42
43
  writeJsonAtomicDurable
43
- } from "./chunk-5YDIHB67.js";
44
+ } from "./chunk-UEWTZZY4.js";
44
45
  import {
45
46
  getApiKey,
46
47
  getBaseUrl
47
- } from "./chunk-NP65LEAY.js";
48
+ } from "./chunk-L4FQ3I7N.js";
48
49
  import {
49
50
  PENDING_COMMAND_PREFIX,
50
51
  SESSION_HISTORY_BACKFILL_LIMIT_BYTES,
@@ -55,30 +56,30 @@ import {
55
56
  } from "./chunk-5TLDFPW6.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,72 @@ 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 assertVerifiedPhone = (offer, pin, expectedFingerprint) => {
146
+ if (!canonicalPublicKey(offer.phonePublicKey) || fingerprint(offer.phonePublicKey) !== expectedFingerprint.trim().toLowerCase()) {
147
+ throw new Error("The fingerprint does not match this phone. Nothing was changed.");
148
+ }
149
+ if (!pin) throw new Error("No existing history trust was found. Nothing was changed.");
150
+ if (!sameRecoveryRecipient(offer, pin)) throw new Error("The recovery recipient also changed. Phone verification cannot approve that change.");
151
+ };
152
+ var trustPhoneRecipient = (request) => {
153
+ const { offer, pinFile } = request;
154
+ const pin = loadPin(pinFile);
155
+ assertVerifiedPhone(offer, pin, request.expectedFingerprint);
156
+ if (!pin) throw new Error("No existing history trust was found.");
157
+ if (verifyTrustedRecipients(offer, pinFile).kind === "match") return "already_trusted";
158
+ const path = approvalFile(pinFile, pin, offer.phonePublicKey);
159
+ mkdirSync2(dirname2(path), { recursive: true, mode: 448 });
160
+ writeJsonAtomicDurable(path, { version: 1, sourcePinHash: pinHash(pin), phonePublicKey: offer.phonePublicKey }, 384);
161
+ return "trusted";
162
+ };
163
+
119
164
  // 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";
165
+ import { createHash as createHash6 } from "crypto";
166
+ import { lstatSync as lstatSync3, readFileSync as readFileSync6, readdirSync as readdirSync2, realpathSync, statSync as statSync3, unlinkSync as unlinkSync2 } from "fs";
167
+ import { basename, dirname as dirname4, isAbsolute, join as join7, relative, resolve as resolve2, sep } from "path";
123
168
 
124
169
  // 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";
170
+ import { closeSync, fstatSync, lstatSync, openSync, readFileSync, readSync, unlinkSync } from "fs";
171
+ import { join as join3 } from "path";
127
172
 
128
173
  // src/transcript-redaction.ts
129
174
  var redactDraft = (text, complete = false) => {
@@ -176,14 +221,14 @@ var stateSchema = external_exports.object({
176
221
  startedAt: external_exports.number().int().positive(),
177
222
  updatedAt: external_exports.number().int().positive()
178
223
  });
179
- var fileFor = (id, directory) => join2(directory, `${id}.display`);
224
+ var fileFor = (id, directory) => join3(directory, `${id}.display`);
180
225
  var readState = (sessionId, runtime) => {
181
226
  if (!identifier.safeParse(sessionId).success) return null;
182
227
  try {
183
228
  const path = fileFor(sessionId, runtime.directory);
184
229
  const info = lstatSync(path);
185
230
  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")));
231
+ const envelope = external_exports.object({ content: external_exports.string() }).parse(JSON.parse(readFileSync(path, "utf8")));
187
232
  const plain = decryptRecord(envelope.content, runtime.sessionKey);
188
233
  if (!plain) return null;
189
234
  const state = stateSchema.parse(JSON.parse(plain));
@@ -266,18 +311,18 @@ var readDisplayDraft = (sessionId, runtime) => {
266
311
  };
267
312
 
268
313
  // src/transcript-session-key.ts
269
- import { existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync3, readdirSync, rmSync as rmSync2, statSync } from "fs";
314
+ import { existsSync, mkdirSync as mkdirSync3, readFileSync as readFileSync2, readdirSync, rmSync as rmSync2, statSync } from "fs";
270
315
  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");
316
+ import { join as join4 } from "path";
317
+ var sessionKeyDirectory = () => process.env.PUSHARY_SESSION_KEY_DIR?.trim() || join4(homedir2(), ".pushary", "sessions");
273
318
  var SESSION_DISPLAY_TTL_MS = 48 * 60 * 60 * 1e3;
274
319
  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;
320
+ var fileFor2 = (sessionId, directory) => SAFE_SESSION_ID.test(sessionId) ? join4(directory, `${sessionId}.json`) : null;
276
321
  var loadSessionKey = (sessionId, directory = sessionKeyDirectory()) => {
277
322
  const path = fileFor2(sessionId, directory);
278
- if (!path || !existsSync2(path)) return null;
323
+ if (!path || !existsSync(path)) return null;
279
324
  try {
280
- const raw = JSON.parse(readFileSync3(path, "utf-8"));
325
+ const raw = JSON.parse(readFileSync2(path, "utf-8"));
281
326
  const key = typeof raw.sessionKey === "string" ? Buffer.from(raw.sessionKey, "base64") : null;
282
327
  if (!key || key.length !== 32) return null;
283
328
  return {
@@ -296,7 +341,7 @@ var saveSessionKey = (sessionId, value, directory = sessionKeyDirectory()) => {
296
341
  const path = fileFor2(sessionId, directory);
297
342
  if (!path) return false;
298
343
  try {
299
- mkdirSync2(directory, { recursive: true, mode: 448 });
344
+ mkdirSync3(directory, { recursive: true, mode: 448 });
300
345
  writeJsonAtomicDurable(path, {
301
346
  sessionKey: Buffer.from(value.sessionKey).toString("base64"),
302
347
  wrappedKey: value.wrappedKey,
@@ -311,12 +356,12 @@ var saveSessionKey = (sessionId, value, directory = sessionKeyDirectory()) => {
311
356
  }
312
357
  };
313
358
  var sweepStaleSessionDisplays = (now = Date.now(), dir = sessionKeyDirectory()) => {
314
- if (!existsSync2(dir)) return 0;
359
+ if (!existsSync(dir)) return 0;
315
360
  let removed = 0;
316
361
  try {
317
362
  for (const name of readdirSync(dir)) {
318
363
  if (!name.endsWith(".display")) continue;
319
- const path = join3(dir, name);
364
+ const path = join4(dir, name);
320
365
  try {
321
366
  if (now - statSync(path).mtimeMs <= SESSION_DISPLAY_TTL_MS) continue;
322
367
  rmSync2(path, { force: true });
@@ -426,9 +471,9 @@ var startDisplayPublisher = (options) => {
426
471
  };
427
472
 
428
473
  // 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";
474
+ import { createHash as createHash5 } from "crypto";
475
+ import { join as join5 } from "path";
476
+ import { closeSync as closeSync3, fstatSync as fstatSync3, openSync as openSync3, readFileSync as readFileSync5, readSync as readSync2, statSync as statSync2 } from "fs";
432
477
 
433
478
  // src/transcript-source.ts
434
479
  var normalizeTranscriptLines = (lines, source) => {
@@ -483,10 +528,10 @@ var transcriptLinesSince = (lines, oldestLineMs, dropCompactionSummaries) => lin
483
528
  });
484
529
 
485
530
  // src/transcript-snapshot-file.ts
486
- import { closeSync as closeSync2, fstatSync as fstatSync2, openSync as openSync2, readFileSync as readFileSync4 } from "fs";
531
+ import { closeSync as closeSync2, fstatSync as fstatSync2, openSync as openSync2, readFileSync as readFileSync3 } from "fs";
487
532
 
488
533
  // src/transcript-snapshot-source.ts
489
- import { createHash } from "crypto";
534
+ import { createHash as createHash3 } from "crypto";
490
535
  var identity = external_exports.string().min(1).max(256);
491
536
  var timestamp = external_exports.string().refine((value) => Number.isFinite(Date.parse(value)));
492
537
  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 +657,7 @@ var parseCursorSnapshot = (text) => {
612
657
  const record = JSON.parse(line);
613
658
  if (isRecord(record) && (record.type === "metadata" || record.type === "turn_ended")) continue;
614
659
  const message = cursorMessage.parse(record);
615
- const fingerprint2 = createHash("sha256").update(canonicalJsonText(message)).digest("hex");
660
+ const fingerprint2 = createHash3("sha256").update(canonicalJsonText(message)).digest("hex");
616
661
  const occurrence = occurrences.get(fingerprint2) ?? 0;
617
662
  occurrences.set(fingerprint2, occurrence + 1);
618
663
  messages.push({ id: `cursor:${fingerprint2}:${occurrence}`, role: message.role, content: message.message.content });
@@ -648,7 +693,7 @@ var readTranscriptSnapshot = (path, source, sessionId) => {
648
693
  try {
649
694
  const before = fstatSync2(descriptor);
650
695
  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");
696
+ const text = readFileSync3(descriptor, "utf8");
652
697
  const after = fstatSync2(descriptor);
653
698
  if (before.size !== after.size || before.mtimeMs !== after.mtimeMs) throw new Error("History snapshot changed while reading");
654
699
  let messages;
@@ -670,11 +715,11 @@ var readTranscriptSnapshot = (path, source, sessionId) => {
670
715
  };
671
716
 
672
717
  // 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";
718
+ import { lstatSync as lstatSync2, mkdirSync as mkdirSync4, readFileSync as readFileSync4 } from "fs";
719
+ import { dirname as dirname3 } from "path";
675
720
 
676
721
  // src/transcript-projection.ts
677
- import { createHash as createHash2 } from "crypto";
722
+ import { createHash as createHash4 } from "crypto";
678
723
  var newestWithinUploadLimits = (messages, limits) => {
679
724
  let start = messages.length;
680
725
  let contentBytes = 0;
@@ -703,7 +748,7 @@ var projectTranscriptSnapshot = (previous, messages, older) => {
703
748
  const order = message.sourcePosition;
704
749
  if (!message.id || message.id.length > 256 || Object.hasOwn(entries, message.id)) throw new Error("Invalid or duplicate history message identity");
705
750
  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");
751
+ const digest = createHash4("sha256").update(JSON.stringify(record)).digest("hex");
707
752
  entries[message.id] = { digest, order };
708
753
  const earlier = previous?.entries[message.id];
709
754
  if (earlier?.digest === digest && earlier.order === order) continue;
@@ -748,7 +793,7 @@ var TranscriptSnapshotJournal = class {
748
793
  return;
749
794
  }
750
795
  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")));
796
+ const saved = journalSchema.parse(JSON.parse(readFileSync4(path, "utf8")));
752
797
  if (saved.binding !== binding) throw new Error("History checkpoint belongs to another source");
753
798
  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
799
  }
@@ -799,7 +844,7 @@ var TranscriptSnapshotJournal = class {
799
844
  }
800
845
  store(next) {
801
846
  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 });
847
+ mkdirSync4(dirname3(this.path), { recursive: true, mode: 448 });
803
848
  writeJsonAtomicDurable(this.path, next, 384);
804
849
  this.current = journalSchema.parse(next);
805
850
  }
@@ -895,7 +940,7 @@ var appendOnlyReader = (path, source, epoch) => ({
895
940
  read(retentionDays, nowMs) {
896
941
  const version = sourceVersion(path);
897
942
  const read2 = epoch ? linesSeenDuringEpoch(epoch, path, source, nowMs, retentionDays) : null;
898
- const lines = read2?.lines ?? normalizeTranscriptLines(readFileSync6(path, "utf8").split("\n"), source);
943
+ const lines = read2?.lines ?? normalizeTranscriptLines(readFileSync5(path, "utf8").split("\n"), source);
899
944
  return {
900
945
  lines,
901
946
  acknowledge() {
@@ -911,8 +956,8 @@ var caughtUpReadStillCurrent = (caughtUp, version, nowMs) => caughtUp !== null &
911
956
  var UNCHANGED_SNAPSHOT_READ = { lines: [], acknowledge: () => "unchanged" };
912
957
  var snapshotReader = (options, source) => {
913
958
  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");
959
+ const binding = createHash5("sha256").update(JSON.stringify([options.baseUrl, options.apiKey, options.machineId, options.sessionId, source, path])).digest("hex");
960
+ const journalPath = join5(options.keyDirectory, "snapshots", binding + ".json");
916
961
  const epochStart = epoch?.startedAtMs ?? 0;
917
962
  const backfill = epoch ? epoch.backfill : SESSION_HISTORY_BACKFILL;
918
963
  let journal;
@@ -967,11 +1012,11 @@ var transcriptHealthReporter = (target) => {
967
1012
  };
968
1013
 
969
1014
  // src/transcript-sync.ts
970
- import { join as join5, resolve } from "path";
1015
+ import { join as join6, resolve } from "path";
971
1016
  var claudeTranscriptPath = (cwd, sessionId) => {
972
1017
  const projectId = resolve(cwd).replace(/[^a-zA-Z0-9-]/g, "-");
973
1018
  const configDir = claudeConfigDir();
974
- return join5(configDir, "projects", projectId, `${sessionId}.jsonl`);
1019
+ return join6(configDir, "projects", projectId, `${sessionId}.jsonl`);
975
1020
  };
976
1021
  var createSyncState = () => ({
977
1022
  sessionKey: null,
@@ -1102,7 +1147,7 @@ var startTranscriptSync = (opts) => {
1102
1147
  recipientFailure = reason;
1103
1148
  });
1104
1149
  if (!offer) return null;
1105
- const verdict = verifyRecipients(offer, loadPin(opts.storage?.recipientPinFile));
1150
+ const verdict = verifyTrustedRecipients(offer, opts.storage?.recipientPinFile);
1106
1151
  if (verdict.kind === "first-use" && !savePin(verdict.pin, opts.storage?.recipientPinFile)) return null;
1107
1152
  if (verdict.kind === "changed") {
1108
1153
  recipientFailure = "phone_key_changed";
@@ -1168,13 +1213,14 @@ var startTranscriptSync = (opts) => {
1168
1213
  if (ending) opts.onDrained?.();
1169
1214
  return;
1170
1215
  }
1216
+ waitingForRecipients = true;
1171
1217
  const recipients = await fetchPinnedRecipients();
1172
1218
  if (!recipients) {
1173
- waitingForRecipients = true;
1174
1219
  if (recipientFailure === "off_for_workspace") passOverConversationWhileHistoryIsOff();
1175
1220
  await reportHealth(recipientFailure);
1176
1221
  return;
1177
1222
  }
1223
+ waitingForRecipients = false;
1178
1224
  if (reader.requiresRevisions && recipients.phoneReadsTranscriptRevisions !== true) {
1179
1225
  await reportHealth("phone_upgrade_required");
1180
1226
  return;
@@ -1253,15 +1299,15 @@ var storedRegistrationSchema = external_exports.object({
1253
1299
  endedAt: external_exports.number().positive().optional(),
1254
1300
  drained: external_exports.boolean().optional()
1255
1301
  });
1256
- var defaultRegistryDirectory = () => process.env.PUSHARY_TRANSCRIPT_REGISTRY_DIR || join6(pusharyDir(), "transcript-capture");
1302
+ var defaultRegistryDirectory = () => process.env.PUSHARY_TRANSCRIPT_REGISTRY_DIR || join7(pusharyDir(), "transcript-capture");
1257
1303
  var cliRegistry = (apiKey = getApiKey()) => ({ apiKey, baseUrl: getBaseUrl(), directory: defaultRegistryDirectory() });
1258
- var accountFor = (registry) => createHash4("sha256").update(`${registry.baseUrl}\0${registry.apiKey}`).digest("hex");
1304
+ var accountFor = (registry) => createHash6("sha256").update(`${registry.baseUrl}\0${registry.apiKey}`).digest("hex");
1259
1305
  var TTL = 48 * 60 * 60 * 1e3;
1260
- var registeredReadPositionsFile = (registry) => join6(registry.directory, "read-positions", `${accountFor(registry)}.json`);
1306
+ var registeredReadPositionsFile = (registry) => join7(registry.directory, "read-positions", `${accountFor(registry)}.json`);
1261
1307
  var SNAPSHOT_KEYS_DIRECTORY_NAME = "snapshot-keys";
1262
1308
  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);
1309
+ var snapshotKeyDirectory = (registry) => join7(registry.directory, SNAPSHOT_KEYS_DIRECTORY_NAME, accountFor(registry));
1310
+ var snapshotRecipientPinFile = (registry = cliRegistry()) => join7(snapshotKeyDirectory(registry), SNAPSHOT_RECIPIENT_PIN_FILE_NAME);
1265
1311
  var entryAt = (path) => {
1266
1312
  try {
1267
1313
  return lstatSync3(path);
@@ -1276,18 +1322,18 @@ var removedPinFile = (path) => {
1276
1322
  return true;
1277
1323
  };
1278
1324
  var clearSnapshotRecipientPins = (registryDirectory = defaultRegistryDirectory()) => {
1279
- const accounts = join6(registryDirectory, SNAPSHOT_KEYS_DIRECTORY_NAME);
1325
+ const accounts = join7(registryDirectory, SNAPSHOT_KEYS_DIRECTORY_NAME);
1280
1326
  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);
1327
+ return readdirSync2(accounts, { withFileTypes: true }).filter((account) => account.isDirectory()).map((account) => removedPinFile(join7(accounts, account.name, SNAPSHOT_RECIPIENT_PIN_FILE_NAME))).includes(true);
1282
1328
  };
1283
1329
  var read = (path) => {
1284
1330
  if (statSync3(path).size > 16384) throw new Error("History registration exceeds its size limit");
1285
- return storedRegistrationSchema.parse(JSON.parse(readFileSync7(path, "utf8")));
1331
+ return storedRegistrationSchema.parse(JSON.parse(readFileSync6(path, "utf8")));
1286
1332
  };
1287
1333
  var consent = (record) => record.consentOverride ?? readTranscriptPreference() ?? true;
1288
1334
  var withoutTrailingSlash = (url) => url.replace(/\/+$/, "");
1289
1335
  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`);
1336
+ var fileFor3 = (registry, sessionId) => join7(registry.directory, `${createHash6("sha256").update(accountFor(registry) + sessionId).digest("hex")}.json`);
1291
1337
  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
1338
  var registeredTranscriptEnabled = (apiKey, sessionId, registry = cliRegistry(apiKey)) => {
1293
1339
  try {
@@ -1297,12 +1343,12 @@ var registeredTranscriptEnabled = (apiKey, sessionId, registry = cliRegistry(api
1297
1343
  }
1298
1344
  };
1299
1345
  var macOwnsClaudeHooks = (cwd) => [
1300
- join6(claudeConfigDir(), "settings.json"),
1301
- join6(cwd, ".claude", "settings.json"),
1302
- join6(cwd, ".claude", "settings.local.json")
1346
+ join7(claudeConfigDir(), "settings.json"),
1347
+ join7(cwd, ".claude", "settings.json"),
1348
+ join7(cwd, ".claude", "settings.local.json")
1303
1349
  ].some((path) => {
1304
1350
  try {
1305
- return hookOwnerOf(JSON.parse(readFileSync7(path, "utf8"))) === "app";
1351
+ return hookOwnerOf(JSON.parse(readFileSync6(path, "utf8"))) === "app";
1306
1352
  } catch {
1307
1353
  return false;
1308
1354
  }
@@ -1310,12 +1356,12 @@ var macOwnsClaudeHooks = (cwd) => [
1310
1356
  var TRANSCRIPT_ROOT_NAME = { claude: "projects", codex: "sessions", gemini: "tmp", cursor: "projects", hermes: "hermes-history" };
1311
1357
  var transcriptRoots = (source) => {
1312
1358
  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")];
1359
+ if (source === "codex") return [join7(codexHome(), TRANSCRIPT_ROOT_NAME.codex)];
1360
+ if (source === "cursor") return [join7(home, ".cursor", TRANSCRIPT_ROOT_NAME.cursor)];
1361
+ if (source === "hermes") return [join7(home, ".pushary", TRANSCRIPT_ROOT_NAME.hermes)];
1362
+ if (source === "gemini") return [join7(process.env.GEMINI_CLI_HOME || home, ".gemini", "tmp"), join7(home, ".cache", ".gemini", "tmp")];
1317
1363
  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)];
1364
+ return [join7(configured || join7(home, ".claude"), TRANSCRIPT_ROOT_NAME.claude), join7(home, ".claude", TRANSCRIPT_ROOT_NAME.claude)];
1319
1365
  };
1320
1366
  var isAgentTranscriptRoot = (root, source) => isAbsolute(root) && resolve2(root) === root && basename(root) === TRANSCRIPT_ROOT_NAME[source];
1321
1367
  var acceptedTranscriptRoots = (source, registeredRoot) => typeof registeredRoot === "string" && isAgentTranscriptRoot(registeredRoot, source) ? [...transcriptRoots(source), registeredRoot] : transcriptRoots(source);
@@ -1324,8 +1370,8 @@ var resolvedSourcePath = (path) => {
1324
1370
  try {
1325
1371
  return realpathSync(absolute);
1326
1372
  } catch {
1327
- const parent = dirname3(absolute);
1328
- return parent === absolute ? absolute : join6(resolvedSourcePath(parent), basename(absolute));
1373
+ const parent = dirname4(absolute);
1374
+ return parent === absolute ? absolute : join7(resolvedSourcePath(parent), basename(absolute));
1329
1375
  }
1330
1376
  };
1331
1377
  var transcriptRootContaining = (candidate, roots) => {
@@ -1345,7 +1391,7 @@ var captureRegistration = (source, value, registeredRoot) => {
1345
1391
  const segments = relative(root, path).split(sep);
1346
1392
  if (source === "hermes" && (segments.length !== 2 || !/^[a-f0-9]{64}$/.test(segments[0] ?? "") || basename(path) !== `${sessionId}.json`)) return null;
1347
1393
  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;
1394
+ if (source === "cursor" && (segments[1] !== "agent-transcripts" || basename(path) !== `${sessionId}.jsonl` || !(segments.length === 3 || segments.length === 4 && basename(dirname4(path)) === sessionId))) return null;
1349
1395
  return { sessionId, cwd: input.cwd, path, source, root };
1350
1396
  };
1351
1397
  var SESSION_END_EVENT = "SessionEnd";
@@ -1419,7 +1465,7 @@ var startRegisteredTranscripts = (apiKey, machineId, options = {}) => {
1419
1465
  try {
1420
1466
  let unreadable = 0;
1421
1467
  const files = readdirSync2(registry.directory).filter((name) => name.endsWith(".json")).flatMap((name) => {
1422
- const path = join6(registry.directory, name);
1468
+ const path = join7(registry.directory, name);
1423
1469
  try {
1424
1470
  const time = statSync3(path).mtimeMs;
1425
1471
  if (Date.now() - time > TTL) {
@@ -1517,15 +1563,20 @@ var startRegisteredTranscripts = (apiKey, machineId, options = {}) => {
1517
1563
  };
1518
1564
 
1519
1565
  export {
1566
+ recipientPinFile,
1567
+ loadPin,
1568
+ clearPin,
1569
+ fingerprint,
1570
+ verifyTrustedRecipients,
1571
+ assertVerifiedPhone,
1572
+ trustPhoneRecipient,
1520
1573
  redactDraft,
1521
1574
  captureDisplayDraft,
1522
1575
  sessionKeyDirectory,
1523
1576
  loadSessionKey,
1524
1577
  startDraftPublisher,
1525
- loadPin,
1526
- clearPin,
1527
- fingerprint,
1528
1578
  claudeTranscriptPath,
1579
+ snapshotRecipientPinFile,
1529
1580
  clearSnapshotRecipientPins,
1530
1581
  registeredTranscriptEnabled,
1531
1582
  captureRegistration,
@@ -9,7 +9,7 @@ import {
9
9
  } from "./chunk-PPY5N2E6.js";
10
10
  import {
11
11
  NPX_LATEST_COMMAND
12
- } from "./chunk-KWWHFTNJ.js";
12
+ } from "./chunk-RC65MCPA.js";
13
13
  import {
14
14
  CLAUDE_CONNECTOR_GUIDE,
15
15
  CLAUDE_CONNECTOR_STEPS,
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  readTranscriptPreference
3
- } from "./chunk-TQ43EX2E.js";
3
+ } from "./chunk-P6EUHG5X.js";
4
4
 
5
5
  // src/transcript-setting.ts
6
6
  var transcriptEnvironmentPreference = () => {
@@ -4,7 +4,7 @@ import {
4
4
  quoteIfNeeded,
5
5
  shellDoubleQuoted,
6
6
  writeFileAtomic
7
- } from "./chunk-5YDIHB67.js";
7
+ } from "./chunk-UEWTZZY4.js";
8
8
  import {
9
9
  cmdShimScriptOf
10
10
  } from "./chunk-ZVU6WMMU.js";
@@ -798,7 +798,7 @@ You have Pushary MCP tools that reach the user on their phone. Use them proactiv
798
798
  - Before a task of more than two steps, break it into steps and find the forks. Settle by yourself every fork that a file, a command or a tool call can answer, then ask the rest as one numbered round with your recommended answer against each. A question in the terminal is free; a push is not. When the user is at the terminal, ask the whole round there. When the user is away, send only the fork that stops the run. Decide another fork on your recommendation only if it is reversible, spends nothing and stays inside the task, and list it in your final message; ask the rest once the first answer is back.
799
799
  - When you are blocked, stuck, or hit an error you cannot resolve on your own, call \`send_notification\` so the user knows, and call \`ask_user\` if you need a decision to continue.
800
800
  - When meaningful work finishes while the user is away, or they requested an alert, call \`send_notification\` so the user knows it is done. Skip trivial completions and duplicate reports to someone already reading. Always pass \`context\` with a \`type\` of "task_complete", "error" or "info": it is what tells Pushary this is a task update, so the user's own setting for where updates land can route it.
801
- - Confirm notifications can offer lock-screen actions; arbitrary choices and text open the app. The Mac notch handles personal account questions, not Partner customer enrollment.
801
+ - Confirm notifications can offer lock-screen actions; arbitrary choices and text open the app. The Mac notch answers questions too.
802
802
  - Use \`cancel_question\` to retract a pending question once it is no longer needed.
803
803
  - When the user asks to be reminded later, call \`schedule_reminder\` with \`body\` and either \`inMinutes\` or \`at\` with an explicit UTC offset. Confirm the returned time, never guess their timezone, and do not use \`ask_user\` as a reminder. Pushary saves it and delivers after this agent exits. Call with no arguments to list pending reminders or \`cancelReminderId\` to cancel one before dispatch starts.
804
804
  - Respect the user's Pushary delivery mode: it decides where an approval is surfaced (phone, terminal, or awareness only), not whether it is required. In Terminal mode do not expect a phone answer; the approval waits in the terminal. If the Pushary hook already gated an action, a single approval is enough, do not ask again.
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  DEFAULT_SESSION
3
- } from "./chunk-4RZXECJS.js";
3
+ } from "./chunk-CV3ALIG7.js";
4
4
 
5
5
  // src/answer.ts
6
6
  var isDeferAnswer = (value) => value === "defer";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  NPX_LATEST_COMMAND
3
- } from "./chunk-KWWHFTNJ.js";
3
+ } from "./chunk-RC65MCPA.js";
4
4
 
5
5
  // src/config.ts
6
6
  import { readFileSync } from "fs";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  guardBinary
3
- } from "./chunk-YLWPJ2MP.js";
3
+ } from "./chunk-JBR2FQKG.js";
4
4
 
5
5
  // src/bell/install.ts
6
6
  import { join } from "path";
@@ -7,7 +7,7 @@ import {
7
7
  recordSealerFor,
8
8
  uploadTranscriptRecords,
9
9
  wrapSessionKey
10
- } from "./chunk-4PS27SCG.js";
10
+ } from "./chunk-VHO3HV7P.js";
11
11
 
12
12
  // src/live-session.ts
13
13
  var MAX_TRANSCRIPT_QUEUE = 100;