@pushary/agent-hooks 1.9.72 → 1.9.75

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 +19 -0
  2. package/dist/{api-K4UGZNOE.js → api-L3BIISGS.js} +10 -10
  3. package/dist/bin/pushary-bell-hook.js +26 -25
  4. package/dist/bin/pushary-bell.js +14 -14
  5. package/dist/bin/pushary-claude.js +54 -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 +35 -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 +1292 -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 +57 -0
  17. package/dist/bin/pushary-doctor.js +50 -50
  18. package/dist/bin/pushary-elicitation-hook.js +28 -27
  19. package/dist/bin/pushary-gemini-bridge.js +16 -16
  20. package/dist/bin/pushary-gemini-hook.js +30 -29
  21. package/dist/bin/pushary-hook.js +30 -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 +25 -24
  27. package/dist/bin/pushary-opencode-hook.js +29 -28
  28. package/dist/bin/pushary-permission-denied-hook.js +30 -29
  29. package/dist/bin/pushary-permission-hook.js +30 -29
  30. package/dist/bin/pushary-post-hook.js +31 -29
  31. package/dist/bin/pushary-prompt-hook.js +31 -29
  32. package/dist/bin/pushary-session-end-hook.js +27 -26
  33. package/dist/bin/pushary-session-start-hook.js +35 -34
  34. package/dist/bin/pushary-setup.js +61 -60
  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 +31 -26
  38. package/dist/bin/pushary-stopfailure-hook.js +25 -24
  39. package/dist/bin/pushary-suggestions.js +18 -18
  40. package/dist/bin/pushary-transcript-register.js +17 -16
  41. package/dist/bin/pushary-transcripts.js +19 -19
  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-QFF3QVIV.js → chunk-2DB7TBA7.js} +2 -2
  46. package/dist/{chunk-N4NSX5MB.js → chunk-2J4IVUEA.js} +3 -2
  47. package/dist/{chunk-E56VJY23.js → chunk-32JJ5KV3.js} +4 -4
  48. package/dist/{chunk-VFF7TP5B.js → chunk-3PPL64AG.js} +3 -3
  49. package/dist/{chunk-KFGRDWAW.js → chunk-3SAF4QXU.js} +4 -4
  50. package/dist/{chunk-TM4JH7YR.js → chunk-4DH54LS7.js} +1 -1
  51. package/dist/{chunk-RXR44U2J.js → chunk-4GWKKUQ5.js} +5 -5
  52. package/dist/{chunk-4U7F7VMC.js → chunk-4T25BBBO.js} +4 -4
  53. package/dist/{chunk-BMVWGPYB.js → chunk-53D6FFVE.js} +1 -1
  54. package/dist/{chunk-EW7CZHYP.js → chunk-5EQOUZYE.js} +2 -2
  55. package/dist/{chunk-PIO66HPG.js → chunk-6BFLDHPM.js} +3 -3
  56. package/dist/{chunk-D6COE26P.js → chunk-7FNSQMQ2.js} +4 -4
  57. package/dist/{chunk-R4B5AVFC.js → chunk-7O54GE5C.js} +3 -3
  58. package/dist/{chunk-GUJBS6TK.js → chunk-AUDYAVNM.js} +3 -3
  59. package/dist/{chunk-5XYYBB5Z.js → chunk-BGEUAJYL.js} +2 -2
  60. package/dist/{chunk-2KDTIUC7.js → chunk-BRG4XS2H.js} +1 -1
  61. package/dist/{chunk-EPARIXRC.js → chunk-CI6MDBQB.js} +7 -7
  62. package/dist/{chunk-LKXOSW2L.js → chunk-CLHRSK5L.js} +4 -4
  63. package/dist/{chunk-5KQN7AIL.js → chunk-D4X4BQVQ.js} +10 -10
  64. package/dist/{chunk-ZWCKS65X.js → chunk-DARPCQPY.js} +2 -2
  65. package/dist/{chunk-QDISUNK3.js → chunk-DCHZ5JOI.js} +6 -6
  66. package/dist/{chunk-N7A44RIA.js → chunk-DOG3BQFO.js} +6 -6
  67. package/dist/{chunk-ILWEGI4H.js → chunk-DUOBFECC.js} +1 -1
  68. package/dist/{chunk-LT2DOCFI.js → chunk-DX65IDBI.js} +1 -1
  69. package/dist/{chunk-37OD7FUI.js → chunk-E5XYFIYL.js} +6 -6
  70. package/dist/{chunk-HIHA3GOR.js → chunk-EH5OLIP2.js} +2 -2
  71. package/dist/{chunk-3S5X7VZJ.js → chunk-EM2RSWDA.js} +5 -5
  72. package/dist/{chunk-DU7L7R4A.js → chunk-EOGVWCLF.js} +5 -5
  73. package/dist/{chunk-UTVNXB6U.js → chunk-EVBG5YUO.js} +3 -3
  74. package/dist/{chunk-YDSOTAZ6.js → chunk-FEC3JUIH.js} +3 -3
  75. package/dist/{chunk-KYEZFN6K.js → chunk-GAAN3Y7V.js} +1 -1
  76. package/dist/{chunk-LISVMDVS.js → chunk-HRRGIVIL.js} +3 -3
  77. package/dist/{chunk-RRW2LVVR.js → chunk-HXV2SY2V.js} +2 -2
  78. package/dist/{chunk-2BVLEMGX.js → chunk-I6TIZMM3.js} +5 -5
  79. package/dist/{chunk-4Y7EI5W5.js → chunk-I73UQU7K.js} +1 -1
  80. package/dist/{chunk-QQ7VXRIN.js → chunk-JCZWWBBA.js} +5 -5
  81. package/dist/{chunk-EEFFP64A.js → chunk-JFVLMWKY.js} +241 -52
  82. package/dist/{chunk-5KS3E3T5.js → chunk-JOMG6J64.js} +1 -1
  83. package/dist/{chunk-EEQG7VF2.js → chunk-KDUWFGY2.js} +4 -4
  84. package/dist/{chunk-6YZ72YP3.js → chunk-LIDUPDEY.js} +4 -4
  85. package/dist/{chunk-DNCEZWRC.js → chunk-LRYZXY4V.js} +1 -1
  86. package/dist/{chunk-L6F62LKX.js → chunk-M7BXAWUC.js} +3 -3
  87. package/dist/{chunk-CR3HOHJC.js → chunk-MD4UJICD.js} +6 -5
  88. package/dist/{chunk-SMVTNLXU.js → chunk-NCTABQMC.js} +4 -3
  89. package/dist/{chunk-GNDPZBG3.js → chunk-P4TWTQ6V.js} +8 -8
  90. package/dist/{chunk-6HTAOF6H.js → chunk-PGLNE556.js} +1 -1
  91. package/dist/{chunk-PZV27E4J.js → chunk-PV5XEBFI.js} +9 -9
  92. package/dist/{chunk-S6BD346V.js → chunk-QDUDXJAF.js} +3 -3
  93. package/dist/{chunk-5XKJH5GD.js → chunk-QRS7FQHE.js} +504 -175
  94. package/dist/{chunk-LFBQ55OO.js → chunk-R7V6BPEV.js} +1 -1
  95. package/dist/{chunk-VZG76CWC.js → chunk-RPKFXNOJ.js} +1 -1
  96. package/dist/{chunk-4W7CKRWR.js → chunk-SFJOGZQV.js} +3 -3
  97. package/dist/{chunk-WHM3OVBA.js → chunk-SHW6KK2K.js} +2 -2
  98. package/dist/{chunk-4TRA3GDE.js → chunk-SIJHPGUW.js} +5 -5
  99. package/dist/{chunk-XGGAZJWJ.js → chunk-SNUHX5GQ.js} +6 -6
  100. package/dist/{chunk-IBHZ3JFE.js → chunk-SOHJ37NA.js} +5 -5
  101. package/dist/{chunk-CAUNUTYE.js → chunk-SONZCJY5.js} +26 -4
  102. package/dist/{chunk-SAXNYDPS.js → chunk-T6RWREWU.js} +1 -1
  103. package/dist/{chunk-LCBZLTHW.js → chunk-TPYZFYQ5.js} +4 -4
  104. package/dist/{chunk-XHVHGDMB.js → chunk-UBANQ4EZ.js} +13 -13
  105. package/dist/{chunk-OGGUOTIK.js → chunk-UEJTWQVF.js} +1 -1
  106. package/dist/{chunk-7KMLITXH.js → chunk-UIS332AZ.js} +1 -1
  107. package/dist/{chunk-PWVQALO2.js → chunk-UU3CDLIX.js} +2 -2
  108. package/dist/{chunk-MWR25RQZ.js → chunk-VGHWK5Z2.js} +5 -3
  109. package/dist/{chunk-OFTTOPTD.js → chunk-WL3V2UV4.js} +6 -6
  110. package/dist/{chunk-NVM4S37G.js → chunk-XO7LP3JC.js} +1 -1
  111. package/dist/{chunk-L3S2ZTQZ.js → chunk-Z7ORL6SC.js} +2 -2
  112. package/dist/{chunk-RO5G3OEN.js → chunk-ZX3KEXO3.js} +343 -84
  113. package/dist/{codex-app-cards-switch-PWLWIWKG.js → codex-app-cards-switch-CN3G7GZW.js} +11 -11
  114. package/dist/{codex-desktop-launch-S3PDGLAY.js → codex-desktop-launch-GFZXCDSL.js} +1 -1
  115. package/dist/{codex-session-launch-PY5IMFJU.js → codex-session-launch-4F2UGQYE.js} +18 -18
  116. package/dist/{config-SWV4YOP3.js → config-BLHDUDCS.js} +3 -3
  117. package/dist/{global-upgrade-DI3LGOQP.js → global-upgrade-IZEC22BQ.js} +10 -10
  118. package/dist/{identity-AY35DGAO.js → identity-3HGTEP5Q.js} +4 -4
  119. package/dist/{live-capabilities-IH3EGKL3.js → live-capabilities-AZK664T7.js} +5 -5
  120. package/dist/{paths-I4Q77ULZ.js → paths-ROMZUSEX.js} +3 -3
  121. package/dist/{reapply-OAV4IAG3.js → reapply-IJYRR4YK.js} +27 -27
  122. package/dist/src/index.d.ts +2 -6
  123. package/dist/src/index.js +31 -30
  124. package/dist/{upgrade-lock-7U5BOK6V.js → upgrade-lock-FGEZPX56.js} +4 -4
  125. package/dist/{usage-NTER4GHT.js → usage-HCURE44F.js} +3 -3
  126. package/package.json +5 -2
