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,339 @@
1
+ import { isIP } from 'node:net';
2
+ const ITEM_ID_RE = /^[a-zA-Z0-9][a-zA-Z0-9._:-]{0,255}$/;
3
+ const RESOURCE_ID_RE = /^[a-zA-Z0-9_-]{16,256}$/;
4
+ const MAX_ITEM_BYTES = 1024 * 1024;
5
+ const MAX_BLOCK_BYTES = 256 * 1024;
6
+ export class ConversationValidationError extends Error {
7
+ constructor(message) {
8
+ super(message);
9
+ this.name = 'ConversationValidationError';
10
+ }
11
+ }
12
+ function isRecord(value) {
13
+ return value !== null && typeof value === 'object' && !Array.isArray(value);
14
+ }
15
+ function text(value, max = MAX_BLOCK_BYTES) {
16
+ return typeof value === 'string' && value.length > 0 && Buffer.byteLength(value) <= max;
17
+ }
18
+ function optionalText(value, max = MAX_BLOCK_BYTES) {
19
+ return value === undefined || text(value, max);
20
+ }
21
+ function jsonValue(value, seen = new Set()) {
22
+ if (value === null || typeof value === 'string' || typeof value === 'boolean')
23
+ return true;
24
+ if (typeof value === 'number')
25
+ return Number.isFinite(value);
26
+ if (typeof value !== 'object' || seen.has(value))
27
+ return false;
28
+ seen.add(value);
29
+ const valid = Array.isArray(value)
30
+ ? value.every((item) => jsonValue(item, seen))
31
+ : (Object.getPrototypeOf(value) === Object.prototype || Object.getPrototypeOf(value) === null)
32
+ && Object.values(value).every((item) => jsonValue(item, seen));
33
+ seen.delete(value);
34
+ return valid;
35
+ }
36
+ function cloneJson(value) {
37
+ return JSON.parse(JSON.stringify(value));
38
+ }
39
+ function validExternalUrl(value) {
40
+ if (typeof value !== 'string' || value.length === 0 || value.length > 8192)
41
+ return false;
42
+ try {
43
+ const url = new URL(value);
44
+ if ((url.protocol !== 'http:' && url.protocol !== 'https:') || url.username || url.password)
45
+ return false;
46
+ const host = url.hostname.replace(/^\[|\]$/g, '').toLowerCase();
47
+ if (host === 'localhost' || host.endsWith('.localhost') || host === '0.0.0.0' || host === '::1')
48
+ return false;
49
+ if (isIP(host) === 4) {
50
+ const octets = host.split('.').map(Number);
51
+ if (octets[0] === 0 || octets[0] === 10 || octets[0] === 127
52
+ || (octets[0] === 169 && octets[1] === 254)
53
+ || (octets[0] === 172 && octets[1] >= 16 && octets[1] <= 31)
54
+ || (octets[0] === 192 && octets[1] === 168))
55
+ return false;
56
+ }
57
+ if (isIP(host) === 6 && (host === '::' || /^fe[89ab][0-9a-f]:/i.test(host)
58
+ || /^(fc|fd)/i.test(host) || /^::ffff:(0:)?(127\.|10\.|169\.254\.|192\.168\.)/i.test(host))) {
59
+ return false;
60
+ }
61
+ return true;
62
+ }
63
+ catch {
64
+ return false;
65
+ }
66
+ }
67
+ function contentBlock(raw) {
68
+ if (!isRecord(raw) || typeof raw.type !== 'string')
69
+ return null;
70
+ if (raw.type === 'text') {
71
+ return typeof raw.text === 'string' && Buffer.byteLength(raw.text) <= MAX_BLOCK_BYTES
72
+ ? { type: 'text', text: raw.text } : null;
73
+ }
74
+ if (raw.type === 'json') {
75
+ if (!jsonValue(raw.value))
76
+ return null;
77
+ const value = cloneJson(raw.value);
78
+ return Buffer.byteLength(JSON.stringify(value)) <= MAX_BLOCK_BYTES ? { type: 'json', value } : null;
79
+ }
80
+ if (raw.type === 'resource') {
81
+ if (typeof raw.resourceId !== 'string' || !RESOURCE_ID_RE.test(raw.resourceId)
82
+ || !optionalText(raw.name, 1024) || !optionalText(raw.mediaType, 256))
83
+ return null;
84
+ return {
85
+ type: 'resource',
86
+ resourceId: raw.resourceId,
87
+ ...(raw.name === undefined ? {} : { name: raw.name }),
88
+ ...(raw.mediaType === undefined ? {} : { mediaType: raw.mediaType }),
89
+ };
90
+ }
91
+ if (raw.type === 'external_link') {
92
+ if (!validExternalUrl(raw.url) || !optionalText(raw.name, 1024))
93
+ return null;
94
+ return {
95
+ type: 'external_link',
96
+ url: raw.url,
97
+ ...(raw.name === undefined ? {} : { name: raw.name }),
98
+ };
99
+ }
100
+ return null;
101
+ }
102
+ function content(value) {
103
+ if (!Array.isArray(value))
104
+ return null;
105
+ const blocks = value.map(contentBlock);
106
+ return blocks.some((block) => block === null) ? null : blocks;
107
+ }
108
+ function extensions(value, agentId) {
109
+ if (value === undefined)
110
+ return undefined;
111
+ if (!isRecord(value))
112
+ return null;
113
+ const out = {};
114
+ for (const [key, extension] of Object.entries(value)) {
115
+ if ((!key.startsWith(`${agentId}.`) && !key.startsWith('conversation.'))
116
+ || key.length > 256 || !jsonValue(extension))
117
+ return null;
118
+ out[key] = cloneJson(extension);
119
+ }
120
+ return out;
121
+ }
122
+ function errorValue(value) {
123
+ if (value === undefined)
124
+ return undefined;
125
+ if (!isRecord(value) || !text(value.code, 256) || !text(value.message, 4096)
126
+ || (value.retryable !== undefined && typeof value.retryable !== 'boolean'))
127
+ return null;
128
+ return {
129
+ code: value.code,
130
+ message: value.message,
131
+ ...(value.retryable === undefined ? {} : { retryable: value.retryable }),
132
+ };
133
+ }
134
+ function truncationValue(value) {
135
+ if (value === undefined)
136
+ return undefined;
137
+ if (!isRecord(value)
138
+ || !['size_limit', 'provider_truncated', 'redacted', 'unavailable'].includes(String(value.reason))
139
+ || (value.originalBytes !== undefined
140
+ && (!Number.isSafeInteger(value.originalBytes) || Number(value.originalBytes) < 0)))
141
+ return null;
142
+ return {
143
+ reason: value.reason,
144
+ ...(value.originalBytes === undefined ? {} : { originalBytes: value.originalBytes }),
145
+ };
146
+ }
147
+ function relativePath(value) {
148
+ if (value === undefined)
149
+ return true;
150
+ if (!text(value, 4096) || value.startsWith('/') || /^[a-zA-Z]:[\\/]/.test(value)
151
+ || value.split(/[\\/]/).includes('..'))
152
+ return false;
153
+ return true;
154
+ }
155
+ function draft(raw, agentId) {
156
+ const ext = extensions(raw.extensions, agentId);
157
+ if (ext === null || !optionalText(raw.groupingId, 256)
158
+ || !optionalText(raw.correlationId, 256))
159
+ return null;
160
+ const base = {
161
+ ...(raw.groupingId === undefined ? {} : { groupingId: raw.groupingId }),
162
+ ...(raw.correlationId === undefined ? {} : { correlationId: raw.correlationId }),
163
+ ...(ext === undefined ? {} : { extensions: ext }),
164
+ };
165
+ if (raw.kind === 'message') {
166
+ const blocks = content(raw.content);
167
+ if (!blocks || !['user', 'assistant', 'system'].includes(String(raw.role)))
168
+ return null;
169
+ return {
170
+ kind: 'message', ...base,
171
+ role: raw.role, content: blocks,
172
+ };
173
+ }
174
+ if (raw.kind === 'reasoning_summary') {
175
+ return typeof raw.text === 'string' && Buffer.byteLength(raw.text) <= MAX_BLOCK_BYTES
176
+ ? { kind: 'reasoning_summary', ...base, text: raw.text } : null;
177
+ }
178
+ if (raw.kind === 'tool_call') {
179
+ if (!text(raw.callId, 256) || !text(raw.name, 512) || !optionalText(raw.summary, 4096)
180
+ || (raw.input !== undefined && !jsonValue(raw.input)))
181
+ return null;
182
+ return {
183
+ kind: 'tool_call', ...base, callId: raw.callId, name: raw.name,
184
+ ...(raw.input === undefined ? {} : { input: cloneJson(raw.input) }),
185
+ ...(raw.summary === undefined ? {} : { summary: raw.summary }),
186
+ };
187
+ }
188
+ if (raw.kind === 'tool_result') {
189
+ const blocks = content(raw.content);
190
+ if (!text(raw.callId, 256) || !blocks
191
+ || (raw.isError !== undefined && typeof raw.isError !== 'boolean'))
192
+ return null;
193
+ return {
194
+ kind: 'tool_result', ...base, callId: raw.callId, content: blocks,
195
+ ...(raw.isError === undefined ? {} : { isError: raw.isError }),
196
+ };
197
+ }
198
+ if (raw.kind === 'diff') {
199
+ if (!relativePath(raw.path) || !optionalText(raw.patch, MAX_BLOCK_BYTES)
200
+ || !optionalText(raw.summary, 4096))
201
+ return null;
202
+ return {
203
+ kind: 'diff', ...base,
204
+ ...(raw.path === undefined ? {} : { path: raw.path }),
205
+ ...(raw.patch === undefined ? {} : { patch: raw.patch }),
206
+ ...(raw.summary === undefined ? {} : { summary: raw.summary }),
207
+ };
208
+ }
209
+ if (raw.kind === 'compaction') {
210
+ return optionalText(raw.summary, MAX_BLOCK_BYTES)
211
+ ? { kind: 'compaction', ...base, ...(raw.summary === undefined ? {} : { summary: raw.summary }) }
212
+ : null;
213
+ }
214
+ if (raw.kind === 'interrupt') {
215
+ if (!['user', 'agent', 'system'].includes(String(raw.actor)) || !optionalText(raw.reason, 1024))
216
+ return null;
217
+ return {
218
+ kind: 'interrupt', ...base, actor: raw.actor,
219
+ ...(raw.reason === undefined ? {} : { reason: raw.reason }),
220
+ };
221
+ }
222
+ if (raw.kind === 'notice') {
223
+ if (!['info', 'warning', 'error'].includes(String(raw.level))
224
+ || !optionalText(raw.code, 256) || !text(raw.message, 4096))
225
+ return null;
226
+ return {
227
+ kind: 'notice', ...base, level: raw.level,
228
+ ...(raw.code === undefined ? {} : { code: raw.code }),
229
+ message: raw.message,
230
+ };
231
+ }
232
+ return null;
233
+ }
234
+ export function normalizeConversationDraft(raw, agentId) {
235
+ if (!isRecord(raw) || typeof raw.kind !== 'string') {
236
+ throw new ConversationValidationError('Invalid Conversation item draft');
237
+ }
238
+ const normalized = draft(raw, agentId);
239
+ if (!normalized)
240
+ throw new ConversationValidationError('Invalid Conversation item draft');
241
+ if (Buffer.byteLength(JSON.stringify(normalized)) > MAX_ITEM_BYTES) {
242
+ throw new ConversationValidationError('Conversation item draft exceeds the size limit');
243
+ }
244
+ return normalized;
245
+ }
246
+ export function normalizeConversationItem(raw, agentId, sessionId) {
247
+ if (!isRecord(raw) || typeof raw.id !== 'string' || !ITEM_ID_RE.test(raw.id)
248
+ || raw.sessionId !== sessionId
249
+ || !['complete', 'error', 'truncated'].includes(String(raw.status))
250
+ || (raw.sourceCreatedAt !== undefined
251
+ && (typeof raw.sourceCreatedAt !== 'number' || !Number.isFinite(raw.sourceCreatedAt)))) {
252
+ throw new ConversationValidationError('Invalid durable Conversation item');
253
+ }
254
+ const draft = normalizeConversationDraft(raw, agentId);
255
+ if ((draft.kind === 'reasoning_summary' && draft.text.length === 0)
256
+ || ((draft.kind === 'message' || draft.kind === 'tool_result')
257
+ && draft.content.some((block) => block.type === 'text' && block.text.length === 0))) {
258
+ throw new ConversationValidationError('Durable Conversation items cannot contain empty text blocks');
259
+ }
260
+ const error = errorValue(raw.error);
261
+ const truncation = truncationValue(raw.truncation);
262
+ if (error === null || truncation === null || (raw.status === 'error' && error === undefined)
263
+ || (raw.status === 'truncated' && truncation === undefined)) {
264
+ throw new ConversationValidationError('Conversation item status metadata is invalid');
265
+ }
266
+ const item = {
267
+ id: raw.id,
268
+ sessionId,
269
+ status: raw.status,
270
+ ...(raw.sourceCreatedAt === undefined ? {} : { sourceCreatedAt: raw.sourceCreatedAt }),
271
+ ...draft,
272
+ ...(error === undefined ? {} : { error }),
273
+ ...(truncation === undefined ? {} : { truncation }),
274
+ };
275
+ if (Buffer.byteLength(JSON.stringify(item)) > MAX_ITEM_BYTES) {
276
+ throw new ConversationValidationError('Conversation item exceeds the size limit');
277
+ }
278
+ return item;
279
+ }
280
+ function sameIdentity(first, second) {
281
+ if (first.kind !== second.kind)
282
+ return false;
283
+ if (first.kind === 'message' && second.kind === 'message')
284
+ return first.role === second.role;
285
+ if (first.kind === 'tool_call' && second.kind === 'tool_call')
286
+ return first.callId === second.callId;
287
+ if (first.kind === 'tool_result' && second.kind === 'tool_result')
288
+ return first.callId === second.callId;
289
+ if (first.kind === 'interrupt' && second.kind === 'interrupt')
290
+ return first.actor === second.actor;
291
+ return true;
292
+ }
293
+ export function normalizeConversationDelta(raw, agentId, current) {
294
+ if (!isRecord(raw) || (raw.op !== 'text.append' && raw.op !== 'item.replace')) {
295
+ throw new ConversationValidationError('Invalid Conversation delta');
296
+ }
297
+ if (raw.op === 'item.replace') {
298
+ const next = normalizeConversationDraft(raw.draft, agentId);
299
+ if (!sameIdentity(current, next)) {
300
+ throw new ConversationValidationError('Conversation delta cannot change provisional identity');
301
+ }
302
+ return { delta: { op: 'item.replace', draft: next }, next };
303
+ }
304
+ if (!text(raw.text) || !['message.content', 'reasoning_summary.text', 'tool_result.content']
305
+ .includes(String(raw.target))
306
+ || (raw.blockIndex !== undefined
307
+ && (!Number.isSafeInteger(raw.blockIndex) || Number(raw.blockIndex) < 0))) {
308
+ throw new ConversationValidationError('Invalid Conversation text delta');
309
+ }
310
+ const next = cloneJson(current);
311
+ if (raw.target === 'reasoning_summary.text' && next.kind === 'reasoning_summary') {
312
+ next.text += raw.text;
313
+ }
314
+ else if ((raw.target === 'message.content' && next.kind === 'message')
315
+ || (raw.target === 'tool_result.content' && next.kind === 'tool_result')) {
316
+ const index = raw.blockIndex === undefined ? next.content.length - 1 : Number(raw.blockIndex);
317
+ const block = next.content[index];
318
+ if (!block || block.type !== 'text') {
319
+ throw new ConversationValidationError('Conversation text delta target is not a text block');
320
+ }
321
+ block.text += raw.text;
322
+ }
323
+ else {
324
+ throw new ConversationValidationError('Conversation text delta target does not match its item');
325
+ }
326
+ const normalized = normalizeConversationDraft(next, agentId);
327
+ return {
328
+ delta: {
329
+ op: 'text.append',
330
+ target: raw.target,
331
+ ...(raw.blockIndex === undefined ? {} : { blockIndex: Number(raw.blockIndex) }),
332
+ text: raw.text,
333
+ },
334
+ next: normalized,
335
+ };
336
+ }
337
+ export function validConversationId(value) {
338
+ return typeof value === 'string' && ITEM_ID_RE.test(value);
339
+ }