pushary 1.9.73 → 1.9.76

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 (127) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/dist/{api-JVWXPKEW.js → api-YDCHRRA4.js} +10 -10
  3. package/dist/bin/pushary-bell-hook.js +27 -25
  4. package/dist/bin/pushary-bell.js +14 -14
  5. package/dist/bin/pushary-claude.js +55 -82
  6. package/dist/bin/pushary-clean.js +37 -37
  7. package/dist/bin/pushary-codex-bridge.js +24 -24
  8. package/dist/bin/pushary-codex-hook.js +36 -34
  9. package/dist/bin/pushary-codex-session.js +8 -8
  10. package/dist/bin/pushary-codex.js +15 -15
  11. package/dist/bin/pushary-connect.js +26 -26
  12. package/dist/bin/pushary-cowork.js +5 -5
  13. package/dist/bin/pushary-daemon.js +1293 -63
  14. package/dist/bin/pushary-disconnect.js +31 -31
  15. package/dist/bin/pushary-display-hook.d.ts +1 -0
  16. package/dist/bin/pushary-display-hook.js +58 -0
  17. package/dist/bin/pushary-doctor.js +50 -50
  18. package/dist/bin/pushary-elicitation-hook.js +29 -27
  19. package/dist/bin/pushary-gemini-bridge.js +16 -16
  20. package/dist/bin/pushary-gemini-hook.js +31 -29
  21. package/dist/bin/pushary-hook.js +31 -29
  22. package/dist/bin/pushary-login.js +20 -20
  23. package/dist/bin/pushary-logout.js +30 -30
  24. package/dist/bin/pushary-mcp.js +14 -14
  25. package/dist/bin/pushary-mode.js +18 -18
  26. package/dist/bin/pushary-notification-hook.js +26 -24
  27. package/dist/bin/pushary-opencode-hook.js +30 -28
  28. package/dist/bin/pushary-permission-denied-hook.js +31 -29
  29. package/dist/bin/pushary-permission-hook.js +31 -29
  30. package/dist/bin/pushary-post-hook.js +32 -29
  31. package/dist/bin/pushary-prompt-hook.js +32 -29
  32. package/dist/bin/pushary-session-end-hook.js +28 -26
  33. package/dist/bin/pushary-session-start-hook.js +36 -34
  34. package/dist/bin/pushary-setup.js +66 -282
  35. package/dist/bin/pushary-stats.js +6 -6
  36. package/dist/bin/pushary-status.js +31 -31
  37. package/dist/bin/pushary-stop-hook.js +32 -26
  38. package/dist/bin/pushary-stopfailure-hook.js +26 -24
  39. package/dist/bin/pushary-suggestions.js +18 -18
  40. package/dist/bin/pushary-transcript-register.js +18 -16
  41. package/dist/bin/pushary-transcripts.js +20 -20
  42. package/dist/bin/pushary-upgrade.js +45 -45
  43. package/dist/bin/pushary-wait.js +18 -18
  44. package/dist/bin/pushary.js +7 -7
  45. package/dist/{chunk-F3WI32HG.js → chunk-2BK2EQOB.js} +7 -7
  46. package/dist/{chunk-ZBZBYUNZ.js → chunk-2QFYEB3P.js} +5 -5
  47. package/dist/{chunk-UIF7HWHX.js → chunk-3YU6NIIL.js} +4 -4
  48. package/dist/{chunk-L2FQBHIR.js → chunk-47TIRGS5.js} +2 -2
  49. package/dist/{chunk-AXZ67XVN.js → chunk-4LX5QUFV.js} +1 -1
  50. package/dist/{chunk-YZ6DMGZZ.js → chunk-57MQNSM5.js} +1 -1
  51. package/dist/{chunk-BLX6KRK5.js → chunk-5FV472Q2.js} +1 -1
  52. package/dist/{chunk-VBSPP4QY.js → chunk-5KPMDLXB.js} +3 -3
  53. package/dist/{chunk-S63VIM7E.js → chunk-5M5F4IWR.js} +2 -2
  54. package/dist/{chunk-7FROPPIH.js → chunk-5NW7VM35.js} +2 -2
  55. package/dist/{chunk-EITOMPFB.js → chunk-74ONBRPF.js} +4 -4
  56. package/dist/{chunk-7S46LCKD.js → chunk-7KYQ24FI.js} +3 -2
  57. package/dist/{chunk-NGNBX3YH.js → chunk-7LLC4R66.js} +4 -4
  58. package/dist/{chunk-AC5TVOAH.js → chunk-7SFP6Y5U.js} +373 -93
  59. package/dist/{chunk-CMYFV3VC.js → chunk-7ZOWQBHQ.js} +5 -5
  60. package/dist/{chunk-2QB7HJVJ.js → chunk-B2PFCS72.js} +2 -2
  61. package/dist/{chunk-UUCRWEEU.js → chunk-BUR2LYQI.js} +2 -2
  62. package/dist/{chunk-W47XZJSH.js → chunk-C2AR7QXU.js} +2 -2
  63. package/dist/{chunk-FK3JSDH5.js → chunk-CHIOUBPB.js} +13 -13
  64. package/dist/{chunk-TTGBS5JB.js → chunk-CMNB4IZO.js} +3 -3
  65. package/dist/{chunk-4UIR25GA.js → chunk-CUD6SYDG.js} +1 -1
  66. package/dist/{chunk-WJJVCDEU.js → chunk-DB3NOEDW.js} +6 -6
  67. package/dist/{chunk-PGJMSREY.js → chunk-DZF2M3LM.js} +8 -8
  68. package/dist/{chunk-2NG3ANMT.js → chunk-E2VJ7B7D.js} +9 -9
  69. package/dist/{chunk-S7VMHSJW.js → chunk-E7U4YM4A.js} +5 -5
  70. package/dist/{chunk-XKQ7CQGG.js → chunk-F7R7M5MG.js} +1 -1
  71. package/dist/{chunk-FAKEKFCI.js → chunk-FAGQAR6I.js} +5 -3
  72. package/dist/{chunk-3JJI5O5N.js → chunk-GII565PB.js} +1 -1
  73. package/dist/{chunk-WXV7J3DG.js → chunk-GJVV2EUC.js} +3 -3
  74. package/dist/{chunk-Z7AIYWZ2.js → chunk-IRLHN5UP.js} +236 -20
  75. package/dist/{chunk-NJSW2REO.js → chunk-IRN6JLSM.js} +4 -4
  76. package/dist/{chunk-JC7XUZST.js → chunk-ITLWEGLE.js} +31 -5
  77. package/dist/{chunk-7M4AA7QF.js → chunk-JNV5LOZH.js} +4 -4
  78. package/dist/{chunk-IN3MPEFA.js → chunk-JZ5KV2FK.js} +5 -5
  79. package/dist/{chunk-QAWWTRDD.js → chunk-KGWLDNNT.js} +4 -4
  80. package/dist/{chunk-O4EAMVSC.js → chunk-KWGOKO4E.js} +5 -5
  81. package/dist/{chunk-KDQLKND5.js → chunk-M5VJ3HXB.js} +6 -6
  82. package/dist/{chunk-R5PZPROX.js → chunk-M7JFGDGP.js} +1 -1
  83. package/dist/{chunk-N4VRSFRZ.js → chunk-MAWC4PJV.js} +5 -5
  84. package/dist/chunk-MHUKYYYR.js +239 -0
  85. package/dist/{chunk-56GK65Z6.js → chunk-MMVIKXCP.js} +3 -3
  86. package/dist/{chunk-CGWUVEOB.js → chunk-MTRZQR2R.js} +3 -3
  87. package/dist/{chunk-JYOR57ZW.js → chunk-NFUV25JZ.js} +3 -3
  88. package/dist/{chunk-EUHTC7K5.js → chunk-NJTAUEW6.js} +1 -1
  89. package/dist/{chunk-2JFEMKDG.js → chunk-NQA56AMZ.js} +6 -6
  90. package/dist/{chunk-AJWGZJFS.js → chunk-NVLKH5FS.js} +131 -28
  91. package/dist/{chunk-2HFC4W6J.js → chunk-PPD64MZW.js} +4 -3
  92. package/dist/{chunk-3RAWW5DH.js → chunk-QUGP76DQ.js} +6 -6
  93. package/dist/{chunk-PPKIL4EL.js → chunk-R3HXGHRQ.js} +1 -1
  94. package/dist/{chunk-3QBKMSOZ.js → chunk-S7C5NY4R.js} +1 -1
  95. package/dist/{chunk-EUEGCEA7.js → chunk-SWRNJ3BP.js} +3 -3
  96. package/dist/{chunk-RZVWEDIS.js → chunk-T2EJ3H6Y.js} +3 -3
  97. package/dist/{chunk-DT6VCMXS.js → chunk-TFYFQM5C.js} +2 -2
  98. package/dist/{chunk-GBR6E7AY.js → chunk-UEY4SHY2.js} +1 -1
  99. package/dist/{chunk-TFDMJHAB.js → chunk-UMBVIFP5.js} +1 -1
  100. package/dist/{chunk-TQ2L3ECX.js → chunk-USMUW3LI.js} +2 -2
  101. package/dist/{chunk-VJUDBZGC.js → chunk-USZJJWZL.js} +2 -2
  102. package/dist/{chunk-4LAD5A2Q.js → chunk-VELPOV2F.js} +4 -4
  103. package/dist/{chunk-JNTYPMVU.js → chunk-VZSSL6US.js} +10 -10
  104. package/dist/{chunk-KSLBS3II.js → chunk-W5AUTVVL.js} +6 -5
  105. package/dist/{chunk-OJ3ZD5VX.js → chunk-WK2L3XVQ.js} +1 -1
  106. package/dist/{chunk-PM2MHBMF.js → chunk-WLHHDDVA.js} +9 -4
  107. package/dist/{chunk-U3EBM73X.js → chunk-WRCM4XGY.js} +2 -2
  108. package/dist/{chunk-SQ5XCMPB.js → chunk-XJ5DYPTM.js} +4 -4
  109. package/dist/{chunk-PNXNWZ5P.js → chunk-XLGD5LDG.js} +3 -3
  110. package/dist/{chunk-7RX5TOU3.js → chunk-Y2CYSVWU.js} +6 -6
  111. package/dist/{chunk-AVHL2ZTS.js → chunk-Y4OBAK4I.js} +459 -172
  112. package/dist/{chunk-JWBZUTYF.js → chunk-YDPGBBV7.js} +5 -5
  113. package/dist/{chunk-FB262EOM.js → chunk-YLB4AGZJ.js} +1 -1
  114. package/dist/{chunk-QMPM3CAN.js → chunk-ZY25SNFQ.js} +1 -1
  115. package/dist/{codex-app-cards-switch-M7VH7AZH.js → codex-app-cards-switch-3OUTABVU.js} +11 -11
  116. package/dist/{codex-desktop-launch-6DASTBBA.js → codex-desktop-launch-JH3E2CIA.js} +1 -1
  117. package/dist/{codex-session-launch-2XSMFA4K.js → codex-session-launch-GR363OQN.js} +18 -18
  118. package/dist/{config-7QQOIFTW.js → config-R43UFHX7.js} +3 -3
  119. package/dist/{global-upgrade-XAKX26AN.js → global-upgrade-73GP4MZM.js} +10 -10
  120. package/dist/{identity-7AS5MBNM.js → identity-CA7CZGDL.js} +4 -4
  121. package/dist/{live-capabilities-DV4F7MIB.js → live-capabilities-RCGMQIIZ.js} +5 -5
  122. package/dist/{paths-GSMXYKT5.js → paths-H7VNNB46.js} +3 -3
  123. package/dist/{reapply-MR7UWBXR.js → reapply-3UIA3CUO.js} +27 -27
  124. package/dist/src/index.js +32 -30
  125. package/dist/{upgrade-lock-6OTHLCBZ.js → upgrade-lock-ZTLX5PDF.js} +4 -4
  126. package/dist/{usage-H53PO3VQ.js → usage-C53NVWC2.js} +3 -3
  127. package/package.json +4 -2