@@ -3,112 +3,214 @@ import {
3
3
  loadPin,
4
4
  savePin,
5
5
  verifyRecipients
6
- } from "./chunk-SAXNYDPS.js";
6
+ } from "./chunk-T6RWREWU.js";
7
7
  import {
8
8
  ensureDaemonRunning
9
- } from "./chunk-QDISUNK3.js";
9
+ } from "./chunk-DCHZ5JOI.js";
10
10
  import {
11
11
  hookOwnerOf
12
- } from "./chunk-LCBZLTHW.js";
12
+ } from "./chunk-TPYZFYQ5.js";
13
13
  import {
14
14
  buildTranscriptRecords,
15
15
  decodePublicKey,
16
+ decryptRecord,
17
+ encryptRecord,
16
18
  fetchTranscriptRecipients,
17
19
  generateSessionKey,
18
20
  recordSealerFor,
19
21
  uploadTranscriptRecords,
20
22
  wrapSessionKey
21
- } from "./chunk-CAUNUTYE.js";
23
+ } from "./chunk-SONZCJY5.js";
24
+ import {
25
+ external_exports
26
+ } from "./chunk-XMSDA5WA.js";
22
27
  import {
23
28
  transcriptEnvironmentPreference,
24
29
  transcriptsEnabled
25
- } from "./chunk-VZG76CWC.js";
30
+ } from "./chunk-RPKFXNOJ.js";
26
31
  import {
27
32
  readTranscriptPreference
28
- } from "./chunk-6YZ72YP3.js";
33
+ } from "./chunk-LIDUPDEY.js";
29
34
  import {
30
35
  claudeConfigDir,
31
36
  codexHome,
32
37
  pusharyDir
33
- } from "./chunk-N4NSX5MB.js";
38
+ } from "./chunk-2J4IVUEA.js";
34
39
  import {
35
40
  writeJsonAtomic
36
- } from "./chunk-GUJBS6TK.js";
41
+ } from "./chunk-AUDYAVNM.js";
37
42
  import {
38
43
  getApiKey,
39
44
  getBaseUrl
40
- } from "./chunk-BMVWGPYB.js";
45
+ } from "./chunk-53D6FFVE.js";
46
+ import {
47
+ PENDING_COMMAND_PREFIX,
48
+ redactSecretsFromContent
49
+ } from "./chunk-QRS7FQHE.js";
41
50
 
