handmux 0.23.0 → 0.25.0

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 (327) hide show
  1. package/README.md +31 -8
  2. package/README.zh-CN.md +31 -8
  3. package/bin/handmux.js +14 -688
  4. package/bin/node-version.js +20 -0
  5. package/dist/bin/handmux.js +1015 -0
  6. package/dist/connectors/bridgeClient.js +385 -0
  7. package/dist/connectors/claude/index.js +476 -0
  8. package/dist/connectors/pi/bridgeClient.js +8 -0
  9. package/dist/connectors/pi/index.js +851 -0
  10. package/dist/hooks/handmux-notify.sh +64 -0
  11. package/{hooks → dist/hooks}/handmux-statusline.cjs +28 -6
  12. package/dist/hooks/handmux-write.cjs +264 -0
  13. package/dist/package.json +127 -0
  14. package/dist/public/assets/index-CTrqTqtT.css +32 -0
  15. package/dist/public/assets/index-DmjeMSQA.js +346 -0
  16. package/dist/public/assets/pcm-worklet-CxcDy7PB.js +1 -0
  17. package/{public → dist/public}/index.html +5 -4
  18. package/dist/src/agent-runtime/adapter.js +204 -0
  19. package/dist/src/agent-runtime/bridge.js +1006 -0
  20. package/dist/src/agent-runtime/bridgeStore.js +25 -0
  21. package/dist/src/agent-runtime/bridgeTransport.js +619 -0
  22. package/dist/src/agent-runtime/bridgeTypes.js +13 -0
  23. package/dist/src/agent-runtime/builtinRuntime.js +193 -0
  24. package/dist/src/agent-runtime/conversation.js +2179 -0
  25. package/dist/src/agent-runtime/conversationActivation.js +75 -0
  26. package/dist/src/agent-runtime/conversationActivity.js +69 -0
  27. package/dist/src/agent-runtime/conversationControls.js +206 -0
  28. package/dist/src/agent-runtime/conversationLiveHub.js +276 -0
  29. package/dist/src/agent-runtime/conversationStore.js +352 -0
  30. package/dist/src/agent-runtime/conversationTypes.js +1 -0
  31. package/dist/src/agent-runtime/conversationValidation.js +339 -0
  32. package/dist/src/agent-runtime/inbox.js +973 -0
  33. package/dist/src/agent-runtime/inboxPushProjection.js +69 -0
  34. package/dist/src/agent-runtime/inboxStore.js +25 -0
  35. package/dist/src/agent-runtime/inboxTypes.js +1 -0
  36. package/dist/src/agent-runtime/interaction.js +829 -0
  37. package/dist/src/agent-runtime/interactionLiveHub.js +225 -0
  38. package/dist/src/agent-runtime/interactionStore.js +18 -0
  39. package/dist/src/agent-runtime/interactionTypes.js +1 -0
  40. package/dist/src/agent-runtime/legacyInboxProjection.js +70 -0
  41. package/dist/src/agent-runtime/migrateLegacyCodexOutbox.js +336 -0
  42. package/dist/src/agent-runtime/resourceTypes.js +1 -0
  43. package/dist/src/agent-runtime/resources.js +245 -0
  44. package/dist/src/agent-runtime/run.js +328 -0
  45. package/dist/src/agent-runtime/runtime.js +793 -0
  46. package/dist/src/agent-runtime/sessionControl.js +164 -0
  47. package/dist/src/agent-runtime/subscriptionUsage.js +190 -0
  48. package/dist/src/agent-runtime/subscriptionUsageLegacy.js +18 -0
  49. package/dist/src/agent-runtime/tmuxRuntime.js +156 -0
  50. package/dist/src/agents/claude.js +203 -0
  51. package/dist/src/agents/claudeConversation.js +362 -0
  52. package/dist/src/agents/claudeInteraction.js +134 -0
  53. package/dist/src/agents/claudeSubscriptionUsage.js +61 -0
  54. package/dist/src/agents/codex.js +262 -0
  55. package/dist/src/agents/codexConversation.js +1103 -0
  56. package/dist/src/agents/codexConversationActivation.js +121 -0
  57. package/dist/src/agents/codexConversationControls.js +256 -0
  58. package/dist/src/agents/codexInteraction.js +306 -0
  59. package/dist/src/agents/codexSessionControl.js +118 -0
  60. package/dist/src/agents/codexSubscriptionUsage.js +313 -0
  61. package/dist/src/agents/codexTerminalControl.js +23 -0
  62. package/dist/src/agents/conversationProjectionSafety.js +416 -0
  63. package/dist/src/agents/index.js +39 -0
  64. package/dist/src/agents/nativeInbox.js +264 -0
  65. package/dist/src/agents/nativeInboxSources.js +115 -0
  66. package/dist/src/agents/pi.js +28 -0
  67. package/dist/src/agents/piConversation.js +523 -0
  68. package/dist/src/agents/piConversationHistory.js +502 -0
  69. package/dist/src/agents/piInboxBridge.js +368 -0
  70. package/dist/src/agents/piSessionControl.js +32 -0
  71. package/dist/src/agents/processIdentity.js +94 -0
  72. package/dist/src/agents/scanUtils.js +262 -0
  73. package/dist/src/apiAccountProviders.js +194 -0
  74. package/dist/src/apiAccounts.js +592 -0
  75. package/dist/src/apiErrors.js +86 -0
  76. package/{src → dist/src}/appName.js +12 -13
  77. package/dist/src/asr/iflyConfig.js +7 -0
  78. package/dist/src/asr/iflySign.js +14 -0
  79. package/dist/src/auth.js +27 -0
  80. package/dist/src/browser/bootstrap.js +72 -0
  81. package/dist/src/browser/cookieProfiles.js +413 -0
  82. package/dist/src/browser/coordinator.js +51 -0
  83. package/dist/src/browser/hammerheadCompat.js +104 -0
  84. package/dist/src/browser/hammerheadRedirectCompat.js +37 -0
  85. package/dist/src/browser/manager.js +560 -0
  86. package/dist/src/browser/originLabel.js +15 -0
  87. package/dist/src/browser/profilePersistence.js +235 -0
  88. package/dist/src/browser/publicProxy.js +176 -0
  89. package/dist/src/browser/routes.js +159 -0
  90. package/dist/src/browser/sessionStore.js +72 -0
  91. package/dist/src/browser/siteVersion.js +121 -0
  92. package/dist/src/browser/targetPolicy.js +163 -0
  93. package/{src → dist/src}/browser/worker.js +14 -18
  94. package/dist/src/browser/workerClient.js +286 -0
  95. package/dist/src/browser/workerServer.js +91 -0
  96. package/dist/src/captureBackground.js +95 -0
  97. package/dist/src/claudeEvents.js +525 -0
  98. package/dist/src/cli/agentIntegration.js +131 -0
  99. package/dist/src/cli/agentLauncher.js +26 -0
  100. package/dist/src/cli/claudeHooks.js +290 -0
  101. package/{src → dist/src}/cli/cloudflareUrl.js +2 -3
  102. package/dist/src/cli/cloudflared.js +106 -0
  103. package/dist/src/cli/codexManaged.js +80 -0
  104. package/{src → dist/src}/cli/cpolarUrl.js +8 -9
  105. package/dist/src/cli/drivers.js +98 -0
  106. package/dist/src/cli/hookScaffold.js +39 -0
  107. package/dist/src/cli/i18n/en.js +392 -0
  108. package/{src → dist/src}/cli/i18n/index.js +27 -25
  109. package/dist/src/cli/i18n/zh.js +391 -0
  110. package/dist/src/cli/legacyCodexHooks.js +52 -0
  111. package/{src → dist/src}/cli/natappUrl.js +2 -3
  112. package/dist/src/cli/options.js +245 -0
  113. package/dist/src/cli/piExtension.js +155 -0
  114. package/dist/src/cli/probe.js +16 -0
  115. package/{src → dist/src}/cli/prompt.js +4 -6
  116. package/dist/src/cli/pushCmd.js +102 -0
  117. package/{src → dist/src}/cli/qr.js +19 -19
  118. package/dist/src/cli/service.js +154 -0
  119. package/dist/src/cli/setupModel.js +194 -0
  120. package/dist/src/cli/setupWizard.js +647 -0
  121. package/dist/src/cli/shortcutEditor.js +351 -0
  122. package/dist/src/cli/sshTunnel.js +18 -0
  123. package/dist/src/cli/state.js +117 -0
  124. package/dist/src/cli/statusLine.js +115 -0
  125. package/dist/src/cli/supervisor.js +278 -0
  126. package/dist/src/cli/supervisorHealth.js +21 -0
  127. package/dist/src/cli/supervisorLaunch.js +148 -0
  128. package/dist/src/cli/supervisorProcesses.js +62 -0
  129. package/dist/src/cli/supervisorState.js +62 -0
  130. package/dist/src/cli/tmuxVersion.js +53 -0
  131. package/{src → dist/src}/cli/tunlite.js +7 -10
  132. package/dist/src/cli/tunnelClients.js +72 -0
  133. package/{src → dist/src}/cli/updateCheck.js +98 -69
  134. package/{src → dist/src}/cli/urlHost.js +8 -4
  135. package/dist/src/cli/workspaceCmd.js +347 -0
  136. package/dist/src/codexAppServer.js +3436 -0
  137. package/dist/src/codexConversationProjection.js +199 -0
  138. package/dist/src/codexDiff.js +118 -0
  139. package/dist/src/codexMessageIdentity.js +18 -0
  140. package/dist/src/codexPlan.js +30 -0
  141. package/dist/src/codexQueueProtocol.js +137 -0
  142. package/dist/src/codexStreamProtocol.js +207 -0
  143. package/dist/src/codexToolProtocol.js +68 -0
  144. package/dist/src/codexTranscriptParse.js +835 -0
  145. package/dist/src/codexUsageSnapshot.js +228 -0
  146. package/dist/src/config.js +8 -0
  147. package/{src → dist/src}/docPath.js +22 -24
  148. package/dist/src/docs.js +337 -0
  149. package/dist/src/git.js +284 -0
  150. package/dist/src/healthProtocol.js +97 -0
  151. package/dist/src/httpApi.js +96 -0
  152. package/dist/src/jsonStore.js +15 -0
  153. package/{src → dist/src}/keyNames.js +3 -3
  154. package/dist/src/notifications.js +111 -0
  155. package/dist/src/orphans.js +167 -0
  156. package/dist/src/paneInput.js +42 -0
  157. package/dist/src/pendingPrompt.js +194 -0
  158. package/dist/src/previewServer.js +202 -0
  159. package/dist/src/previews.js +179 -0
  160. package/dist/src/privateStateStore.js +124 -0
  161. package/dist/src/projectTask/backup.js +224 -0
  162. package/dist/src/projectTask/lock.js +108 -0
  163. package/dist/src/projectTask/migrations.js +90 -0
  164. package/dist/src/projectTask/routes.js +155 -0
  165. package/dist/src/projectTask/runtime.js +182 -0
  166. package/dist/src/projectTask/schema.js +56 -0
  167. package/dist/src/projectTask/store.js +457 -0
  168. package/dist/src/push.js +224 -0
  169. package/dist/src/routes/agents.js +631 -0
  170. package/dist/src/routes/apiAccounts.js +82 -0
  171. package/dist/src/routes/files.js +230 -0
  172. package/dist/src/routes/git.js +89 -0
  173. package/dist/src/routes/health.js +16 -0
  174. package/dist/src/routes/notifications.js +16 -0
  175. package/dist/src/routes/previews.js +43 -0
  176. package/dist/src/routes/push.js +145 -0
  177. package/dist/src/routes/sessions.js +244 -0
  178. package/dist/src/routes/system.js +220 -0
  179. package/dist/src/routes/terminal.js +244 -0
  180. package/dist/src/routes/workspace.js +267 -0
  181. package/dist/src/server.js +315 -0
  182. package/dist/src/shortcutConfig.js +83 -0
  183. package/{src → dist/src}/staticCache.js +5 -3
  184. package/{src → dist/src}/staticCompression.js +5 -6
  185. package/dist/src/terminalStream.js +441 -0
  186. package/{src → dist/src}/tmux/commands.js +185 -172
  187. package/dist/src/tmux/format.js +47 -0
  188. package/dist/src/transcriptParse.js +261 -0
  189. package/dist/src/transcriptReader.js +209 -0
  190. package/{src → dist/src}/trimCapture.js +11 -11
  191. package/dist/src/uploadTypes.js +31 -0
  192. package/dist/src/urlPolicy.js +18 -0
  193. package/dist/src/usagePaths.js +15 -0
  194. package/dist/src/workspace/agentRunner.js +301 -0
  195. package/dist/src/workspace/atomicJson.js +31 -0
  196. package/dist/src/workspace/capture.js +139 -0
  197. package/dist/src/workspace/checkpointer.js +228 -0
  198. package/dist/src/workspace/environment.js +71 -0
  199. package/dist/src/workspace/lock.js +137 -0
  200. package/dist/src/workspace/mapping.js +128 -0
  201. package/dist/src/workspace/operations.js +271 -0
  202. package/dist/src/workspace/paths.js +15 -0
  203. package/dist/src/workspace/planner.js +292 -0
  204. package/dist/src/workspace/restore.js +363 -0
  205. package/dist/src/workspace/runtime.js +318 -0
  206. package/dist/src/workspace/schema.js +156 -0
  207. package/dist/src/workspace/store.js +464 -0
  208. package/dist/src/workspace/tmuxAdapter.js +704 -0
  209. package/dist/src/workspaceProtocol.js +158 -0
  210. package/package.json +30 -17
  211. package/hooks/handmux-notify.sh +0 -22
  212. package/hooks/handmux-write.cjs +0 -78
  213. package/public/assets/index-C3N7OkyP.css +0 -32
  214. package/public/assets/index-D1DWcC5u.js +0 -337
  215. package/src/.gitkeep +0 -0
  216. package/src/agents/claude.js +0 -159
  217. package/src/agents/codex.js +0 -168
  218. package/src/agents/index.js +0 -21
  219. package/src/agents/processIdentity.js +0 -72
  220. package/src/agents/scanUtils.js +0 -225
  221. package/src/asr/iflyConfig.js +0 -10
  222. package/src/asr/iflySign.js +0 -16
  223. package/src/auth.js +0 -30
  224. package/src/browser/bootstrap.js +0 -68
  225. package/src/browser/cookieProfiles.js +0 -403
  226. package/src/browser/coordinator.js +0 -47
  227. package/src/browser/hammerheadCompat.js +0 -86
  228. package/src/browser/hammerheadRedirectCompat.js +0 -38
  229. package/src/browser/manager.js +0 -556
  230. package/src/browser/originLabel.js +0 -17
  231. package/src/browser/profilePersistence.js +0 -241
  232. package/src/browser/publicProxy.js +0 -175
  233. package/src/browser/routes.js +0 -154
  234. package/src/browser/sessionStore.js +0 -78
  235. package/src/browser/siteVersion.js +0 -124
  236. package/src/browser/targetPolicy.js +0 -137
  237. package/src/browser/workerClient.js +0 -253
  238. package/src/browser/workerServer.js +0 -98
  239. package/src/captureBackground.js +0 -93
  240. package/src/claudeEvents.js +0 -399
  241. package/src/cli/claudeHooks.js +0 -206
  242. package/src/cli/cloudflared.js +0 -99
  243. package/src/cli/codexManaged.js +0 -77
  244. package/src/cli/drivers.js +0 -97
  245. package/src/cli/hookScaffold.js +0 -33
  246. package/src/cli/i18n/en.js +0 -385
  247. package/src/cli/i18n/zh.js +0 -384
  248. package/src/cli/legacyCodexHooks.js +0 -49
  249. package/src/cli/options.js +0 -211
  250. package/src/cli/probe.js +0 -16
  251. package/src/cli/pushCmd.js +0 -60
  252. package/src/cli/service.js +0 -126
  253. package/src/cli/setupModel.js +0 -154
  254. package/src/cli/setupWizard.js +0 -506
  255. package/src/cli/shortcutEditor.js +0 -288
  256. package/src/cli/sshTunnel.js +0 -12
  257. package/src/cli/state.js +0 -90
  258. package/src/cli/statusLine.js +0 -90
  259. package/src/cli/supervisor.js +0 -183
  260. package/src/cli/supervisorProcesses.js +0 -57
  261. package/src/cli/tmuxVersion.js +0 -49
  262. package/src/cli/tunnelClients.js +0 -62
  263. package/src/cli/workspaceCmd.js +0 -300
  264. package/src/codexAppServer.js +0 -1620
  265. package/src/codexDiff.js +0 -108
  266. package/src/codexTranscriptParse.js +0 -413
  267. package/src/codexUsageSnapshot.cjs +0 -129
  268. package/src/config.js +0 -9
  269. package/src/docs.js +0 -228
  270. package/src/git.js +0 -208
  271. package/src/httpApi.js +0 -72
  272. package/src/jsonStore.js +0 -24
  273. package/src/notifications.js +0 -68
  274. package/src/orphans.js +0 -152
  275. package/src/pendingPrompt.js +0 -163
  276. package/src/previewServer.js +0 -168
  277. package/src/previews.js +0 -116
  278. package/src/push.js +0 -183
  279. package/src/routes/codex.js +0 -473
  280. package/src/routes/files.js +0 -172
  281. package/src/routes/git.js +0 -57
  282. package/src/routes/notifications.js +0 -19
  283. package/src/routes/previews.js +0 -35
  284. package/src/routes/push.js +0 -119
  285. package/src/routes/sessions.js +0 -168
  286. package/src/routes/system.js +0 -132
  287. package/src/routes/terminal.js +0 -210
  288. package/src/routes/transcript.js +0 -171
  289. package/src/routes/workspace.js +0 -267
  290. package/src/server.js +0 -186
  291. package/src/shortcutConfig.js +0 -75
  292. package/src/terminalStream.js +0 -391
  293. package/src/tmux/format.js +0 -45
  294. package/src/transcriptParse.js +0 -180
  295. package/src/transcriptReader.js +0 -101
  296. package/src/uploadTypes.js +0 -30
  297. package/src/urlPolicy.js +0 -14
  298. package/src/usage.js +0 -260
  299. package/src/workspace/agentRunner.js +0 -188
  300. package/src/workspace/atomicJson.js +0 -32
  301. package/src/workspace/capture.js +0 -139
  302. package/src/workspace/checkpointer.js +0 -187
  303. package/src/workspace/environment.js +0 -75
  304. package/src/workspace/lock.js +0 -128
  305. package/src/workspace/mapping.js +0 -124
  306. package/src/workspace/operations.js +0 -252
  307. package/src/workspace/paths.js +0 -16
  308. package/src/workspace/planner.js +0 -247
  309. package/src/workspace/restore.js +0 -279
  310. package/src/workspace/runtime.js +0 -314
  311. package/src/workspace/schema.js +0 -136
  312. package/src/workspace/store.js +0 -382
  313. package/src/workspace/tmuxAdapter.js +0 -554
  314. /package/{public → dist/public}/fonts/JetBrainsMonoNerdFontMono-Regular.woff2 +0 -0
  315. /package/{public → dist/public}/fonts/TWUnifont.woff2 +0 -0
  316. /package/{public → dist/public}/icons/apple-touch-icon.png +0 -0
  317. /package/{public → dist/public}/icons/badge-96.png +0 -0
  318. /package/{public → dist/public}/icons/icon-192-maskable.png +0 -0
  319. /package/{public → dist/public}/icons/icon-192.png +0 -0
  320. /package/{public → dist/public}/icons/icon-512-maskable.png +0 -0
  321. /package/{public → dist/public}/icons/icon-512.png +0 -0
  322. /package/{public → dist/public}/icons/logo.svg +0 -0
  323. /package/{public → dist/public}/manifest.webmanifest +0 -0
  324. /package/{public → dist/public}/offline.html +0 -0
  325. /package/{public → dist/public}/sw.js +0 -0
  326. /package/{src → dist/src}/browser/protocol.js +0 -0
  327. /package/{src → dist/src}/cli/cfNamed.js +0 -0