@@ -3,112 +3,218 @@ import {
3
3
  loadPin,
4
4
  savePin,
5
5
  verifyRecipients
6
- } from "./chunk-FB262EOM.js";
6
+ } from "./chunk-YLB4AGZJ.js";
7
+ import {
8
+ SESSION_HISTORY_RETENTION_DAYS
9
+ } from "./chunk-MHUKYYYR.js";
7
10
  import {
8
11
  ensureDaemonRunning
9
- } from "./chunk-WJJVCDEU.js";
12
+ } from "./chunk-DB3NOEDW.js";
10
13
  import {
11
14
  hookOwnerOf
12
- } from "./chunk-QAWWTRDD.js";
15
+ } from "./chunk-KGWLDNNT.js";
13
16
  import {
14
17
  buildTranscriptRecords,
15
18
  decodePublicKey,
19
+ decryptRecord,
20
+ encryptRecord,
16
21
  fetchTranscriptRecipients,
17
22
  generateSessionKey,
18
23
  recordSealerFor,
19
24
  uploadTranscriptRecords,
20
25
  wrapSessionKey
21
- } from "./chunk-JC7XUZST.js";
26
+ } from "./chunk-ITLWEGLE.js";
27
+ import {
28
+ external_exports
29
+ } from "./chunk-XMSDA5WA.js";
22
30
  import {
23
31
  transcriptEnvironmentPreference,
24
32
  transcriptsEnabled
25
- } from "./chunk-S63VIM7E.js";
33
+ } from "./chunk-5M5F4IWR.js";
26
34
  import {
27
35
  readTranscriptPreference
28
- } from "./chunk-NGNBX3YH.js";
36
+ } from "./chunk-7LLC4R66.js";
29
37
  import {
30
38
  claudeConfigDir,
31
39
  codexHome,
32
40
  pusharyDir
33
- } from "./chunk-7S46LCKD.js";
41
+ } from "./chunk-7KYQ24FI.js";
34
42
  import {
35
43
  writeJsonAtomic
36
- } from "./chunk-RZVWEDIS.js";
44
+ } from "./chunk-T2EJ3H6Y.js";
37
45
  import {
38
46
  getApiKey,
39
47
  getBaseUrl
40
- } from "./chunk-R5PZPROX.js";
48
+ } from "./chunk-M7JFGDGP.js";
49
+ import {
50
+ PENDING_COMMAND_PREFIX,
51
+ SESSION_HISTORY_BACKFILL_LIMIT_BYTES,
52
+ redactSecretsFromContent
53
+ } from "./chunk-Y4OBAK4I.js";
41
54
 