42
51
  // src/transcript-registration.ts
43
52
  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";
53
+ import { readFileSync as readFileSync4, readdirSync as readdirSync2, statSync as statSync3, unlinkSync as unlinkSync2 } from "fs";
54
+ import { basename, isAbsolute, join as join4, resolve as resolve2, sep } from "path";
46
55
 
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
- });
56
+ // src/claude-display-draft.ts
57
+ import { closeSync, fstatSync, lstatSync, openSync, readFileSync, readSync, unlinkSync } from "fs";
58
+ import { join } from "path";
59
+
60
+ // src/transcript-redaction.ts
61
+ var redactDraft = (text, complete = false) => {
62
+ let end = complete ? text.length : text.lastIndexOf("\n") + 1;
63
+ const pem = /-----BEGIN [^\n]*PRIVATE KEY-----/g;
64
+ for (const match of text.matchAll(pem)) {
65
+ if (!/-----END [^\n]*PRIVATE KEY-----/.test(text.slice(match.index, end))) end = Math.min(end, match.index);
66
+ }
67
+ const quoted = /(?:secret|token|password|passwd|api[_-]?key|access[_-]?key|client[_-]?secret|private[_-]?key)\s*[=:]\s*(["'])/gi;
68
+ for (const match of text.matchAll(quoted)) {
69
+ const close = text.indexOf(match[1], match.index + match[0].length);
70
+ if (close < 0 || close >= end) end = Math.min(end, match.index);
71
+ }
72
+ return redactSecretsFromContent(text.slice(0, end));
61
73
  };
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;
74
+
75
+ // src/claude-display-draft.ts
76
+ var MAX_TEXT = 32 * 1024;
77
+ var MAX_TAIL = 256 * 1024;
78
+ var MAX_AGE_MS = 9e4;
79
+ var identifier = external_exports.string().regex(/^[\w-]{1,128}$/);
80
+ var displaySchema = external_exports.object({
81
+ hook_event_name: external_exports.literal("MessageDisplay"),
82
+ session_id: identifier,
83
+ message_id: identifier,
84
+ index: external_exports.number().int().nonnegative(),
85
+ final: external_exports.boolean(),
86
+ delta: external_exports.string().max(MAX_TEXT),
87
+ transcript_path: external_exports.string().min(1),
88
+ agent_id: external_exports.undefined()
89
+ });
90
+ var containsPhoneInstruction = (content) => content.some((text) => {
91
+ if (typeof text !== "string") return false;
92
+ const prefixAt = text.indexOf(PENDING_COMMAND_PREFIX);
93
+ return prefixAt >= 0 && text.slice(prefixAt + PENDING_COMMAND_PREFIX.length).trim().length > 0;
94
+ });
95
+ var anchorSchema = external_exports.object({ uuid: identifier, isSidechain: external_exports.literal(false).optional() }).and(external_exports.union([
96
+ external_exports.object({ type: external_exports.enum(["user", "assistant"]) }),
97
+ external_exports.object({ type: external_exports.literal("attachment"), attachment: external_exports.object({
98
+ type: external_exports.literal("hook_additional_context"),
99
+ content: external_exports.array(external_exports.unknown()).refine(containsPhoneInstruction)
100
+ }) })
101
+ ]));
102
+ var stateSchema = external_exports.object({
103
+ displayId: identifier,
104
+ afterRecordId: identifier,
105
+ index: external_exports.number().int().nonnegative(),
106
+ text: external_exports.string().max(MAX_TEXT),
107
+ complete: external_exports.boolean(),
108
+ startedAt: external_exports.number().int().positive(),
109
+ updatedAt: external_exports.number().int().positive()
110
+ });
111
+ var fileFor = (id, directory) => join(directory, `${id}.display`);
112
+ var readState = (sessionId, runtime) => {
113
+ if (!identifier.safeParse(sessionId).success) return null;
114
+ try {
115
+ const path = fileFor(sessionId, runtime.directory);
116
+ const info = lstatSync(path);
117
+ if (!info.isFile() || info.nlink !== 1 || info.size > MAX_TAIL || (info.mode & 511) !== 384) return null;
118
+ const envelope = external_exports.object({ content: external_exports.string() }).parse(JSON.parse(readFileSync(path, "utf8")));
119
+ const plain = decryptRecord(envelope.content, runtime.sessionKey);
120
+ if (!plain) return null;
121
+ const state = stateSchema.parse(JSON.parse(plain));
122
+ return runtime.now - state.updatedAt <= MAX_AGE_MS && runtime.now >= state.updatedAt ? state : null;
123
+ } catch {
124
+ return null;
125
+ }
71
126
  };
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) => {
127
+ var conversationAnchor = (path) => {
128
+ let descriptor;
75
129
  try {
76
- const value = JSON.parse(line);
77
- return isTranscriptObject(value) ? value : null;
130
+ descriptor = openSync(path, "r");
131
+ const size = fstatSync(descriptor).size;
132
+ const start = Math.max(0, size - MAX_TAIL);
133
+ const buffer = Buffer.alloc(Math.min(size, MAX_TAIL));
134
+ const length = readSync(descriptor, buffer, 0, buffer.length, start);
135
+ const lines = buffer.subarray(0, length).toString("utf8").split("\n");
136
+ if (start > 0) lines.shift();
137
+ lines.pop();
138
+ for (const line of lines.reverse()) {
139
+ try {
140
+ const value = anchorSchema.safeParse(JSON.parse(line));
141
+ if (value.success) return value.data.uuid;
142
+ } catch {
143
+ continue;
144
+ }
145
+ }
78
146
  } catch {
79
147
  return null;
148
+ } finally {
149
+ if (descriptor !== void 0) closeSync(descriptor);
80
150
  }
151
+ return null;
81
152
  };
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;
153
+ var captureDisplayDraft = (input, runtime) => {
154
+ const parsed = displaySchema.safeParse(input);
155
+ if (!parsed.success) return false;
156
+ const event = parsed.data;
157
+ const previous = readState(event.session_id, runtime);
158
+ if (previous?.displayId === event.message_id && event.index <= previous.index) return false;
159
+ if (event.index !== 0 && (previous?.displayId !== event.message_id || previous.complete)) return false;
160
+ if (event.index !== 0 && previous && event.index !== previous.index + 1) {
161
+ try {
162
+ unlinkSync(fileFor(event.session_id, runtime.directory));
163
+ } catch {
164
+ }
165
+ return false;
166
+ }
167
+ if (event.index === 0 && event.final) return false;
168
+ const anchor = event.index === 0 ? conversationAnchor(event.transcript_path) : previous?.afterRecordId;
169
+ if (!anchor) return false;
170
+ const text = (event.index === 0 ? "" : previous?.text ?? "") + event.delta;
171
+ if (Buffer.byteLength(text, "utf8") > MAX_TEXT) return false;
172
+ const state = {
173
+ displayId: event.message_id,
174
+ afterRecordId: anchor,
175
+ index: event.index,
176
+ text,
177
+ complete: event.final,
178
+ startedAt: event.index === 0 ? runtime.now : previous?.startedAt ?? runtime.now,
179
+ updatedAt: runtime.now
180
+ };
181
+ const target = fileFor(event.session_id, runtime.directory);
182
+ const existing = lstatSync(target, { throwIfNoEntry: false });
183
+ if (existing && (!existing.isFile() || existing.nlink !== 1 || (existing.mode & 511) !== 384)) return false;
184
+ writeJsonAtomic(target, { content: encryptRecord(JSON.stringify(state), runtime.sessionKey) }, 384);
185
+ return true;
186
+ };
187
+ var readDisplayDraft = (sessionId, runtime) => {
188
+ const state = readState(sessionId, runtime);
189
+ if (!state) return null;
190
+ const text = redactDraft(state.text, state.complete);
191
+ if (!text) return null;
192
+ return { startedAt: state.startedAt, revision: state.index + 1, preview: {
193
+ displayId: state.displayId,
194
+ afterRecordId: state.afterRecordId,
195
+ text,
196
+ status: state.complete ? "complete" : "streaming"
197
+ } };
86
198
  };
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
199
 
98
200
  // src/transcript-session-key.ts
99
- import { existsSync, mkdirSync, readFileSync, readdirSync, rmSync, statSync, writeFileSync } from "fs";
201
+ import { existsSync, mkdirSync, readFileSync as readFileSync2, readdirSync, rmSync, statSync, writeFileSync } from "fs";
100
202
  import { homedir } from "os";
101
- import { join } from "path";
102
- var sessionDir = () => process.env.PUSHARY_SESSION_KEY_DIR?.trim() || join(homedir(), ".pushary", "sessions");
203
+ import { join as join2 } from "path";
204
+ var sessionKeyDirectory = () => process.env.PUSHARY_SESSION_KEY_DIR?.trim() || join2(homedir(), ".pushary", "sessions");
103
205
  var SESSION_KEY_TTL_MS = 48 * 60 * 60 * 1e3;
104
206
  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);
207
+ var fileFor2 = (sessionId, directory) => SAFE_SESSION_ID.test(sessionId) ? join2(directory, `${sessionId}.json`) : null;
208
+ var loadSessionKey = (sessionId, directory = sessionKeyDirectory()) => {
209
+ const path = fileFor2(sessionId, directory);
108
210
  if (!path || !existsSync(path)) return null;
109
211
  try {
110
212
  if (Date.now() - statSync(path).mtimeMs > SESSION_KEY_TTL_MS) return null;
111
- const raw = JSON.parse(readFileSync(path, "utf-8"));
213
+ const raw = JSON.parse(readFileSync2(path, "utf-8"));
112
214
  const key = typeof raw.sessionKey === "string" ? Buffer.from(raw.sessionKey, "base64") : null;
113
215
  if (!key || key.length !== 32) return null;
114
216
  return {
@@ -123,8 +225,8 @@ var loadSessionKey = (sessionId, directory = sessionDir()) => {
123
225
  return null;
124
226
  }
125
227
  };
126
- var saveSessionKey = (sessionId, value, directory = sessionDir()) => {
127
- const path = fileFor(sessionId, directory);
228
+ var saveSessionKey = (sessionId, value, directory = sessionKeyDirectory()) => {
229
+ const path = fileFor2(sessionId, directory);
128
230
  if (!path) return;
129
231
  try {
130
232
  mkdirSync(directory, { recursive: true, mode: 448 });
@@ -143,13 +245,13 @@ var saveSessionKey = (sessionId, value, directory = sessionDir()) => {
143
245
  } catch {
144
246
  }
145
247
  };
146
- var sweepStaleSessionKeys = (now = Date.now(), dir = sessionDir()) => {
248
+ var sweepStaleSessionKeys = (now = Date.now(), dir = sessionKeyDirectory()) => {
147
249
  if (!existsSync(dir)) return 0;
148
250
  let removed = 0;
149
251
  try {
150
252
  for (const name of readdirSync(dir)) {
151
- if (!name.endsWith(".json")) continue;
152
- const path = join(dir, name);
253
+ if (!name.endsWith(".json") && !name.endsWith(".display")) continue;
254
+ const path = join2(dir, name);
153
255
  try {
154
256
  if (now - statSync(path).mtimeMs <= SESSION_KEY_TTL_MS) continue;
155
257
  rmSync(path, { force: true });
@@ -163,11 +265,155 @@ var sweepStaleSessionKeys = (now = Date.now(), dir = sessionDir()) => {
163
265
  return removed;
164
266
  };
165
267
 
268
+ // src/transcript-draft-publisher.ts
269
+ var startDraftPublisher = (options) => {
270
+ let running = true;
271
+ let pending;
272
+ let active;
273
+ let sent = "";
274
+ let retryAt = 0;
275
+ let retryMs = 1e3;
276
+ let reportedFailure = false;
277
+ const publish = async () => {
278
+ const controller = new AbortController();
279
+ active = controller;
280
+ try {
281
+ const sessionId = options.sessionId();
282
+ if (!sessionId) return;
283
+ const key = loadSessionKey(sessionId, options.keyDirectory);
284
+ if (!key?.keyUploaded) return;
285
+ const draft = options.snapshot(key.sessionKey);
286
+ if (!draft) return;
287
+ const identity = JSON.stringify([sessionId, draft.startedAt, draft.revision]);
288
+ if (identity === sent) return;
289
+ const response = await fetch(`${options.baseUrl}/api/agent/transcript`, {
290
+ method: "POST",
291
+ headers: { Authorization: `Bearer ${options.apiKey}`, "Content-Type": "application/json" },
292
+ body: JSON.stringify({ sessionId, draft: {
293
+ startedAt: draft.startedAt,
294
+ revision: draft.revision,
295
+ content: encryptRecord(JSON.stringify(draft.preview), key.sessionKey)
296
+ } }),
297
+ signal: AbortSignal.any([controller.signal, AbortSignal.timeout(5e3)])
298
+ });
299
+ if (!response.ok) throw new Error(`HTTP ${response.status}`);
300
+ sent = identity;
301
+ retryAt = 0;
302
+ retryMs = 1e3;
303
+ reportedFailure = false;
304
+ } catch (error) {
305
+ retryAt = Date.now() + retryMs;
306
+ retryMs = Math.min(3e4, retryMs * 2);
307
+ if (running && !controller.signal.aborted && !reportedFailure) {
308
+ reportedFailure = true;
309
+ process.stderr.write(`[pushary] live transcript preview could not sync (${error instanceof Error ? error.message : "request failed"}); saved history remains available.
310
+ `);
311
+ }
312
+ } finally {
313
+ active = void 0;
314
+ }
315
+ };
316
+ const flush = async (final = false) => {
317
+ if (!running) return;
318
+ if (!options.enabled()) {
319
+ active?.abort();
320
+ return;
321
+ }
322
+ if (pending || !final && Date.now() < retryAt) return;
323
+ pending = publish();
324
+ try {
325
+ await pending;
326
+ } finally {
327
+ pending = void 0;
328
+ }
329
+ };
330
+ const timer = setInterval(() => {
331
+ void flush();
332
+ }, 500);
333
+ timer.unref();
334
+ const stop = () => {
335
+ running = false;
336
+ clearInterval(timer);
337
+ active?.abort();
338
+ };
339
+ return {
340
+ stop,
341
+ finish: async () => {
342
+ clearInterval(timer);
343
+ await pending;
344
+ await flush(true);
345
+ stop();
346
+ }
347
+ };
348
+ };
349
+
350
+ // src/claude-display-publisher.ts
351
+ var startDisplayPublisher = (options) => {
352
+ const directory = options.directory ?? sessionKeyDirectory();
353
+ return startDraftPublisher({
354
+ apiKey: options.apiKey,
355
+ baseUrl: options.baseUrl,
356
+ keyDirectory: directory,
357
+ sessionId: () => options.sessionId,
358
+ enabled: options.enabled,
359
+ snapshot: (sessionKey) => readDisplayDraft(options.sessionId, { directory, sessionKey, now: Date.now() })
360
+ }).stop;
361
+ };
362
+
363
+ // src/transcript-source.ts
364
+ var normalizeTranscriptLines = (lines, source) => {
365
+ if (source === "claude") return [...lines];
366
+ return lines.flatMap((line) => {
367
+ try {
368
+ const record = JSON.parse(line);
369
+ const item = record.payload;
370
+ if (record.type !== "response_item" || item?.type !== "message" || !["user", "assistant"].includes(item.role)) return [];
371
+ 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 }] : []);
372
+ return content.length ? [JSON.stringify({ timestamp: record.timestamp, message: { role: item.role, content } })] : [];
373
+ } catch {
374
+ return [];
375
+ }
376
+ });
377
+ };
378
+ var createdDuringEpoch = (sighting, epochStartedAtMs) => sighting.birthtimeMs !== null && sighting.birthtimeMs >= epochStartedAtMs && sighting.birthtimeMs <= sighting.seenAtMs;
379
+ var everythingAlreadyWritten = (sighting) => ({ identity: sighting.identity, offset: sighting.sizeBytes, startedBeforeEpoch: true });
380
+ var cutoffAtFirstSight = (sighting, epochStartedAtMs) => sighting.identity === null || createdDuringEpoch(sighting, epochStartedAtMs) ? { identity: sighting.identity, offset: 0, startedBeforeEpoch: false } : everythingAlreadyWritten(sighting);
381
+ var replacedOrShrunk = (previous, sighting) => previous.identity !== sighting.identity || sighting.sizeBytes < previous.offset;
382
+ var nextTranscriptCutoff = (previous, sighting, epochStartedAtMs) => {
383
+ if (!previous) return cutoffAtFirstSight(sighting, epochStartedAtMs);
384
+ if (sighting.identity === null) return previous;
385
+ if (previous.identity === null) return { ...previous, identity: sighting.identity };
386
+ return replacedOrShrunk(previous, sighting) ? everythingAlreadyWritten(sighting) : previous;
387
+ };
388
+ var advancedTranscriptCutoff = (current, readFrom, settledThroughOffset) => current !== void 0 && current.identity === readFrom.identity && settledThroughOffset > current.offset ? { ...current, offset: settledThroughOffset } : current;
389
+ var isTranscriptObject = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
390
+ var parsedTranscriptRecord = (line) => {
391
+ try {
392
+ const value = JSON.parse(line);
393
+ return isTranscriptObject(value) ? value : null;
394
+ } catch {
395
+ return null;
396
+ }
397
+ };
398
+ var stampedBefore = (record, momentMs) => {
399
+ if (typeof record.timestamp !== "string") return false;
400
+ const stamped = Date.parse(record.timestamp);
401
+ return Number.isFinite(stamped) && stamped < momentMs;
402
+ };
403
+ var isCompactionSummary = (record) => record.isCompactSummary === true;
404
+ var transcriptLinesSeenLive = (lines, epochStartedAtMs, startedBeforeEpoch) => lines.filter((line) => {
405
+ const record = parsedTranscriptRecord(line);
406
+ if (record === null) return true;
407
+ return !stampedBefore(record, epochStartedAtMs) && !(startedBeforeEpoch && isCompactionSummary(record));
408
+ });
409
+
166
410
  // src/transcript-sync.ts
411
+ import { join as join3, resolve } from "path";
412
+ 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
413
  var claudeTranscriptPath = (cwd, sessionId) => {
168
414
  const projectId = resolve(cwd).replace(/[^a-zA-Z0-9-]/g, "-");
169
415
  const configDir = claudeConfigDir();
170
- return join2(configDir, "projects", projectId, `${sessionId}.jsonl`);
416
+ return join3(configDir, "projects", projectId, `${sessionId}.jsonl`);
171
417
  };
172
418
  var createSyncState = () => ({
173
419
  sessionKey: null,
@@ -269,7 +515,7 @@ var chunkRecords = (records) => {
269
515
  };
270
516
  var readLines = (path) => {
271
517
  try {
272
- return existsSync2(path) ? readFileSync2(path, "utf-8").split("\n") : [];
518
+ return existsSync2(path) ? readFileSync3(path, "utf-8").split("\n") : [];
273
519
  } catch {
274
520
  return [];
275
521
  }
@@ -315,7 +561,7 @@ var readThroughALineEnd = (descriptor, offset, unread) => {
315
561
  let window = Math.min(unread, MAXIMUM_TRANSCRIPT_READ_BYTES);
316
562
  for (; ; ) {
317
563
  const buffer = Buffer.alloc(window);
318
- const received = buffer.subarray(0, readSync(descriptor, buffer, 0, window, offset));
564
+ const received = buffer.subarray(0, readSync2(descriptor, buffer, 0, window, offset));
319
565
  if (window >= unread || received.length < window || received.includes(LINE_FEED)) return received;
320
566
  window = Math.min(unread, window * 2);
321
567
  }
@@ -323,12 +569,12 @@ var readThroughALineEnd = (descriptor, offset, unread) => {
323
569
  var readLinesSinceCutoff = (epoch, path, seenAtMs) => {
324
570
  let descriptor;
325
571
  try {
326
- descriptor = openSync(path, "r");
572
+ descriptor = openSync2(path, "r");
327
573
  } catch {
328
574
  return nothingUnread(sightTranscript(epoch, path, seenAtMs));
329
575
  }
330
576
  try {
331
- const info = fstatSync(descriptor);
577
+ const info = fstatSync2(descriptor);
332
578
  const cutoff = recordSighting(epoch, path, sightingOf(info, seenAtMs));
333
579
  const unread = info.size - cutoff.offset;
334
580
  if (unread <= 0) return nothingUnread(cutoff);
@@ -342,7 +588,7 @@ var readLinesSinceCutoff = (epoch, path, seenAtMs) => {
342
588
  reachedEnd
343
589
  };
344
590
  } finally {
345
- closeSync(descriptor);
591
+ closeSync2(descriptor);
346
592
  }
347
593
  };
348
594
  var linesSeenDuringEpoch = (epoch, path, source, seenAtMs) => {
@@ -443,8 +689,16 @@ var startTranscriptSync = (opts) => {
443
689
  }, delayMs);
444
690
  if (typeof timer.unref === "function") timer.unref();
445
691
  };
692
+ const stopDisplay = (opts.source ?? "claude") === "claude" ? startDisplayPublisher({
693
+ apiKey: opts.apiKey,
694
+ sessionId: opts.sessionId,
695
+ baseUrl: opts.baseUrl ?? getBaseUrl(),
696
+ directory: opts.storage?.sessionKeyDirectory,
697
+ enabled: () => running && !state.keyRejected && (opts.enabled ? opts.enabled() : transcriptsEnabled())
698
+ }) : void 0;
446
699
  schedule(0);
447
700
  return () => {
701
+ stopDisplay?.();
448
702
  running = false;
449
703
  if (timer) clearTimeout(timer);
450
704
  };
@@ -452,30 +706,30 @@ var startTranscriptSync = (opts) => {
452
706
 
453
707
  // src/transcript-registration.ts
454
708
  import { homedir as homedir2 } from "os";
455
- var defaultRegistryDirectory = () => process.env.PUSHARY_TRANSCRIPT_REGISTRY_DIR || join3(pusharyDir(), "transcript-capture");
709
+ var defaultRegistryDirectory = () => process.env.PUSHARY_TRANSCRIPT_REGISTRY_DIR || join4(pusharyDir(), "transcript-capture");
456
710
  var cliRegistry = (apiKey = getApiKey()) => ({ apiKey, baseUrl: getBaseUrl(), directory: defaultRegistryDirectory() });
457
711
  var accountFor = (registry) => createHash("sha256").update(`${registry.baseUrl}\0${registry.apiKey}`).digest("hex");
458
712
  var TTL = 48 * 60 * 60 * 1e3;
459
- var read = (path) => JSON.parse(readFileSync3(path, "utf8"));
713
+ var read = (path) => JSON.parse(readFileSync4(path, "utf8"));
460
714
  var consent = (record) => record.consentOverride ?? readTranscriptPreference() ?? false;
461
715
  var withoutTrailingSlash = (url) => url.replace(/\/+$/, "");
462
716
  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`);
717
+ var fileFor3 = (registry, sessionId) => join4(registry.directory, `${createHash("sha256").update(accountFor(registry) + sessionId).digest("hex")}.json`);
464
718
  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
719
  var registeredTranscriptEnabled = (apiKey, sessionId, registry = cliRegistry(apiKey)) => {
466
720
  try {
467
- return permitted(registry, read(fileFor2(registry, sessionId)));
721
+ return permitted(registry, read(fileFor3(registry, sessionId)));
468
722
  } catch {
469
723
  return false;
470
724
  }
471
725
  };
472
726
  var macOwnsClaudeHooks = (cwd) => [
473
- join3(claudeConfigDir(), "settings.json"),
474
- join3(cwd, ".claude", "settings.json"),
475
- join3(cwd, ".claude", "settings.local.json")
727
+ join4(claudeConfigDir(), "settings.json"),
728
+ join4(cwd, ".claude", "settings.json"),
729
+ join4(cwd, ".claude", "settings.local.json")
476
730
  ].some((path) => {
477
731
  try {
478
- return hookOwnerOf(JSON.parse(readFileSync3(path, "utf8"))) === "app";
732
+ return hookOwnerOf(JSON.parse(readFileSync4(path, "utf8"))) === "app";
479
733
  } catch {
480
734
  return false;
481
735
  }
@@ -483,9 +737,9 @@ var macOwnsClaudeHooks = (cwd) => [
483
737
  var TRANSCRIPT_ROOT_NAME = { claude: "projects", codex: "sessions" };
484
738
  var transcriptRoots = (source) => {
485
739
  const home = homedir2();
486
- if (source === "codex") return [join3(codexHome(), TRANSCRIPT_ROOT_NAME.codex)];
740
+ if (source === "codex") return [join4(codexHome(), TRANSCRIPT_ROOT_NAME.codex)];
487
741
  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)];
742
+ return [join4(configured || join4(home, ".claude"), TRANSCRIPT_ROOT_NAME.claude), join4(home, ".claude", TRANSCRIPT_ROOT_NAME.claude)];
489
743
  };
490
744
  var isAgentTranscriptRoot = (root, source) => isAbsolute(root) && resolve2(root) === root && basename(root) === TRANSCRIPT_ROOT_NAME[source];
491
745
  var acceptedTranscriptRoots = (source, registeredRoot) => typeof registeredRoot === "string" && isAgentTranscriptRoot(registeredRoot, source) ? [...transcriptRoots(source), registeredRoot] : transcriptRoots(source);
@@ -513,7 +767,7 @@ var registerTranscript = (source, input, startDaemon = () => {
513
767
  try {
514
768
  const registry = options.registry ?? cliRegistry();
515
769
  const account = accountFor(registry);
516
- const path = fileFor2(registry, registration.sessionId);
770
+ const path = fileFor3(registry, registration.sessionId);
517
771
  let previous;
518
772
  try {
519
773
  previous = read(path);
@@ -563,11 +817,11 @@ var startRegisteredTranscripts = (apiKey, machineId, options = {}) => {
563
817
  try {
564
818
  let unreadable = 0;
565
819
  const files = readdirSync2(registry.directory).filter((name) => name.endsWith(".json")).flatMap((name) => {
566
- const path = join3(registry.directory, name);
820
+ const path = join4(registry.directory, name);
567
821
  try {
568
822
  const time = statSync3(path).mtimeMs;
569
823
  if (Date.now() - time > TTL) {
570
- unlinkSync(path);
824
+ unlinkSync2(path);
571
825
  return [];
572
826
  }
573
827
  return [{ path, time, raw: read(path) }];
@@ -657,9 +911,14 @@ var startRegisteredTranscripts = (apiKey, machineId, options = {}) => {
657
911
  };
658
912
 
659
913
  export {
914
+ redactDraft,
915
+ captureDisplayDraft,
916
+ sessionKeyDirectory,
660
917
  loadSessionKey,
918
+ startDraftPublisher,
661
919
  claudeTranscriptPath,
662
920
  registeredTranscriptEnabled,
921
+ captureRegistration,
663
922
  registerTranscript,
664
923
  startRegisteredTranscripts
665
924
  };
@@ -22,22 +22,22 @@ import {
22
22
  turnCodexAppCardsOff,
23
23
  turnCodexAppCardsOn,
24
24
  whyLaunchctlFailed
25
- } from "./chunk-VFF7TP5B.js";
26
- import "./chunk-HIHA3GOR.js";
25
+ } from "./chunk-3PPL64AG.js";
26
+ import "./chunk-EH5OLIP2.js";
27
27
  import "./chunk-XCEN33UP.js";
28
28
  import "./chunk-QCGNGEGE.js";
29
- import "./chunk-QDISUNK3.js";
29
+ import "./chunk-DCHZ5JOI.js";
30
30
  import "./chunk-XHKBHWLX.js";
31
- import "./chunk-6YZ72YP3.js";
31
+ import "./chunk-LIDUPDEY.js";
32
32
  import "./chunk-E46NE7FB.js";
33
- import "./chunk-5XYYBB5Z.js";
34
- import "./chunk-ZWCKS65X.js";
35
- import "./chunk-N4NSX5MB.js";
36
- import "./chunk-GUJBS6TK.js";
37
- import "./chunk-BMVWGPYB.js";
38
- import "./chunk-7KMLITXH.js";
33
+ import "./chunk-BGEUAJYL.js";
34
+ import "./chunk-DARPCQPY.js";
35
+ import "./chunk-2J4IVUEA.js";
36
+ import "./chunk-AUDYAVNM.js";
37
+ import "./chunk-53D6FFVE.js";
38
+ import "./chunk-UIS332AZ.js";
39
39
  import "./chunk-QTOLNRET.js";
40
- import "./chunk-5XKJH5GD.js";
40
+ import "./chunk-QRS7FQHE.js";
41
41
  import "./chunk-MLKGABMK.js";
42
42
  export {
43
43
  CODEX_APP_CARDS_DIRECTORY,
@@ -11,7 +11,7 @@ import {
11
11
  installedCodexApp,
12
12
  shellQuoted,
13
13
  writeDesktopProxy
14
- } from "./chunk-HIHA3GOR.js";
14
+ } from "./chunk-EH5OLIP2.js";
15
15
  import "./chunk-XCEN33UP.js";
16
16
  import {
17
17
  CODEX_APP_LAUNCH_VARIABLE