@@ -0,0 +1,207 @@
1
+ import { parseCodexConversationMutation, parseCodexConversationMutationValue, projectCodexConversationMutation, } from './codexConversationProjection.js';
2
+ const GOAL_STATUSES = [
3
+ 'active', 'paused', 'blocked', 'usageLimited', 'budgetLimited', 'complete',
4
+ ];
5
+ const GOAL_EVENTS = [
6
+ 'set', 'restarted', 'active', 'paused', 'blocked', 'usageLimited', 'budgetLimited', 'complete',
7
+ ];
8
+ function recordOf(value) {
9
+ return value != null && typeof value === 'object' && !Array.isArray(value)
10
+ ? value
11
+ : null;
12
+ }
13
+ function nonEmptyString(value) {
14
+ return typeof value === 'string' && value ? value : null;
15
+ }
16
+ function nullableString(value) {
17
+ if (value == null)
18
+ return null;
19
+ return nonEmptyString(value) ?? undefined;
20
+ }
21
+ function optionalFiniteNumber(record, key) {
22
+ if (!Object.hasOwn(record, key))
23
+ return undefined;
24
+ const value = record[key];
25
+ return typeof value === 'number' && Number.isFinite(value) ? value : undefined;
26
+ }
27
+ function parseConversationSnapshotMessages(value) {
28
+ if (!Array.isArray(value) || value.length > 100)
29
+ return null;
30
+ const allowedTypes = new Set([
31
+ 'text', 'thinking', 'tool', 'plan', 'goal', 'compact', 'interrupt', 'slash',
32
+ ]);
33
+ const messages = [];
34
+ for (const candidate of value) {
35
+ const message = recordOf(candidate);
36
+ if (!message || typeof message.k !== 'number' || !Number.isSafeInteger(message.k) || message.k < 0
37
+ || typeof message.type !== 'string' || !allowedTypes.has(message.type)
38
+ || (message.id != null && !nonEmptyString(message.id)))
39
+ return null;
40
+ messages.push(message);
41
+ }
42
+ return messages;
43
+ }
44
+ export function parseCodexGoal(value) {
45
+ const record = recordOf(value);
46
+ if (!record)
47
+ return null;
48
+ const objective = nonEmptyString(record.objective);
49
+ const status = GOAL_STATUSES.find((candidate) => candidate === record.status);
50
+ if (!objective || !status)
51
+ return null;
52
+ const threadId = record.threadId == null ? undefined : nonEmptyString(record.threadId) ?? undefined;
53
+ if (record.threadId != null && !threadId)
54
+ return null;
55
+ const numericKeys = ['createdAt', 'updatedAt', 'tokensUsed', 'timeUsedSeconds'];
56
+ const numeric = Object.fromEntries(numericKeys.map((key) => [key, optionalFiniteNumber(record, key)]));
57
+ if (numericKeys.some((key) => Object.hasOwn(record, key) && numeric[key] === undefined))
58
+ return null;
59
+ const tokenBudget = record.tokenBudget;
60
+ if (Object.hasOwn(record, 'tokenBudget') && tokenBudget !== null
61
+ && (typeof tokenBudget !== 'number' || !Number.isFinite(tokenBudget)))
62
+ return null;
63
+ return {
64
+ ...record,
65
+ objective,
66
+ status,
67
+ ...(threadId ? { threadId } : {}),
68
+ ...Object.fromEntries(Object.entries(numeric).filter(([, field]) => field !== undefined)),
69
+ ...(Object.hasOwn(record, 'tokenBudget') ? { tokenBudget: tokenBudget } : {}),
70
+ };
71
+ }
72
+ function parseAgentEvent(record) {
73
+ const type = record.type;
74
+ if (type !== 'started' && type !== 'snapshot' && type !== 'delta' && type !== 'completed')
75
+ return null;
76
+ const threadId = nonEmptyString(record.threadId);
77
+ const turnId = nonEmptyString(record.turnId);
78
+ const itemId = nonEmptyString(record.itemId);
79
+ if (!threadId || !turnId || !itemId)
80
+ return null;
81
+ if (type === 'delta' && typeof record.delta !== 'string')
82
+ return null;
83
+ if (type !== 'delta' && typeof record.text !== 'string')
84
+ return null;
85
+ if (record.completed != null && typeof record.completed !== 'boolean')
86
+ return null;
87
+ return { ...record, type, threadId, turnId, itemId };
88
+ }
89
+ export function parseCodexStreamEvent(value) {
90
+ const record = recordOf(value);
91
+ if (!record)
92
+ return null;
93
+ const agentEvent = parseAgentEvent(record);
94
+ if (agentEvent)
95
+ return agentEvent;
96
+ const type = record.type;
97
+ if (type === 'error') {
98
+ const message = nonEmptyString(record.message);
99
+ return message ? { ...record, type, message } : null;
100
+ }
101
+ const threadId = nonEmptyString(record.threadId);
102
+ if (!threadId)
103
+ return null;
104
+ if (type === 'conversation') {
105
+ const mutation = parseCodexConversationMutationValue(record.mutation);
106
+ if (!mutation || mutation.operation !== 'upsert')
107
+ return null;
108
+ const turnId = mutation.message.turnId;
109
+ const itemId = mutation.message.type === 'text' ? mutation.message.itemId : null;
110
+ if (record.turnId !== turnId || record.itemId !== itemId)
111
+ return null;
112
+ return { ...record, type, threadId, turnId, itemId, mutation };
113
+ }
114
+ if (type === 'conversationSnapshot') {
115
+ const cursor = record.cursor;
116
+ const messages = parseConversationSnapshotMessages(record.messages);
117
+ return typeof cursor === 'number' && Number.isSafeInteger(cursor) && cursor >= 0 && messages
118
+ ? { ...record, type, threadId, cursor, messages }
119
+ : null;
120
+ }
121
+ if (type === 'ready' || type === 'disconnected')
122
+ return { ...record, type, threadId };
123
+ if (type === 'cursorReset') {
124
+ const cursor = record.cursor;
125
+ return typeof cursor === 'number' && Number.isSafeInteger(cursor) && cursor >= 0
126
+ ? { ...record, type, threadId, cursor }
127
+ : null;
128
+ }
129
+ if (type === 'turnCompleted') {
130
+ const turnId = nullableString(record.turnId);
131
+ const status = nullableString(record.status);
132
+ if (turnId === undefined || status === undefined)
133
+ return null;
134
+ return { ...record, type, threadId, turnId, status };
135
+ }
136
+ if (type === 'goalCleared') {
137
+ const turnId = nullableString(record.turnId);
138
+ return turnId === undefined ? null : { ...record, type, threadId, turnId };
139
+ }
140
+ if (type === 'goal') {
141
+ const turnId = nullableString(record.turnId);
142
+ const event = GOAL_EVENTS.find((candidate) => candidate === record.event);
143
+ const goal = parseCodexGoal(record.goal);
144
+ if (turnId === undefined || !event || !goal)
145
+ return null;
146
+ return { ...record, type, threadId, turnId, event, goal };
147
+ }
148
+ return null;
149
+ }
150
+ function projectionFields(event) {
151
+ if (event.type === 'started' || event.type === 'snapshot'
152
+ || event.type === 'delta' || event.type === 'completed') {
153
+ return { kind: 'assistantMessage', lifecycle: event.type };
154
+ }
155
+ if (event.type === 'conversation') {
156
+ return {
157
+ kind: event.mutation.operation === 'upsert' && event.mutation.message.type === 'goal'
158
+ ? 'goal' : 'assistantMessage',
159
+ lifecycle: 'persisted',
160
+ };
161
+ }
162
+ if (event.type === 'turnCompleted')
163
+ return { kind: 'turn', lifecycle: 'completed' };
164
+ if (event.type === 'goal')
165
+ return { kind: 'goal', lifecycle: event.event };
166
+ return { kind: 'goal', lifecycle: 'cleared' };
167
+ }
168
+ function isDomainEvent(event) {
169
+ return event.type !== 'ready' && event.type !== 'cursorReset' && event.type !== 'conversationSnapshot'
170
+ && event.type !== 'disconnected' && event.type !== 'error';
171
+ }
172
+ export function projectCodexStreamEvent(value, sequence) {
173
+ if (!Number.isSafeInteger(sequence) || sequence <= 0)
174
+ return null;
175
+ const event = parseCodexStreamEvent(value);
176
+ if (!event || !isDomainEvent(event))
177
+ return null;
178
+ return {
179
+ ...event,
180
+ eventId: `${event.threadId}:${sequence}`,
181
+ sequence,
182
+ itemId: 'itemId' in event ? event.itemId : null,
183
+ ...projectionFields(event),
184
+ mutation: projectCodexConversationMutation(event),
185
+ };
186
+ }
187
+ export function parseCodexProjectedStreamEvent(value) {
188
+ const record = recordOf(value);
189
+ if (!record)
190
+ return null;
191
+ const event = parseCodexStreamEvent(record);
192
+ if (!event || !isDomainEvent(event))
193
+ return null;
194
+ const sequence = record.sequence;
195
+ const eventId = nonEmptyString(record.eventId);
196
+ if (typeof sequence !== 'number' || !Number.isSafeInteger(sequence) || sequence <= 0
197
+ || eventId !== `${event.threadId}:${sequence}`)
198
+ return null;
199
+ const fields = projectionFields(event);
200
+ const itemId = 'itemId' in event ? event.itemId : null;
201
+ if (record.itemId !== itemId || record.kind !== fields.kind || record.lifecycle !== fields.lifecycle)
202
+ return null;
203
+ const mutation = parseCodexConversationMutation(record.mutation, event);
204
+ if (mutation === undefined)
205
+ return null;
206
+ return { ...event, eventId, sequence, itemId, ...fields, mutation };
207
+ }
@@ -0,0 +1,68 @@
1
+ function recordOf(value) {
2
+ return value != null && typeof value === 'object' && !Array.isArray(value)
3
+ ? value
4
+ : null;
5
+ }
6
+ function positionOf(value) {
7
+ return value === null ? null : Number.isInteger(value) ? value : undefined;
8
+ }
9
+ export function parseCodexDiff(value) {
10
+ const record = recordOf(value);
11
+ if (!record || !Number.isInteger(record.added) || !Number.isInteger(record.removed)
12
+ || record.added < 0 || record.removed < 0)
13
+ return null;
14
+ let hunks = null;
15
+ if (record.hunks !== null) {
16
+ if (!Array.isArray(record.hunks))
17
+ return null;
18
+ hunks = [];
19
+ for (const candidate of record.hunks) {
20
+ const hunk = recordOf(candidate);
21
+ if (!hunk)
22
+ return null;
23
+ const oldStart = positionOf(hunk.oldStart);
24
+ const newStart = positionOf(hunk.newStart);
25
+ if (oldStart === undefined || newStart === undefined
26
+ || !Array.isArray(hunk.lines) || hunk.lines.some((line) => typeof line !== 'string'))
27
+ return null;
28
+ hunks.push({ oldStart, newStart, lines: hunk.lines });
29
+ }
30
+ }
31
+ if (record.created != null && record.created !== true)
32
+ return null;
33
+ return {
34
+ ...record,
35
+ added: record.added,
36
+ removed: record.removed,
37
+ hunks,
38
+ ...(record.created === true ? { created: true } : {}),
39
+ };
40
+ }
41
+ export function parseCodexToolProjection(value) {
42
+ const record = recordOf(value);
43
+ if (!record || typeof record.name !== 'string' || !record.name
44
+ || typeof record.isError !== 'boolean'
45
+ || (record.result !== null && typeof record.result !== 'string'))
46
+ return null;
47
+ const input = Array.isArray(record.input) ? record.input : recordOf(record.input);
48
+ if (!input)
49
+ return null;
50
+ const outcomes = ['running', 'success', 'failed', 'declined', 'completed'];
51
+ const outcome = record.outcome == null
52
+ ? undefined
53
+ : outcomes.find((candidate) => candidate === record.outcome);
54
+ if (record.outcome != null && !outcome)
55
+ return null;
56
+ const diff = record.diff == null ? undefined : parseCodexDiff(record.diff);
57
+ if (record.diff != null && !diff)
58
+ return null;
59
+ return {
60
+ ...record,
61
+ name: record.name,
62
+ input,
63
+ result: record.result,
64
+ isError: record.isError,
65
+ ...(outcome ? { outcome } : {}),
66
+ ...(diff ? { diff } : {}),
67
+ };
68
+ }