42
55
  // src/transcript-registration.ts
43
56
  import { createHash } from "crypto";
44
- import { readFileSync as readFileSync3, readdirSync as readdirSync2, statSync as statSync3, unlinkSync } from "fs";
45
- import { basename, isAbsolute, join as join3, resolve as resolve2, sep } from "path";
57
+ import { readFileSync as readFileSync4, readdirSync as readdirSync2, statSync as statSync3, unlinkSync as unlinkSync2 } from "fs";
58
+ import { basename, isAbsolute, join as join4, resolve as resolve2, sep } from "path";
46
59
 
47
- // src/transcript-source.ts
48
- var normalizeTranscriptLines = (lines, source) => {
49
- if (source === "claude") return [...lines];
50
- return lines.flatMap((line) => {
51
- try {
52
- const record = JSON.parse(line);
53
- const item = record.payload;
54
- if (record.type !== "response_item" || item?.type !== "message" || !["user", "assistant"].includes(item.role)) return [];
55
- const content = (Array.isArray(item.content) ? item.content : []).flatMap((block) => ["input_text", "output_text"].includes(block.type ?? "") && typeof block.text === "string" ? [{ type: "text", text: block.text }] : []);
56
- return content.length ? [JSON.stringify({ timestamp: record.timestamp, message: { role: item.role, content } })] : [];
57
- } catch {
58
- return [];
59
- }
60
- });
60
+ // src/claude-display-draft.ts
61
+ import { closeSync, fstatSync, lstatSync, openSync, readFileSync, readSync, unlinkSync } from "fs";
62
+ import { join } from "path";
63
+
64
+ // src/transcript-redaction.ts
65
+ var redactDraft = (text, complete = false) => {
66
+ let end = complete ? text.length : text.lastIndexOf("\n") + 1;
67
+ const pem = /-----BEGIN [^\n]*PRIVATE KEY-----/g;
68
+ for (const match of text.matchAll(pem)) {
69
+ if (!/-----END [^\n]*PRIVATE KEY-----/.test(text.slice(match.index, end))) end = Math.min(end, match.index);
70
+ }
71
+ const quoted = /(?:secret|token|password|passwd|api[_-]?key|access[_-]?key|client[_-]?secret|private[_-]?key)\s*[=:]\s*(["'])/gi;
72
+ for (const match of text.matchAll(quoted)) {
73
+ const close = text.indexOf(match[1], match.index + match[0].length);
74
+ if (close < 0 || close >= end) end = Math.min(end, match.index);
75
+ }
76
+ return redactSecretsFromContent(text.slice(0, end));
61
77
  };
62
- var createdDuringEpoch = (sighting, epochStartedAtMs) => sighting.birthtimeMs !== null && sighting.birthtimeMs >= epochStartedAtMs && sighting.birthtimeMs <= sighting.seenAtMs;
63
- var everythingAlreadyWritten = (sighting) => ({ identity: sighting.identity, offset: sighting.sizeBytes, startedBeforeEpoch: true });
64
- var cutoffAtFirstSight = (sighting, epochStartedAtMs) => sighting.identity === null || createdDuringEpoch(sighting, epochStartedAtMs) ? { identity: sighting.identity, offset: 0, startedBeforeEpoch: false } : everythingAlreadyWritten(sighting);
65
- var replacedOrShrunk = (previous, sighting) => previous.identity !== sighting.identity || sighting.sizeBytes < previous.offset;
66
- var nextTranscriptCutoff = (previous, sighting, epochStartedAtMs) => {
67
- if (!previous) return cutoffAtFirstSight(sighting, epochStartedAtMs);
68
- if (sighting.identity === null) return previous;
69
- if (previous.identity === null) return { ...previous, identity: sighting.identity };
70
- return replacedOrShrunk(previous, sighting) ? everythingAlreadyWritten(sighting) : previous;
78
+
79
+ // src/claude-display-draft.ts
80
+ var MAX_TEXT = 32 * 1024;
81
+ var MAX_TAIL = 256 * 1024;
82
+ var MAX_AGE_MS = 9e4;
83
+ var identifier = external_exports.string().regex(/^[\w-]{1,128}$/);
84
+ var displaySchema = external_exports.object({
85
+ hook_event_name: external_exports.literal("MessageDisplay"),
86
+ session_id: identifier,
87
+ message_id: identifier,
88
+ index: external_exports.number().int().nonnegative(),
89
+ final: external_exports.boolean(),
90
+ delta: external_exports.string().max(MAX_TEXT),
91
+ transcript_path: external_exports.string().min(1),
92
+ agent_id: external_exports.undefined()
93
+ });
94
+ var containsPhoneInstruction = (content) => content.some((text) => {
95
+ if (typeof text !== "string") return false;
96
+ const prefixAt = text.indexOf(PENDING_COMMAND_PREFIX);
97
+ return prefixAt >= 0 && text.slice(prefixAt + PENDING_COMMAND_PREFIX.length).trim().length > 0;
98
+ });
99
+ var anchorSchema = external_exports.object({ uuid: identifier, isSidechain: external_exports.literal(false).optional() }).and(external_exports.union([
100
+ external_exports.object({ type: external_exports.enum(["user", "assistant"]) }),
101
+ external_exports.object({ type: external_exports.literal("attachment"), attachment: external_exports.object({
102
+ type: external_exports.literal("hook_additional_context"),
103
+ content: external_exports.array(external_exports.unknown()).refine(containsPhoneInstruction)
104
+ }) })
105
+ ]));
106
+ var stateSchema = external_exports.object({
107
+ displayId: identifier,
108
+ afterRecordId: identifier,
109
+ index: external_exports.number().int().nonnegative(),
110
+ text: external_exports.string().max(MAX_TEXT),
111
+ complete: external_exports.boolean(),
112
+ startedAt: external_exports.number().int().positive(),
113
+ updatedAt: external_exports.number().int().positive()
114
+ });
115
+ var fileFor = (id, directory) => join(directory, `${id}.display`);
116
+ var readState = (sessionId, runtime) => {
117
+ if (!identifier.safeParse(sessionId).success) return null;
118
+ try {
119
+ const path = fileFor(sessionId, runtime.directory);
120
+ const info = lstatSync(path);
121
+ if (!info.isFile() || info.nlink !== 1 || info.size > MAX_TAIL || (info.mode & 511) !== 384) return null;
122
+ const envelope = external_exports.object({ content: external_exports.string() }).parse(JSON.parse(readFileSync(path, "utf8")));
123
+ const plain = decryptRecord(envelope.content, runtime.sessionKey);
124
+ if (!plain) return null;
125
+ const state = stateSchema.parse(JSON.parse(plain));
126
+ return runtime.now - state.updatedAt <= MAX_AGE_MS && runtime.now >= state.updatedAt ? state : null;
127
+ } catch {
128
+ return null;
129
+ }
71
130
  };
72
- var advancedTranscriptCutoff = (current, readFrom, settledThroughOffset) => current !== void 0 && current.identity === readFrom.identity && settledThroughOffset > current.offset ? { ...current, offset: settledThroughOffset } : current;
73
- var isTranscriptObject = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
74
- var parsedTranscriptRecord = (line) => {
131
+ var conversationAnchor = (path) => {
132
+ let descriptor;
75
133
  try {
76
- const value = JSON.parse(line);
77
- return isTranscriptObject(value) ? value : null;
134
+ descriptor = openSync(path, "r");
135
+ const size = fstatSync(descriptor).size;
136
+ const start = Math.max(0, size - MAX_TAIL);
137
+ const buffer = Buffer.alloc(Math.min(size, MAX_TAIL));
138
+ const length = readSync(descriptor, buffer, 0, buffer.length, start);
139
+ const lines = buffer.subarray(0, length).toString("utf8").split("\n");
140
+ if (start > 0) lines.shift();
141
+ lines.pop();
142
+ for (const line of lines.reverse()) {
143
+ try {
144
+ const value = anchorSchema.safeParse(JSON.parse(line));
145
+ if (value.success) return value.data.uuid;
146
+ } catch {
147
+ continue;
148
+ }
149
+ }
78
150
  } catch {
79
151
  return null;
152
+ } finally {
153
+ if (descriptor !== void 0) closeSync(descriptor);
80
154
  }
155
+ return null;
81
156
  };
82
- var stampedBefore = (record, momentMs) => {
83
- if (typeof record.timestamp !== "string") return false;
84
- const stamped = Date.parse(record.timestamp);
85
- return Number.isFinite(stamped) && stamped < momentMs;
157
+ var captureDisplayDraft = (input, runtime) => {
158
+ const parsed = displaySchema.safeParse(input);
159
+ if (!parsed.success) return false;
160
+ const event = parsed.data;
161
+ const previous = readState(event.session_id, runtime);
162
+ if (previous?.displayId === event.message_id && event.index <= previous.index) return false;
163
+ if (event.index !== 0 && (previous?.displayId !== event.message_id || previous.complete)) return false;
164
+ if (event.index !== 0 && previous && event.index !== previous.index + 1) {
165
+ try {
166
+ unlinkSync(fileFor(event.session_id, runtime.directory));
167
+ } catch {
168
+ }
169
+ return false;
170
+ }
171
+ if (event.index === 0 && event.final) return false;
172
+ const anchor = event.index === 0 ? conversationAnchor(event.transcript_path) : previous?.afterRecordId;
173
+ if (!anchor) return false;
174
+ const text = (event.index === 0 ? "" : previous?.text ?? "") + event.delta;
175
+ if (Buffer.byteLength(text, "utf8") > MAX_TEXT) return false;
176
+ const state = {
177
+ displayId: event.message_id,
178
+ afterRecordId: anchor,
179
+ index: event.index,
180
+ text,
181
+ complete: event.final,
182
+ startedAt: event.index === 0 ? runtime.now : previous?.startedAt ?? runtime.now,
183
+ updatedAt: runtime.now
184
+ };
185
+ const target = fileFor(event.session_id, runtime.directory);
186
+ const existing = lstatSync(target, { throwIfNoEntry: false });
187
+ if (existing && (!existing.isFile() || existing.nlink !== 1 || (existing.mode & 511) !== 384)) return false;
188
+ writeJsonAtomic(target, { content: encryptRecord(JSON.stringify(state), runtime.sessionKey) }, 384);
189
+ return true;
190
+ };
191
+ var readDisplayDraft = (sessionId, runtime) => {
192
+ const state = readState(sessionId, runtime);
193
+ if (!state) return null;
194
+ const text = redactDraft(state.text, state.complete);
195
+ if (!text) return null;
196
+ return { startedAt: state.startedAt, revision: state.index + 1, preview: {
197
+ displayId: state.displayId,
198
+ afterRecordId: state.afterRecordId,
199
+ text,
200
+ status: state.complete ? "complete" : "streaming"
201
+ } };
86
202
  };
87
- var isCompactionSummary = (record) => record.isCompactSummary === true;
88
- var transcriptLinesSeenLive = (lines, epochStartedAtMs, startedBeforeEpoch) => lines.filter((line) => {
89
- const record = parsedTranscriptRecord(line);
90
- if (record === null) return true;
91
- return !stampedBefore(record, epochStartedAtMs) && !(startedBeforeEpoch && isCompactionSummary(record));
92
- });
93
-
94
- // src/transcript-sync.ts
95
- import { join as join2, resolve } from "path";
96
- import { closeSync, existsSync as existsSync2, fstatSync, openSync, readFileSync as readFileSync2, readSync, statSync as statSync2 } from "fs";
97
203
 
98
204
  // src/transcript-session-key.ts
99
- import { existsSync, mkdirSync, readFileSync, readdirSync, rmSync, statSync, writeFileSync } from "fs";
205
+ import { existsSync, mkdirSync, readFileSync as readFileSync2, readdirSync, rmSync, statSync, writeFileSync } from "fs";
100
206
  import { homedir } from "os";
101
- import { join } from "path";
102
- var sessionDir = () => process.env.PUSHARY_SESSION_KEY_DIR?.trim() || join(homedir(), ".pushary", "sessions");
207
+ import { join as join2 } from "path";
208
+ var sessionKeyDirectory = () => process.env.PUSHARY_SESSION_KEY_DIR?.trim() || join2(homedir(), ".pushary", "sessions");
103
209
  var SESSION_KEY_TTL_MS = 48 * 60 * 60 * 1e3;
104
210
  var SAFE_SESSION_ID = /^[A-Za-z0-9._-]{1,128}$/;
105
- var fileFor = (sessionId, directory) => SAFE_SESSION_ID.test(sessionId) ? join(directory, `${sessionId}.json`) : null;
106
- var loadSessionKey = (sessionId, directory = sessionDir()) => {
107
- const path = fileFor(sessionId, directory);
211
+ var fileFor2 = (sessionId, directory) => SAFE_SESSION_ID.test(sessionId) ? join2(directory, `${sessionId}.json`) : null;
212
+ var loadSessionKey = (sessionId, directory = sessionKeyDirectory()) => {
213
+ const path = fileFor2(sessionId, directory);
108
214
  if (!path || !existsSync(path)) return null;
109
215
  try {
110
216
  if (Date.now() - statSync(path).mtimeMs > SESSION_KEY_TTL_MS) return null;
111
- const raw = JSON.parse(readFileSync(path, "utf-8"));
217
+ const raw = JSON.parse(readFileSync2(path, "utf-8"));
112
218
  const key = typeof raw.sessionKey === "string" ? Buffer.from(raw.sessionKey, "base64") : null;
113
219
  if (!key || key.length !== 32) return null;
114
220
  return {
@@ -123,8 +229,8 @@ var loadSessionKey = (sessionId, directory = sessionDir()) => {
123
229
  return null;
124
230
  }
125
231
  };
126
- var saveSessionKey = (sessionId, value, directory = sessionDir()) => {
127
- const path = fileFor(sessionId, directory);
232
+ var saveSessionKey = (sessionId, value, directory = sessionKeyDirectory()) => {
233
+ const path = fileFor2(sessionId, directory);
128
234
  if (!path) return;
129
235
  try {
130
236
  mkdirSync(directory, { recursive: true, mode: 448 });
@@ -143,13 +249,13 @@ var saveSessionKey = (sessionId, value, directory = sessionDir()) => {
143
249
  } catch {
144
250
  }
145
251
  };
146
- var sweepStaleSessionKeys = (now = Date.now(), dir = sessionDir()) => {
252
+ var sweepStaleSessionKeys = (now = Date.now(), dir = sessionKeyDirectory()) => {
147
253
  if (!existsSync(dir)) return 0;
148
254
  let removed = 0;
149
255
  try {
150
256
  for (const name of readdirSync(dir)) {
151
- if (!name.endsWith(".json")) continue;
152
- const path = join(dir, name);
257
+ if (!name.endsWith(".json") && !name.endsWith(".display")) continue;
258
+ const path = join2(dir, name);
153
259
  try {
154
260
  if (now - statSync(path).mtimeMs <= SESSION_KEY_TTL_MS) continue;
155
261
  rmSync(path, { force: true });
@@ -163,11 +269,160 @@ var sweepStaleSessionKeys = (now = Date.now(), dir = sessionDir()) => {
163
269
  return removed;
164
270
  };
165
271
 
272
+ // src/transcript-draft-publisher.ts
273
+ var startDraftPublisher = (options) => {
274
+ let running = true;
275
+ let pending;
276
+ let active;
277
+ let sent = "";
278
+ let retryAt = 0;
279
+ let retryMs = 1e3;
280
+ let reportedFailure = false;
281
+ const publish = async () => {
282
+ const controller = new AbortController();
283
+ active = controller;
284
+ try {
285
+ const sessionId = options.sessionId();
286
+ if (!sessionId) return;
287
+ const key = loadSessionKey(sessionId, options.keyDirectory);
288
+ if (!key?.keyUploaded) return;
289
+ const draft = options.snapshot(key.sessionKey);
290
+ if (!draft) return;
291
+ const identity = JSON.stringify([sessionId, draft.startedAt, draft.revision]);
292
+ if (identity === sent) return;
293
+ const response = await fetch(`${options.baseUrl}/api/agent/transcript`, {
294
+ method: "POST",
295
+ headers: { Authorization: `Bearer ${options.apiKey}`, "Content-Type": "application/json" },
296
+ body: JSON.stringify({ sessionId, draft: {
297
+ startedAt: draft.startedAt,
298
+ revision: draft.revision,
299
+ content: encryptRecord(JSON.stringify(draft.preview), key.sessionKey)
300
+ } }),
301
+ signal: AbortSignal.any([controller.signal, AbortSignal.timeout(5e3)])
302
+ });
303
+ if (!response.ok) throw new Error(`HTTP ${response.status}`);
304
+ sent = identity;
305
+ retryAt = 0;
306
+ retryMs = 1e3;
307
+ reportedFailure = false;
308
+ } catch (error) {
309
+ retryAt = Date.now() + retryMs;
310
+ retryMs = Math.min(3e4, retryMs * 2);
311
+ if (running && !controller.signal.aborted && !reportedFailure) {
312
+ reportedFailure = true;
313
+ process.stderr.write(`[pushary] live transcript preview could not sync (${error instanceof Error ? error.message : "request failed"}); saved history remains available.
314
+ `);
315
+ }
316
+ } finally {
317
+ active = void 0;
318
+ }
319
+ };
320
+ const flush = async (final = false) => {
321
+ if (!running) return;
322
+ if (!options.enabled()) {
323
+ active?.abort();
324
+ return;
325
+ }
326
+ if (pending || !final && Date.now() < retryAt) return;
327
+ pending = publish();
328
+ try {
329
+ await pending;
330
+ } finally {
331
+ pending = void 0;
332
+ }
333
+ };
334
+ const timer = setInterval(() => {
335
+ void flush();
336
+ }, 500);
337
+ timer.unref();
338
+ const stop = () => {
339
+ running = false;
340
+ clearInterval(timer);
341
+ active?.abort();
342
+ };
343
+ return {
344
+ stop,
345
+ finish: async () => {
346
+ clearInterval(timer);
347
+ await pending;
348
+ await flush(true);
349
+ stop();
350
+ }
351
+ };
352
+ };
353
+
354
+ // src/claude-display-publisher.ts
355
+ var startDisplayPublisher = (options) => {
356
+ const directory = options.directory ?? sessionKeyDirectory();
357
+ return startDraftPublisher({
358
+ apiKey: options.apiKey,
359
+ baseUrl: options.baseUrl,
360
+ keyDirectory: directory,
361
+ sessionId: () => options.sessionId,
362
+ enabled: options.enabled,
363
+ snapshot: (sessionKey) => readDisplayDraft(options.sessionId, { directory, sessionKey, now: Date.now() })
364
+ }).stop;
365
+ };
366
+
367
+ // src/transcript-source.ts
368
+ var normalizeTranscriptLines = (lines, source) => {
369
+ if (source === "claude") return [...lines];
370
+ return lines.flatMap((line) => {
371
+ try {
372
+ const record = JSON.parse(line);
373
+ const item = record.payload;
374
+ if (record.type !== "response_item" || item?.type !== "message" || !["user", "assistant"].includes(item.role)) return [];
375
+ const content = (Array.isArray(item.content) ? item.content : []).flatMap((block) => ["input_text", "output_text"].includes(block.type ?? "") && typeof block.text === "string" ? [{ type: "text", text: block.text }] : []);
376
+ return content.length ? [JSON.stringify({ timestamp: record.timestamp, message: { role: item.role, content } })] : [];
377
+ } catch {
378
+ return [];
379
+ }
380
+ });
381
+ };
382
+ var createdDuringEpoch = (sighting, epochStartedAtMs) => sighting.birthtimeMs !== null && sighting.birthtimeMs >= epochStartedAtMs && sighting.birthtimeMs <= sighting.seenAtMs;
383
+ var SESSION_HISTORY_BACKFILL = { limitBytes: SESSION_HISTORY_BACKFILL_LIMIT_BYTES };
384
+ var MS_PER_DAY = 864e5;
385
+ var everythingAlreadyWritten = (sighting) => ({ identity: sighting.identity, offset: sighting.sizeBytes, startedBeforeEpoch: true });
386
+ var earlierConversationStart = (sighting, backfill) => backfill === null ? everythingAlreadyWritten(sighting) : { identity: sighting.identity, offset: Math.max(0, sighting.sizeBytes - backfill.limitBytes), startedBeforeEpoch: true };
387
+ var cutoffAtFirstSight = (sighting, epochStartedAtMs, backfill) => sighting.identity === null || createdDuringEpoch(sighting, epochStartedAtMs) ? { identity: sighting.identity, offset: 0, startedBeforeEpoch: false } : earlierConversationStart(sighting, backfill);
388
+ var replacedOrShrunk = (previous, sighting) => previous.identity !== sighting.identity || sighting.sizeBytes < previous.offset;
389
+ var nextTranscriptCutoff = (previous, sighting, epochStartedAtMs, backfill = null) => {
390
+ if (!previous) return cutoffAtFirstSight(sighting, epochStartedAtMs, backfill);
391
+ if (sighting.identity === null) return previous;
392
+ if (previous.identity === null) return { ...previous, identity: sighting.identity };
393
+ return replacedOrShrunk(previous, sighting) ? everythingAlreadyWritten(sighting) : previous;
394
+ };
395
+ var advancedTranscriptCutoff = (current, readFrom, settledThroughOffset) => current !== void 0 && current.identity === readFrom.identity && settledThroughOffset > current.offset ? { ...current, offset: settledThroughOffset } : current;
396
+ var isTranscriptObject = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
397
+ var parsedTranscriptRecord = (line) => {
398
+ try {
399
+ const value = JSON.parse(line);
400
+ return isTranscriptObject(value) ? value : null;
401
+ } catch {
402
+ return null;
403
+ }
404
+ };
405
+ var stampedBefore = (record, momentMs) => {
406
+ if (typeof record.timestamp !== "string") return false;
407
+ const stamped = Date.parse(record.timestamp);
408
+ return Number.isFinite(stamped) && stamped < momentMs;
409
+ };
410
+ var isCompactionSummary = (record) => record.isCompactSummary === true;
411
+ var oldestTranscriptLineMs = (epochStartedAtMs, backfill, retentionDays, nowMs) => backfill === null ? epochStartedAtMs : nowMs - retentionDays * MS_PER_DAY;
412
+ var dropsCompactionSummaries = (cutoff, backfill) => cutoff.startedBeforeEpoch && backfill === null;
413
+ var transcriptLinesSince = (lines, oldestLineMs, dropCompactionSummaries) => lines.filter((line) => {
414
+ const record = parsedTranscriptRecord(line);
415
+ if (record === null) return true;
416
+ return !stampedBefore(record, oldestLineMs) && !(dropCompactionSummaries && isCompactionSummary(record));
417
+ });
418
+
166
419
  // src/transcript-sync.ts
420
+ import { join as join3, resolve } from "path";
421
+ import { closeSync as closeSync2, existsSync as existsSync2, fstatSync as fstatSync2, openSync as openSync2, readFileSync as readFileSync3, readSync as readSync2, statSync as statSync2 } from "fs";
167
422
  var claudeTranscriptPath = (cwd, sessionId) => {
168
423
  const projectId = resolve(cwd).replace(/[^a-zA-Z0-9-]/g, "-");
169
424
  const configDir = claudeConfigDir();
170
- return join2(configDir, "projects", projectId, `${sessionId}.jsonl`);
425
+ return join3(configDir, "projects", projectId, `${sessionId}.jsonl`);
171
426
  };
172
427
  var createSyncState = () => ({
173
428
  sessionKey: null,
@@ -179,7 +434,8 @@ var createSyncState = () => ({
179
434
  keyUploaded: false,
180
435
  keyRejected: false,
181
436
  keyUnavailable: false,
182
- unsettled: 0
437
+ unsettled: 0,
438
+ retentionDays: null
183
439
  });
184
440
  var withSessionKey = async (state, uploader) => {
185
441
  if (state.sessionKey || state.keyRejected) return state;
@@ -192,7 +448,8 @@ var withSessionKey = async (state, uploader) => {
192
448
  wrappedKey: wrapSessionKey(sessionKey, decodePublicKey(recipients.phonePublicKey)),
193
449
  providerWrappedKey: recipients.provider ? wrapSessionKey(sessionKey, decodePublicKey(recipients.provider.publicKey)) : state.providerWrappedKey,
194
450
  providerKeyId: recipients.provider ? recipients.provider.keyId : state.providerKeyId,
195
- compressRecords: recipients.phoneReadsCompressedRecords
451
+ compressRecords: recipients.phoneReadsCompressedRecords,
452
+ retentionDays: recipients.retentionDays
196
453
  };
197
454
  };
198
455
  var syncTick = async (lines, state, uploader, sessionId, machineId) => {
@@ -245,7 +502,8 @@ var syncTick = async (lines, state, uploader, sessionId, machineId) => {
245
502
  keyUploaded,
246
503
  keyRejected,
247
504
  keyUnavailable,
248
- unsettled: built.records.filter((record) => !seen.has(record.localId)).length
505
+ unsettled: built.records.filter((record) => !seen.has(record.localId)).length,
506
+ retentionDays: keyed.retentionDays
249
507
  };
250
508
  };
251
509
  var MAX_RECORDS_PER_BATCH = 200;
@@ -269,12 +527,15 @@ var chunkRecords = (records) => {
269
527
  };
270
528
  var readLines = (path) => {
271
529
  try {
272
- return existsSync2(path) ? readFileSync2(path, "utf-8").split("\n") : [];
530
+ return existsSync2(path) ? readFileSync3(path, "utf-8").split("\n") : [];
273
531
  } catch {
274
532
  return [];
275
533
  }
276
534
  };
277
535
  var IDLE_CEILING_MS = 6e4;
536
+ var readPositionsKeptInMemory = { load: () => /* @__PURE__ */ new Map(), save: () => {
537
+ } };
538
+ var startTranscriptCaptureEpoch = (startedAtMs, positions = readPositionsKeptInMemory, backfill = null) => ({ startedAtMs, cutoffs: new Map(positions.load()), positions, backfill });
278
539
  var recordedBirthtime = (info) => process.platform === "darwin" && info.birthtimeMs > 0 ? info.birthtimeMs : null;
279
540
  var sightingOf = (info, seenAtMs) => info ? { identity: `${info.dev}:${info.ino}`, sizeBytes: info.size, birthtimeMs: recordedBirthtime(info), seenAtMs } : { identity: null, sizeBytes: 0, birthtimeMs: null, seenAtMs };
280
541
  var storeCutoff = (epoch, path, cutoff) => {
@@ -283,7 +544,7 @@ var storeCutoff = (epoch, path, cutoff) => {
283
544
  };
284
545
  var recordSighting = (epoch, path, sighting) => {
285
546
  const previous = epoch.cutoffs.get(path);
286
- const cutoff = nextTranscriptCutoff(previous, sighting, epoch.startedAtMs);
547
+ const cutoff = nextTranscriptCutoff(previous, sighting, epoch.startedAtMs, epoch.backfill);
287
548
  if (cutoff !== previous) storeCutoff(epoch, path, cutoff);
288
549
  return cutoff;
289
550
  };
@@ -315,7 +576,7 @@ var readThroughALineEnd = (descriptor, offset, unread) => {
315
576
  let window = Math.min(unread, MAXIMUM_TRANSCRIPT_READ_BYTES);
316
577
  for (; ; ) {
317
578
  const buffer = Buffer.alloc(window);
318
- const received = buffer.subarray(0, readSync(descriptor, buffer, 0, window, offset));
579
+ const received = buffer.subarray(0, readSync2(descriptor, buffer, 0, window, offset));
319
580
  if (window >= unread || received.length < window || received.includes(LINE_FEED)) return received;
320
581
  window = Math.min(unread, window * 2);
321
582
  }
@@ -323,12 +584,12 @@ var readThroughALineEnd = (descriptor, offset, unread) => {
323
584
  var readLinesSinceCutoff = (epoch, path, seenAtMs) => {
324
585
  let descriptor;
325
586
  try {
326
- descriptor = openSync(path, "r");
587
+ descriptor = openSync2(path, "r");
327
588
  } catch {
328
589
  return nothingUnread(sightTranscript(epoch, path, seenAtMs));
329
590
  }
330
591
  try {
331
- const info = fstatSync(descriptor);
592
+ const info = fstatSync2(descriptor);
332
593
  const cutoff = recordSighting(epoch, path, sightingOf(info, seenAtMs));
333
594
  const unread = info.size - cutoff.offset;
334
595
  if (unread <= 0) return nothingUnread(cutoff);
@@ -342,12 +603,16 @@ var readLinesSinceCutoff = (epoch, path, seenAtMs) => {
342
603
  reachedEnd
343
604
  };
344
605
  } finally {
345
- closeSync(descriptor);
606
+ closeSync2(descriptor);
346
607
  }
347
608
  };
348
- var linesSeenDuringEpoch = (epoch, path, source, seenAtMs) => {
609
+ var linesSeenDuringEpoch = (epoch, path, source, seenAtMs, retentionDays) => {
349
610
  const read2 = readLinesSinceCutoff(epoch, path, seenAtMs);
350
- return { ...read2, lines: transcriptLinesSeenLive(normalizeTranscriptLines(read2.lines, source), epoch.startedAtMs, read2.cutoff.startedBeforeEpoch) };
611
+ const oldestLineMs = oldestTranscriptLineMs(epoch.startedAtMs, epoch.backfill, retentionDays, seenAtMs);
612
+ return {
613
+ ...read2,
614
+ lines: transcriptLinesSince(normalizeTranscriptLines(read2.lines, source), oldestLineMs, dropsCompactionSummaries(read2.cutoff, epoch.backfill))
615
+ };
351
616
  };
352
617
  var nothingLeftToUpload = (state) => state.sessionKey !== null && !state.keyRejected && state.unsettled === 0;
353
618
  var uploadStalled = (before, after) => after.sessionKey !== null && !after.keyRejected && after.unsettled > 0 && after.seen.size === before.seen.size;
@@ -412,7 +677,8 @@ var startTranscriptSync = (opts) => {
412
677
  const previous = state;
413
678
  const modified = existsSync2(path) ? statSync2(path).mtimeMs : null;
414
679
  const source = opts.source ?? "claude";
415
- const read2 = opts.epoch ? linesSeenDuringEpoch(opts.epoch, path, source, Date.now()) : null;
680
+ const retentionDays = state.retentionDays ?? SESSION_HISTORY_RETENTION_DAYS.standard;
681
+ const read2 = opts.epoch ? linesSeenDuringEpoch(opts.epoch, path, source, Date.now(), retentionDays) : null;
416
682
  const lines = read2?.lines ?? normalizeTranscriptLines(readLines(path), source);
417
683
  state = await syncTick(lines, state, uploader, opts.sessionId, opts.machineId);
418
684
  stalled = uploadStalled(previous, state);
@@ -443,8 +709,16 @@ var startTranscriptSync = (opts) => {
443
709
  }, delayMs);
444
710
  if (typeof timer.unref === "function") timer.unref();
445
711
  };
712
+ const stopDisplay = (opts.source ?? "claude") === "claude" ? startDisplayPublisher({
713
+ apiKey: opts.apiKey,
714
+ sessionId: opts.sessionId,
715
+ baseUrl: opts.baseUrl ?? getBaseUrl(),
716
+ directory: opts.storage?.sessionKeyDirectory,
717
+ enabled: () => running && !state.keyRejected && (opts.enabled ? opts.enabled() : transcriptsEnabled())
718
+ }) : void 0;
446
719
  schedule(0);
447
720
  return () => {
721
+ stopDisplay?.();
448
722
  running = false;
449
723
  if (timer) clearTimeout(timer);
450
724
  };
@@ -452,30 +726,30 @@ var startTranscriptSync = (opts) => {
452
726
 
453
727
  // src/transcript-registration.ts
454
728
  import { homedir as homedir2 } from "os";
455
- var defaultRegistryDirectory = () => process.env.PUSHARY_TRANSCRIPT_REGISTRY_DIR || join3(pusharyDir(), "transcript-capture");
729
+ var defaultRegistryDirectory = () => process.env.PUSHARY_TRANSCRIPT_REGISTRY_DIR || join4(pusharyDir(), "transcript-capture");
456
730
  var cliRegistry = (apiKey = getApiKey()) => ({ apiKey, baseUrl: getBaseUrl(), directory: defaultRegistryDirectory() });
457
731
  var accountFor = (registry) => createHash("sha256").update(`${registry.baseUrl}\0${registry.apiKey}`).digest("hex");
458
732
  var TTL = 48 * 60 * 60 * 1e3;
459
- var read = (path) => JSON.parse(readFileSync3(path, "utf8"));
460
- var consent = (record) => record.consentOverride ?? readTranscriptPreference() ?? false;
733
+ var read = (path) => JSON.parse(readFileSync4(path, "utf8"));
734
+ var consent = (record) => record.consentOverride ?? readTranscriptPreference() ?? true;
461
735
  var withoutTrailingSlash = (url) => url.replace(/\/+$/, "");
462
736
  var sameServer = (url, registry) => withoutTrailingSlash(url) === withoutTrailingSlash(registry.baseUrl);
463
- var fileFor2 = (registry, sessionId) => join3(registry.directory, `${createHash("sha256").update(accountFor(registry) + sessionId).digest("hex")}.json`);
737
+ var fileFor3 = (registry, sessionId) => join4(registry.directory, `${createHash("sha256").update(accountFor(registry) + sessionId).digest("hex")}.json`);
464
738
  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));
465
739
  var registeredTranscriptEnabled = (apiKey, sessionId, registry = cliRegistry(apiKey)) => {
466
740
  try {
467
- return permitted(registry, read(fileFor2(registry, sessionId)));
741
+ return permitted(registry, read(fileFor3(registry, sessionId)));
468
742
  } catch {
469
743
  return false;
470
744
  }
471
745
  };
472
746
  var macOwnsClaudeHooks = (cwd) => [
473
- join3(claudeConfigDir(), "settings.json"),
474
- join3(cwd, ".claude", "settings.json"),
475
- join3(cwd, ".claude", "settings.local.json")
747
+ join4(claudeConfigDir(), "settings.json"),
748
+ join4(cwd, ".claude", "settings.json"),
749
+ join4(cwd, ".claude", "settings.local.json")
476
750
  ].some((path) => {
477
751
  try {
478
- return hookOwnerOf(JSON.parse(readFileSync3(path, "utf8"))) === "app";
752
+ return hookOwnerOf(JSON.parse(readFileSync4(path, "utf8"))) === "app";
479
753
  } catch {
480
754
  return false;
481
755
  }
@@ -483,9 +757,9 @@ var macOwnsClaudeHooks = (cwd) => [
483
757
  var TRANSCRIPT_ROOT_NAME = { claude: "projects", codex: "sessions" };
484
758
  var transcriptRoots = (source) => {
485
759
  const home = homedir2();
486
- if (source === "codex") return [join3(codexHome(), TRANSCRIPT_ROOT_NAME.codex)];
760
+ if (source === "codex") return [join4(codexHome(), TRANSCRIPT_ROOT_NAME.codex)];
487
761
  const configured = process.env.CLAUDE_CONFIG_DIR;
488
- return [join3(configured || join3(home, ".claude"), TRANSCRIPT_ROOT_NAME.claude), join3(home, ".claude", TRANSCRIPT_ROOT_NAME.claude)];
762
+ return [join4(configured || join4(home, ".claude"), TRANSCRIPT_ROOT_NAME.claude), join4(home, ".claude", TRANSCRIPT_ROOT_NAME.claude)];
489
763
  };
490
764
  var isAgentTranscriptRoot = (root, source) => isAbsolute(root) && resolve2(root) === root && basename(root) === TRANSCRIPT_ROOT_NAME[source];
491
765
  var acceptedTranscriptRoots = (source, registeredRoot) => typeof registeredRoot === "string" && isAgentTranscriptRoot(registeredRoot, source) ? [...transcriptRoots(source), registeredRoot] : transcriptRoots(source);
@@ -513,7 +787,7 @@ var registerTranscript = (source, input, startDaemon = () => {
513
787
  try {
514
788
  const registry = options.registry ?? cliRegistry();
515
789
  const account = accountFor(registry);
516
- const path = fileFor2(registry, registration.sessionId);
790
+ const path = fileFor3(registry, registration.sessionId);
517
791
  let previous;
518
792
  try {
519
793
  previous = read(path);
@@ -549,7 +823,8 @@ var sightRegisteredTranscripts = (epoch, registry, listing) => {
549
823
  if (listing.complete) forgetUnregisteredTranscripts(epoch, registeredPaths);
550
824
  };
551
825
  var startRegisteredTranscripts = (apiKey, machineId, options = {}) => {
552
- const { intervalMs = 3e3, enabled = () => true, storage, epoch } = options;
826
+ const { intervalMs = 3e3, enabled = () => true, storage } = options;
827
+ const epoch = options.epoch ?? startTranscriptCaptureEpoch(Date.now(), void 0, SESSION_HISTORY_BACKFILL);
553
828
  const registry = { apiKey, baseUrl: options.baseUrl ?? getBaseUrl(), directory: options.registryDirectory ?? defaultRegistryDirectory() };
554
829
  const active = /* @__PURE__ */ new Map();
555
830
  let stopped = false;
@@ -563,11 +838,11 @@ var startRegisteredTranscripts = (apiKey, machineId, options = {}) => {
563
838
  try {
564
839
  let unreadable = 0;
565
840
  const files = readdirSync2(registry.directory).filter((name) => name.endsWith(".json")).flatMap((name) => {
566
- const path = join3(registry.directory, name);
841
+ const path = join4(registry.directory, name);
567
842
  try {
568
843
  const time = statSync3(path).mtimeMs;
569
844
  if (Date.now() - time > TTL) {
570
- unlinkSync(path);
845
+ unlinkSync2(path);
571
846
  return [];
572
847
  }
573
848
  return [{ path, time, raw: read(path) }];
@@ -657,9 +932,14 @@ var startRegisteredTranscripts = (apiKey, machineId, options = {}) => {
657
932
  };
658
933
 
659
934
  export {
935
+ redactDraft,
936
+ captureDisplayDraft,
937
+ sessionKeyDirectory,
660
938
  loadSessionKey,
939
+ startDraftPublisher,
661
940
  claudeTranscriptPath,
662
941
  registeredTranscriptEnabled,
942
+ captureRegistration,
663
943
  registerTranscript,
664
944
  startRegisteredTranscripts
665
945
  };