@tschmz/imp 0.1.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 (377) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/LICENSE +201 -0
  3. package/README.md +21 -0
  4. package/config.example.json +143 -0
  5. package/dist/agents/default-system-prompt.d.ts +1 -0
  6. package/dist/agents/default-system-prompt.js +67 -0
  7. package/dist/agents/default-system-prompt.js.map +1 -0
  8. package/dist/agents/registry.d.ts +6 -0
  9. package/dist/agents/registry.js +12 -0
  10. package/dist/agents/registry.js.map +1 -0
  11. package/dist/application/backup-use-cases.d.ts +28 -0
  12. package/dist/application/backup-use-cases.js +571 -0
  13. package/dist/application/backup-use-cases.js.map +1 -0
  14. package/dist/application/commands/agent-command.d.ts +2 -0
  15. package/dist/application/commands/agent-command.js +61 -0
  16. package/dist/application/commands/agent-command.js.map +1 -0
  17. package/dist/application/commands/config-command.d.ts +2 -0
  18. package/dist/application/commands/config-command.js +39 -0
  19. package/dist/application/commands/config-command.js.map +1 -0
  20. package/dist/application/commands/conversation-context.d.ts +6 -0
  21. package/dist/application/commands/conversation-context.js +35 -0
  22. package/dist/application/commands/conversation-context.js.map +1 -0
  23. package/dist/application/commands/export-command.d.ts +2 -0
  24. package/dist/application/commands/export-command.js +25 -0
  25. package/dist/application/commands/export-command.js.map +1 -0
  26. package/dist/application/commands/help-command.d.ts +3 -0
  27. package/dist/application/commands/help-command.js +40 -0
  28. package/dist/application/commands/help-command.js.map +1 -0
  29. package/dist/application/commands/history-command.d.ts +2 -0
  30. package/dist/application/commands/history-command.js +23 -0
  31. package/dist/application/commands/history-command.js.map +1 -0
  32. package/dist/application/commands/logs-command.d.ts +2 -0
  33. package/dist/application/commands/logs-command.js +42 -0
  34. package/dist/application/commands/logs-command.js.map +1 -0
  35. package/dist/application/commands/new-command.d.ts +2 -0
  36. package/dist/application/commands/new-command.js +37 -0
  37. package/dist/application/commands/new-command.js.map +1 -0
  38. package/dist/application/commands/parse-inbound-command.d.ts +15 -0
  39. package/dist/application/commands/parse-inbound-command.js +33 -0
  40. package/dist/application/commands/parse-inbound-command.js.map +1 -0
  41. package/dist/application/commands/ping-command.d.ts +2 -0
  42. package/dist/application/commands/ping-command.js +18 -0
  43. package/dist/application/commands/ping-command.js.map +1 -0
  44. package/dist/application/commands/priority-inbound-commands.d.ts +2 -0
  45. package/dist/application/commands/priority-inbound-commands.js +14 -0
  46. package/dist/application/commands/priority-inbound-commands.js.map +1 -0
  47. package/dist/application/commands/registry.d.ts +7 -0
  48. package/dist/application/commands/registry.js +38 -0
  49. package/dist/application/commands/registry.js.map +1 -0
  50. package/dist/application/commands/reload-command.d.ts +2 -0
  51. package/dist/application/commands/reload-command.js +23 -0
  52. package/dist/application/commands/reload-command.js.map +1 -0
  53. package/dist/application/commands/rename-command.d.ts +2 -0
  54. package/dist/application/commands/rename-command.js +36 -0
  55. package/dist/application/commands/rename-command.js.map +1 -0
  56. package/dist/application/commands/renderers.d.ts +13 -0
  57. package/dist/application/commands/renderers.js +238 -0
  58. package/dist/application/commands/renderers.js.map +1 -0
  59. package/dist/application/commands/reset-command.d.ts +2 -0
  60. package/dist/application/commands/reset-command.js +36 -0
  61. package/dist/application/commands/reset-command.js.map +1 -0
  62. package/dist/application/commands/restart-command.d.ts +2 -0
  63. package/dist/application/commands/restart-command.js +23 -0
  64. package/dist/application/commands/restart-command.js.map +1 -0
  65. package/dist/application/commands/restore-command.d.ts +2 -0
  66. package/dist/application/commands/restore-command.js +52 -0
  67. package/dist/application/commands/restore-command.js.map +1 -0
  68. package/dist/application/commands/status-command.d.ts +2 -0
  69. package/dist/application/commands/status-command.js +24 -0
  70. package/dist/application/commands/status-command.js.map +1 -0
  71. package/dist/application/commands/types.d.ts +49 -0
  72. package/dist/application/commands/types.js +2 -0
  73. package/dist/application/commands/types.js.map +1 -0
  74. package/dist/application/commands/utils.d.ts +4 -0
  75. package/dist/application/commands/utils.js +26 -0
  76. package/dist/application/commands/utils.js.map +1 -0
  77. package/dist/application/commands/whoami-command.d.ts +2 -0
  78. package/dist/application/commands/whoami-command.js +27 -0
  79. package/dist/application/commands/whoami-command.js.map +1 -0
  80. package/dist/application/config-key-path.d.ts +2 -0
  81. package/dist/application/config-key-path.js +65 -0
  82. package/dist/application/config-key-path.js.map +1 -0
  83. package/dist/application/get-config-value-use-case.d.ts +4 -0
  84. package/dist/application/get-config-value-use-case.js +30 -0
  85. package/dist/application/get-config-value-use-case.js.map +1 -0
  86. package/dist/application/handle-incoming-message.d.ts +7 -0
  87. package/dist/application/handle-incoming-message.js +56 -0
  88. package/dist/application/handle-incoming-message.js.map +1 -0
  89. package/dist/application/inbound/dispatch-command.d.ts +2 -0
  90. package/dist/application/inbound/dispatch-command.js +23 -0
  91. package/dist/application/inbound/dispatch-command.js.map +1 -0
  92. package/dist/application/inbound/execute-agent.d.ts +2 -0
  93. package/dist/application/inbound/execute-agent.js +41 -0
  94. package/dist/application/inbound/execute-agent.js.map +1 -0
  95. package/dist/application/inbound/persist-conversation.d.ts +2 -0
  96. package/dist/application/inbound/persist-conversation.js +7 -0
  97. package/dist/application/inbound/persist-conversation.js.map +1 -0
  98. package/dist/application/inbound/resolve-conversation.d.ts +2 -0
  99. package/dist/application/inbound/resolve-conversation.js +19 -0
  100. package/dist/application/inbound/resolve-conversation.js.map +1 -0
  101. package/dist/application/inbound/resolve-skills.d.ts +2 -0
  102. package/dist/application/inbound/resolve-skills.js +73 -0
  103. package/dist/application/inbound/resolve-skills.js.map +1 -0
  104. package/dist/application/inbound/run-hooks-start.d.ts +2 -0
  105. package/dist/application/inbound/run-hooks-start.js +4 -0
  106. package/dist/application/inbound/run-hooks-start.js.map +1 -0
  107. package/dist/application/inbound/run-hooks-success-error.d.ts +3 -0
  108. package/dist/application/inbound/run-hooks-success-error.js +18 -0
  109. package/dist/application/inbound/run-hooks-success-error.js.map +1 -0
  110. package/dist/application/inbound/types.d.ts +21 -0
  111. package/dist/application/inbound/types.js +2 -0
  112. package/dist/application/inbound/types.js.map +1 -0
  113. package/dist/application/init-config-use-case.d.ts +5 -0
  114. package/dist/application/init-config-use-case.js +62 -0
  115. package/dist/application/init-config-use-case.js.map +1 -0
  116. package/dist/application/message-processor.d.ts +19 -0
  117. package/dist/application/message-processor.js +122 -0
  118. package/dist/application/message-processor.js.map +1 -0
  119. package/dist/application/reload-config-use-case.d.ts +15 -0
  120. package/dist/application/reload-config-use-case.js +26 -0
  121. package/dist/application/reload-config-use-case.js.map +1 -0
  122. package/dist/application/run-daemon-use-case.d.ts +27 -0
  123. package/dist/application/run-daemon-use-case.js +42 -0
  124. package/dist/application/run-daemon-use-case.js.map +1 -0
  125. package/dist/application/runtime-target.d.ts +25 -0
  126. package/dist/application/runtime-target.js +41 -0
  127. package/dist/application/runtime-target.js.map +1 -0
  128. package/dist/application/service-use-cases.d.ts +45 -0
  129. package/dist/application/service-use-cases.js +100 -0
  130. package/dist/application/service-use-cases.js.map +1 -0
  131. package/dist/application/set-config-value-use-case.d.ts +5 -0
  132. package/dist/application/set-config-value-use-case.js +87 -0
  133. package/dist/application/set-config-value-use-case.js.map +1 -0
  134. package/dist/application/validate-config-use-case.d.ts +3 -0
  135. package/dist/application/validate-config-use-case.js +14 -0
  136. package/dist/application/validate-config-use-case.js.map +1 -0
  137. package/dist/application/view-logs-use-case.d.ts +6 -0
  138. package/dist/application/view-logs-use-case.js +14 -0
  139. package/dist/application/view-logs-use-case.js.map +1 -0
  140. package/dist/cli/create-cli.d.ts +68 -0
  141. package/dist/cli/create-cli.js +188 -0
  142. package/dist/cli/create-cli.js.map +1 -0
  143. package/dist/config/default-app-config.d.ts +20 -0
  144. package/dist/config/default-app-config.js +138 -0
  145. package/dist/config/default-app-config.js.map +1 -0
  146. package/dist/config/discover-config-path.d.ts +10 -0
  147. package/dist/config/discover-config-path.js +71 -0
  148. package/dist/config/discover-config-path.js.map +1 -0
  149. package/dist/config/init-app-config.d.ts +13 -0
  150. package/dist/config/init-app-config.js +31 -0
  151. package/dist/config/init-app-config.js.map +1 -0
  152. package/dist/config/load-app-config.d.ts +2 -0
  153. package/dist/config/load-app-config.js +29 -0
  154. package/dist/config/load-app-config.js.map +1 -0
  155. package/dist/config/prompt-init-config.d.ts +15 -0
  156. package/dist/config/prompt-init-config.js +201 -0
  157. package/dist/config/prompt-init-config.js.map +1 -0
  158. package/dist/config/resolve-runtime-config.d.ts +8 -0
  159. package/dist/config/resolve-runtime-config.js +122 -0
  160. package/dist/config/resolve-runtime-config.js.map +1 -0
  161. package/dist/config/schema.d.ts +3 -0
  162. package/dist/config/schema.js +203 -0
  163. package/dist/config/schema.js.map +1 -0
  164. package/dist/config/secret-value.d.ts +20 -0
  165. package/dist/config/secret-value.js +69 -0
  166. package/dist/config/secret-value.js.map +1 -0
  167. package/dist/config/types.d.ts +76 -0
  168. package/dist/config/types.js +2 -0
  169. package/dist/config/types.js.map +1 -0
  170. package/dist/config/validate-secret-references.d.ts +7 -0
  171. package/dist/config/validate-secret-references.js +14 -0
  172. package/dist/config/validate-secret-references.js.map +1 -0
  173. package/dist/daemon/bootstrap/acquire-runtime-state.d.ts +6 -0
  174. package/dist/daemon/bootstrap/acquire-runtime-state.js +19 -0
  175. package/dist/daemon/bootstrap/acquire-runtime-state.js.map +1 -0
  176. package/dist/daemon/bootstrap/build-runtime-components.d.ts +21 -0
  177. package/dist/daemon/bootstrap/build-runtime-components.js +26 -0
  178. package/dist/daemon/bootstrap/build-runtime-components.js.map +1 -0
  179. package/dist/daemon/bootstrap/prepare-runtime-filesystem.d.ts +2 -0
  180. package/dist/daemon/bootstrap/prepare-runtime-filesystem.js +11 -0
  181. package/dist/daemon/bootstrap/prepare-runtime-filesystem.js.map +1 -0
  182. package/dist/daemon/create-daemon.d.ts +13 -0
  183. package/dist/daemon/create-daemon.js +98 -0
  184. package/dist/daemon/create-daemon.js.map +1 -0
  185. package/dist/daemon/runtime-bootstrap.d.ts +12 -0
  186. package/dist/daemon/runtime-bootstrap.js +24 -0
  187. package/dist/daemon/runtime-bootstrap.js.map +1 -0
  188. package/dist/daemon/runtime-runner.d.ts +18 -0
  189. package/dist/daemon/runtime-runner.js +126 -0
  190. package/dist/daemon/runtime-runner.js.map +1 -0
  191. package/dist/daemon/runtime-shutdown.d.ts +15 -0
  192. package/dist/daemon/runtime-shutdown.js +50 -0
  193. package/dist/daemon/runtime-shutdown.js.map +1 -0
  194. package/dist/daemon/runtime-state.d.ts +10 -0
  195. package/dist/daemon/runtime-state.js +90 -0
  196. package/dist/daemon/runtime-state.js.map +1 -0
  197. package/dist/daemon/types.d.ts +63 -0
  198. package/dist/daemon/types.js +2 -0
  199. package/dist/daemon/types.js.map +1 -0
  200. package/dist/domain/agent.d.ts +51 -0
  201. package/dist/domain/agent.js +2 -0
  202. package/dist/domain/agent.js.map +1 -0
  203. package/dist/domain/conversation.d.ts +51 -0
  204. package/dist/domain/conversation.js +2 -0
  205. package/dist/domain/conversation.js.map +1 -0
  206. package/dist/domain/errors.d.ts +13 -0
  207. package/dist/domain/errors.js +27 -0
  208. package/dist/domain/errors.js.map +1 -0
  209. package/dist/domain/message.d.ts +27 -0
  210. package/dist/domain/message.js +2 -0
  211. package/dist/domain/message.js.map +1 -0
  212. package/dist/files/backup.d.ts +2 -0
  213. package/dist/files/backup.js +7 -0
  214. package/dist/files/backup.js.map +1 -0
  215. package/dist/files/managed-file.d.ts +11 -0
  216. package/dist/files/managed-file.js +82 -0
  217. package/dist/files/managed-file.js.map +1 -0
  218. package/dist/files/tar-archive.d.ts +7 -0
  219. package/dist/files/tar-archive.js +308 -0
  220. package/dist/files/tar-archive.js.map +1 -0
  221. package/dist/index.d.ts +2 -0
  222. package/dist/index.js +2 -0
  223. package/dist/index.js.map +1 -0
  224. package/dist/logging/daemon-startup-failure-reporter.d.ts +2 -0
  225. package/dist/logging/daemon-startup-failure-reporter.js +19 -0
  226. package/dist/logging/daemon-startup-failure-reporter.js.map +1 -0
  227. package/dist/logging/file-logger.d.ts +3 -0
  228. package/dist/logging/file-logger.js +87 -0
  229. package/dist/logging/file-logger.js.map +1 -0
  230. package/dist/logging/types.d.ts +34 -0
  231. package/dist/logging/types.js +2 -0
  232. package/dist/logging/types.js.map +1 -0
  233. package/dist/logging/view-logs.d.ts +27 -0
  234. package/dist/logging/view-logs.js +171 -0
  235. package/dist/logging/view-logs.js.map +1 -0
  236. package/dist/main.d.ts +2 -0
  237. package/dist/main.js +109 -0
  238. package/dist/main.js.map +1 -0
  239. package/dist/plugins/hook-runner.d.ts +11 -0
  240. package/dist/plugins/hook-runner.js +41 -0
  241. package/dist/plugins/hook-runner.js.map +1 -0
  242. package/dist/plugins/index.d.ts +2 -0
  243. package/dist/plugins/index.js +2 -0
  244. package/dist/plugins/index.js.map +1 -0
  245. package/dist/plugins/types.d.ts +64 -0
  246. package/dist/plugins/types.js +2 -0
  247. package/dist/plugins/types.js.map +1 -0
  248. package/dist/runtime/agent-execution.d.ts +42 -0
  249. package/dist/runtime/agent-execution.js +109 -0
  250. package/dist/runtime/agent-execution.js.map +1 -0
  251. package/dist/runtime/context.d.ts +20 -0
  252. package/dist/runtime/context.js +2 -0
  253. package/dist/runtime/context.js.map +1 -0
  254. package/dist/runtime/create-oauth-api-key-resolver.d.ts +9 -0
  255. package/dist/runtime/create-oauth-api-key-resolver.js +66 -0
  256. package/dist/runtime/create-oauth-api-key-resolver.js.map +1 -0
  257. package/dist/runtime/create-pi-agent-engine.d.ts +28 -0
  258. package/dist/runtime/create-pi-agent-engine.js +150 -0
  259. package/dist/runtime/create-pi-agent-engine.js.map +1 -0
  260. package/dist/runtime/mcp-tool-cache.d.ts +13 -0
  261. package/dist/runtime/mcp-tool-cache.js +43 -0
  262. package/dist/runtime/mcp-tool-cache.js.map +1 -0
  263. package/dist/runtime/mcp-tool-runtime.d.ts +76 -0
  264. package/dist/runtime/mcp-tool-runtime.js +180 -0
  265. package/dist/runtime/mcp-tool-runtime.js.map +1 -0
  266. package/dist/runtime/message-mapping.d.ts +11 -0
  267. package/dist/runtime/message-mapping.js +129 -0
  268. package/dist/runtime/message-mapping.js.map +1 -0
  269. package/dist/runtime/model-resolution.d.ts +5 -0
  270. package/dist/runtime/model-resolution.js +13 -0
  271. package/dist/runtime/model-resolution.js.map +1 -0
  272. package/dist/runtime/pipeline/execute-agent-stage.d.ts +12 -0
  273. package/dist/runtime/pipeline/execute-agent-stage.js +25 -0
  274. package/dist/runtime/pipeline/execute-agent-stage.js.map +1 -0
  275. package/dist/runtime/pipeline/resolve-model-stage.d.ts +8 -0
  276. package/dist/runtime/pipeline/resolve-model-stage.js +9 -0
  277. package/dist/runtime/pipeline/resolve-model-stage.js.map +1 -0
  278. package/dist/runtime/pipeline/resolve-prompt-stage.d.ts +12 -0
  279. package/dist/runtime/pipeline/resolve-prompt-stage.js +30 -0
  280. package/dist/runtime/pipeline/resolve-prompt-stage.js.map +1 -0
  281. package/dist/runtime/pipeline/resolve-tools-stage.d.ts +27 -0
  282. package/dist/runtime/pipeline/resolve-tools-stage.js +46 -0
  283. package/dist/runtime/pipeline/resolve-tools-stage.js.map +1 -0
  284. package/dist/runtime/prompt-template.d.ts +54 -0
  285. package/dist/runtime/prompt-template.js +78 -0
  286. package/dist/runtime/prompt-template.js.map +1 -0
  287. package/dist/runtime/system-prompt-cache.d.ts +33 -0
  288. package/dist/runtime/system-prompt-cache.js +77 -0
  289. package/dist/runtime/system-prompt-cache.js.map +1 -0
  290. package/dist/runtime/system-prompt-resolution.d.ts +22 -0
  291. package/dist/runtime/system-prompt-resolution.js +170 -0
  292. package/dist/runtime/system-prompt-resolution.js.map +1 -0
  293. package/dist/runtime/tool-resolution.d.ts +19 -0
  294. package/dist/runtime/tool-resolution.js +281 -0
  295. package/dist/runtime/tool-resolution.js.map +1 -0
  296. package/dist/runtime/types.d.ts +23 -0
  297. package/dist/runtime/types.js +2 -0
  298. package/dist/runtime/types.js.map +1 -0
  299. package/dist/service/install-plan.d.ts +21 -0
  300. package/dist/service/install-plan.js +71 -0
  301. package/dist/service/install-plan.js.map +1 -0
  302. package/dist/service/install-service.d.ts +32 -0
  303. package/dist/service/install-service.js +80 -0
  304. package/dist/service/install-service.js.map +1 -0
  305. package/dist/service/linux-service-environment.d.ts +5 -0
  306. package/dist/service/linux-service-environment.js +61 -0
  307. package/dist/service/linux-service-environment.js.map +1 -0
  308. package/dist/service/manage-service.d.ts +14 -0
  309. package/dist/service/manage-service.js +36 -0
  310. package/dist/service/manage-service.js.map +1 -0
  311. package/dist/service/platforms/get-service-platform-adapter.d.ts +3 -0
  312. package/dist/service/platforms/get-service-platform-adapter.js +12 -0
  313. package/dist/service/platforms/get-service-platform-adapter.js.map +1 -0
  314. package/dist/service/platforms/linux-systemd-user-adapter.d.ts +2 -0
  315. package/dist/service/platforms/linux-systemd-user-adapter.js +99 -0
  316. package/dist/service/platforms/linux-systemd-user-adapter.js.map +1 -0
  317. package/dist/service/platforms/macos-launchd-adapter.d.ts +2 -0
  318. package/dist/service/platforms/macos-launchd-adapter.js +122 -0
  319. package/dist/service/platforms/macos-launchd-adapter.js.map +1 -0
  320. package/dist/service/platforms/service-platform-adapter.d.ts +35 -0
  321. package/dist/service/platforms/service-platform-adapter.js +16 -0
  322. package/dist/service/platforms/service-platform-adapter.js.map +1 -0
  323. package/dist/service/platforms/windows-winsw-adapter.d.ts +2 -0
  324. package/dist/service/platforms/windows-winsw-adapter.js +47 -0
  325. package/dist/service/platforms/windows-winsw-adapter.js.map +1 -0
  326. package/dist/service/service-error.d.ts +7 -0
  327. package/dist/service/service-error.js +45 -0
  328. package/dist/service/service-error.js.map +1 -0
  329. package/dist/service/service-installer.d.ts +8 -0
  330. package/dist/service/service-installer.js +40 -0
  331. package/dist/service/service-installer.js.map +1 -0
  332. package/dist/service/service-operation-result.d.ts +9 -0
  333. package/dist/service/service-operation-result.js +2 -0
  334. package/dist/service/service-operation-result.js.map +1 -0
  335. package/dist/service/uninstall-service.d.ts +13 -0
  336. package/dist/service/uninstall-service.js +45 -0
  337. package/dist/service/uninstall-service.js.map +1 -0
  338. package/dist/skills/discovery.d.ts +10 -0
  339. package/dist/skills/discovery.js +216 -0
  340. package/dist/skills/discovery.js.map +1 -0
  341. package/dist/skills/types.d.ts +20 -0
  342. package/dist/skills/types.js +2 -0
  343. package/dist/skills/types.js.map +1 -0
  344. package/dist/storage/fs-store.d.ts +3 -0
  345. package/dist/storage/fs-store.js +707 -0
  346. package/dist/storage/fs-store.js.map +1 -0
  347. package/dist/storage/types.d.ts +29 -0
  348. package/dist/storage/types.js +2 -0
  349. package/dist/storage/types.js.map +1 -0
  350. package/dist/tools/registry.d.ts +7 -0
  351. package/dist/tools/registry.js +18 -0
  352. package/dist/tools/registry.js.map +1 -0
  353. package/dist/tools/types.d.ts +14 -0
  354. package/dist/tools/types.js +2 -0
  355. package/dist/tools/types.js.map +1 -0
  356. package/dist/transports/builtins.d.ts +1 -0
  357. package/dist/transports/builtins.js +15 -0
  358. package/dist/transports/builtins.js.map +1 -0
  359. package/dist/transports/registry.d.ts +21 -0
  360. package/dist/transports/registry.js +28 -0
  361. package/dist/transports/registry.js.map +1 -0
  362. package/dist/transports/telegram/openai-voice-transcriber.d.ts +18 -0
  363. package/dist/transports/telegram/openai-voice-transcriber.js +75 -0
  364. package/dist/transports/telegram/openai-voice-transcriber.js.map +1 -0
  365. package/dist/transports/telegram/render-telegram-message.d.ts +2 -0
  366. package/dist/transports/telegram/render-telegram-message.js +458 -0
  367. package/dist/transports/telegram/render-telegram-message.js.map +1 -0
  368. package/dist/transports/telegram/telegram-transport.d.ts +56 -0
  369. package/dist/transports/telegram/telegram-transport.js +414 -0
  370. package/dist/transports/telegram/telegram-transport.js.map +1 -0
  371. package/dist/transports/telegram/transport-adapter.d.ts +33 -0
  372. package/dist/transports/telegram/transport-adapter.js +62 -0
  373. package/dist/transports/telegram/transport-adapter.js.map +1 -0
  374. package/dist/transports/types.d.ts +15 -0
  375. package/dist/transports/types.js +2 -0
  376. package/dist/transports/types.js.map +1 -0
  377. package/package.json +49 -0
@@ -0,0 +1,707 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { mkdir, open, readFile, readdir, rename, rm, writeFile } from "node:fs/promises";
3
+ import { dirname, join } from "node:path";
4
+ const conversationWriteQueues = new Map();
5
+ const legacySessionId = "legacy";
6
+ // The critical section is intentionally short: read latest snapshot, compute next state, atomically rename.
7
+ // A short-lived lock file guards that section across processes while the in-memory queue serializes writers locally.
8
+ const lockTtlMs = 30_000;
9
+ const lockRetryDelayMs = 25;
10
+ const lockRetryCount = 400;
11
+ const LEGACY_ASSISTANT_REPLAY_SOURCE = {
12
+ api: "legacy",
13
+ provider: "legacy",
14
+ model: "legacy",
15
+ };
16
+ const EMPTY_USAGE = {
17
+ input: 0,
18
+ output: 0,
19
+ cacheRead: 0,
20
+ cacheWrite: 0,
21
+ totalTokens: 0,
22
+ cost: {
23
+ input: 0,
24
+ output: 0,
25
+ cacheRead: 0,
26
+ cacheWrite: 0,
27
+ total: 0,
28
+ },
29
+ };
30
+ function getChatDir(conversationsDir, ref) {
31
+ return join(conversationsDir, sanitizePathSegment(ref.transport), sanitizePathSegment(ref.externalId));
32
+ }
33
+ function getSessionsDir(conversationsDir, ref) {
34
+ return join(getChatDir(conversationsDir, ref), "sessions");
35
+ }
36
+ function getSessionDir(conversationsDir, ref) {
37
+ const sessionId = requireSessionId(ref);
38
+ return join(getSessionsDir(conversationsDir, ref), sanitizePathSegment(sessionId));
39
+ }
40
+ function getSessionSnapshotPath(conversationsDir, ref) {
41
+ return join(getSessionDir(conversationsDir, ref), "conversation.json");
42
+ }
43
+ function getActiveSessionPath(conversationsDir, ref) {
44
+ return join(getChatDir(conversationsDir, ref), "active.json");
45
+ }
46
+ function getConversationLockPath(conversationsDir, ref) {
47
+ return join(getChatDir(conversationsDir, ref), "conversation.lock");
48
+ }
49
+ export function createFsConversationStore(paths) {
50
+ return {
51
+ async get(ref) {
52
+ if ("sessionId" in ref) {
53
+ return readSessionSnapshot(ref, paths.conversationsDir);
54
+ }
55
+ return withConversationWriteQueue(ref, async () => withConversationLock(paths.conversationsDir, ref, async () => {
56
+ await migrateLegacyConversationIfNeeded(paths.conversationsDir, ref);
57
+ const activeRef = await readActiveConversationRef(paths.conversationsDir, ref);
58
+ if (!activeRef) {
59
+ return undefined;
60
+ }
61
+ return readSessionSnapshot(activeRef, paths.conversationsDir);
62
+ }));
63
+ },
64
+ async put(context) {
65
+ await withConversationWriteQueue(context.state.conversation, async () => {
66
+ await withConversationLock(paths.conversationsDir, context.state.conversation, async () => {
67
+ const snapshotPath = getSessionSnapshotPath(paths.conversationsDir, context.state.conversation);
68
+ const current = await readSessionSnapshot(context.state.conversation, paths.conversationsDir);
69
+ const nextSnapshot = resolveNextSnapshot(normalizeSnapshot(context), current);
70
+ const tempPath = `${snapshotPath}.${process.pid}.tmp`;
71
+ await mkdir(dirname(snapshotPath), { recursive: true });
72
+ await writeFile(tempPath, JSON.stringify(nextSnapshot, null, 2));
73
+ await rename(tempPath, snapshotPath);
74
+ });
75
+ });
76
+ },
77
+ async listBackups(ref) {
78
+ return withConversationWriteQueue(ref, async () => withConversationLock(paths.conversationsDir, ref, async () => {
79
+ await migrateLegacyConversationIfNeeded(paths.conversationsDir, ref);
80
+ const activeRef = await readActiveConversationRef(paths.conversationsDir, ref);
81
+ return readInactiveSessions(paths.conversationsDir, ref, activeRef?.sessionId);
82
+ }));
83
+ },
84
+ async restore(ref, backupId) {
85
+ return withConversationWriteQueue(ref, async () => withConversationLock(paths.conversationsDir, ref, async () => {
86
+ await migrateLegacyConversationIfNeeded(paths.conversationsDir, ref);
87
+ const summaries = await readInactiveSessions(paths.conversationsDir, ref);
88
+ const selected = summaries.find((summary) => summary.id === backupId);
89
+ if (!selected) {
90
+ return false;
91
+ }
92
+ await writeActiveConversationRef(paths.conversationsDir, {
93
+ transport: ref.transport,
94
+ externalId: ref.externalId,
95
+ sessionId: selected.sessionId,
96
+ });
97
+ return true;
98
+ }));
99
+ },
100
+ async create(ref, options) {
101
+ return withConversationWriteQueue(ref, async () => withConversationLock(paths.conversationsDir, ref, async () => {
102
+ await migrateLegacyConversationIfNeeded(paths.conversationsDir, ref);
103
+ const context = createEmptyConversationContext(ref, options);
104
+ await writeConversationSnapshot(paths.conversationsDir, context);
105
+ await writeActiveConversationRef(paths.conversationsDir, context.state.conversation);
106
+ return context;
107
+ }));
108
+ },
109
+ async ensureActive(ref, options) {
110
+ return withConversationWriteQueue(ref, async () => withConversationLock(paths.conversationsDir, ref, async () => {
111
+ await migrateLegacyConversationIfNeeded(paths.conversationsDir, ref);
112
+ const activeRef = await readActiveConversationRef(paths.conversationsDir, ref);
113
+ if (activeRef) {
114
+ const existing = await readSessionSnapshot(activeRef, paths.conversationsDir);
115
+ if (existing) {
116
+ return existing;
117
+ }
118
+ }
119
+ const created = createEmptyConversationContext(ref, options);
120
+ await writeConversationSnapshot(paths.conversationsDir, created);
121
+ await writeActiveConversationRef(paths.conversationsDir, created.state.conversation);
122
+ return created;
123
+ }));
124
+ },
125
+ };
126
+ }
127
+ async function readInactiveSessions(conversationsDir, ref, activeSessionId) {
128
+ const sessionsDir = getSessionsDir(conversationsDir, ref);
129
+ let entries;
130
+ try {
131
+ entries = await readdir(sessionsDir, { withFileTypes: true });
132
+ }
133
+ catch (error) {
134
+ if (isMissingFile(error)) {
135
+ return [];
136
+ }
137
+ throw error;
138
+ }
139
+ const backups = await Promise.all(entries
140
+ .filter((entry) => entry.isDirectory())
141
+ .map(async (entry) => {
142
+ const sessionId = entry.name;
143
+ if (activeSessionId && sessionId === activeSessionId) {
144
+ return undefined;
145
+ }
146
+ const snapshot = await readSessionSnapshot(pathsafeRef(ref, sessionId), conversationsDir);
147
+ return snapshot ? toBackupSummary(snapshot) : undefined;
148
+ }));
149
+ return backups
150
+ .filter((backup) => backup !== undefined)
151
+ .sort((left, right) => right.updatedAt.localeCompare(left.updatedAt));
152
+ }
153
+ function createEmptyConversationContext(ref, options) {
154
+ const conversation = {
155
+ transport: ref.transport,
156
+ externalId: ref.externalId,
157
+ sessionId: randomUUID(),
158
+ };
159
+ return normalizeSnapshot({
160
+ state: {
161
+ conversation,
162
+ agentId: options.agentId,
163
+ ...(typeof options.title === "string" ? { title: options.title } : {}),
164
+ createdAt: options.now,
165
+ updatedAt: options.now,
166
+ version: 1,
167
+ },
168
+ messages: [],
169
+ });
170
+ }
171
+ async function writeConversationSnapshot(conversationsDir, context) {
172
+ const snapshotPath = getSessionSnapshotPath(conversationsDir, context.state.conversation);
173
+ const tempPath = `${snapshotPath}.${process.pid}.tmp`;
174
+ await mkdir(dirname(snapshotPath), { recursive: true });
175
+ await writeFile(tempPath, JSON.stringify(context, null, 2));
176
+ await rename(tempPath, snapshotPath);
177
+ }
178
+ function pathsafeRef(ref, sessionId) {
179
+ return {
180
+ transport: ref.transport,
181
+ externalId: ref.externalId,
182
+ sessionId,
183
+ };
184
+ }
185
+ function toBackupSummary(snapshot) {
186
+ const sessionId = snapshot.state.conversation.sessionId ?? legacySessionId;
187
+ return {
188
+ id: sessionId,
189
+ sessionId,
190
+ ...(snapshot.state.title ? { title: snapshot.state.title } : {}),
191
+ createdAt: snapshot.state.createdAt,
192
+ updatedAt: snapshot.state.updatedAt,
193
+ agentId: snapshot.state.agentId,
194
+ messageCount: snapshot.messages.length,
195
+ ...(snapshot.state.workingDirectory
196
+ ? { workingDirectory: snapshot.state.workingDirectory }
197
+ : {}),
198
+ };
199
+ }
200
+ async function readSessionSnapshot(ref, conversationsDir) {
201
+ const snapshotPath = getSessionSnapshotPath(conversationsDir, ref);
202
+ try {
203
+ const raw = await readFile(snapshotPath, "utf8");
204
+ return normalizeSnapshot(JSON.parse(raw));
205
+ }
206
+ catch (error) {
207
+ if (isMissingFile(error)) {
208
+ return undefined;
209
+ }
210
+ throw error;
211
+ }
212
+ }
213
+ async function readActiveConversationRef(conversationsDir, ref) {
214
+ const activePath = getActiveSessionPath(conversationsDir, ref);
215
+ try {
216
+ const raw = await readFile(activePath, "utf8");
217
+ const parsed = JSON.parse(raw);
218
+ if (typeof parsed.sessionId !== "string" || parsed.sessionId.length === 0) {
219
+ return undefined;
220
+ }
221
+ return {
222
+ transport: ref.transport,
223
+ externalId: ref.externalId,
224
+ sessionId: parsed.sessionId,
225
+ };
226
+ }
227
+ catch (error) {
228
+ if (isMissingFile(error)) {
229
+ return undefined;
230
+ }
231
+ throw error;
232
+ }
233
+ }
234
+ async function writeActiveConversationRef(conversationsDir, ref) {
235
+ const activePath = getActiveSessionPath(conversationsDir, ref);
236
+ const tempPath = `${activePath}.${process.pid}.tmp`;
237
+ await mkdir(dirname(activePath), { recursive: true });
238
+ await writeFile(tempPath, `${JSON.stringify({ sessionId: ref.sessionId }, null, 2)}\n`, "utf8");
239
+ await rename(tempPath, activePath);
240
+ }
241
+ async function migrateLegacyConversationIfNeeded(conversationsDir, ref) {
242
+ const activeRef = await readActiveConversationRef(conversationsDir, ref);
243
+ if (activeRef) {
244
+ return;
245
+ }
246
+ const legacy = await readLegacyConversation(conversationsDir, ref);
247
+ if (!legacy) {
248
+ return;
249
+ }
250
+ const migratedRef = {
251
+ transport: ref.transport,
252
+ externalId: ref.externalId,
253
+ sessionId: legacySessionId,
254
+ };
255
+ const migrated = normalizeSnapshot({
256
+ ...legacy,
257
+ state: {
258
+ ...legacy.state,
259
+ conversation: migratedRef,
260
+ },
261
+ });
262
+ const snapshotPath = getSessionSnapshotPath(conversationsDir, migratedRef);
263
+ const tempPath = `${snapshotPath}.${process.pid}.tmp`;
264
+ await mkdir(dirname(snapshotPath), { recursive: true });
265
+ await writeFile(tempPath, JSON.stringify(migrated, null, 2));
266
+ await rename(tempPath, snapshotPath);
267
+ await writeActiveConversationRef(conversationsDir, migratedRef);
268
+ }
269
+ async function readLegacyConversation(conversationsDir, ref) {
270
+ const snapshotPath = join(getChatDir(conversationsDir, ref), "conversation.json");
271
+ try {
272
+ const raw = await readFile(snapshotPath, "utf8");
273
+ return normalizeSnapshot(JSON.parse(raw));
274
+ }
275
+ catch (error) {
276
+ if (!isMissingFile(error)) {
277
+ throw error;
278
+ }
279
+ }
280
+ const statePath = join(getChatDir(conversationsDir, ref), "meta.json");
281
+ try {
282
+ const raw = await readFile(statePath, "utf8");
283
+ const state = JSON.parse(raw);
284
+ const messages = await readLegacyMessages(conversationsDir, ref);
285
+ return normalizeSnapshot({
286
+ state: {
287
+ conversation: {
288
+ transport: ref.transport,
289
+ externalId: ref.externalId,
290
+ sessionId: legacySessionId,
291
+ },
292
+ agentId: state.agentId,
293
+ ...(typeof state.title === "string" ? { title: state.title } : {}),
294
+ ...(typeof state.workingDirectory === "string"
295
+ ? { workingDirectory: state.workingDirectory }
296
+ : {}),
297
+ createdAt: state.createdAt,
298
+ updatedAt: state.updatedAt,
299
+ version: state.version,
300
+ },
301
+ messages,
302
+ });
303
+ }
304
+ catch (error) {
305
+ if (isMissingFile(error)) {
306
+ return undefined;
307
+ }
308
+ throw error;
309
+ }
310
+ }
311
+ async function readLegacyMessages(conversationsDir, ref) {
312
+ const path = join(getChatDir(conversationsDir, ref), "messages.json");
313
+ try {
314
+ const raw = await readFile(path, "utf8");
315
+ return JSON.parse(raw).map((message) => normalizeConversationEvent(message));
316
+ }
317
+ catch (error) {
318
+ if (isMissingFile(error)) {
319
+ return [];
320
+ }
321
+ throw error;
322
+ }
323
+ }
324
+ function normalizeSnapshot(snapshot) {
325
+ return {
326
+ ...snapshot,
327
+ state: {
328
+ ...snapshot.state,
329
+ version: snapshot.state.version ?? 0,
330
+ },
331
+ messages: snapshot.messages.map((message) => normalizeConversationEvent(message)),
332
+ };
333
+ }
334
+ function resolveNextSnapshot(incoming, current) {
335
+ const incomingVersion = getSnapshotVersion(incoming);
336
+ if (!current) {
337
+ if (incomingVersion !== 0 && incomingVersion !== 1) {
338
+ throw new Error("Conversation write conflict: version mismatch");
339
+ }
340
+ return {
341
+ ...incoming,
342
+ state: {
343
+ ...incoming.state,
344
+ version: incomingVersion === 0 ? 1 : incomingVersion,
345
+ },
346
+ };
347
+ }
348
+ const currentVersion = getSnapshotVersion(current);
349
+ if (incomingVersion > currentVersion) {
350
+ throw new Error("Conversation write conflict: version mismatch");
351
+ }
352
+ if (incomingVersion === currentVersion) {
353
+ return {
354
+ ...incoming,
355
+ state: {
356
+ ...incoming.state,
357
+ version: currentVersion + 1,
358
+ },
359
+ };
360
+ }
361
+ const mergedMessages = mergeConversationMessages(current.messages, incoming.messages);
362
+ if (mergedMessages.length === current.messages.length) {
363
+ throw new Error("Conversation write conflict: version mismatch");
364
+ }
365
+ return {
366
+ ...current,
367
+ messages: mergedMessages,
368
+ state: {
369
+ ...current.state,
370
+ updatedAt: pickLatestTimestamp(current.state.updatedAt, incoming.state.updatedAt),
371
+ version: currentVersion + 1,
372
+ },
373
+ };
374
+ }
375
+ function getSnapshotVersion(snapshot) {
376
+ return snapshot.state.version ?? 0;
377
+ }
378
+ function mergeConversationMessages(currentMessages, incomingMessages) {
379
+ const mergedMessages = [...currentMessages];
380
+ const currentMessagesById = new Map(currentMessages.map((message) => [message.id, message]));
381
+ for (const message of incomingMessages) {
382
+ const existing = currentMessagesById.get(message.id);
383
+ if (!existing) {
384
+ mergedMessages.push(message);
385
+ currentMessagesById.set(message.id, message);
386
+ continue;
387
+ }
388
+ if (!areMessagesEqual(existing, message)) {
389
+ throw new Error(`Conversation write conflict: message "${message.id}" diverged`);
390
+ }
391
+ }
392
+ return mergedMessages;
393
+ }
394
+ function areMessagesEqual(left, right) {
395
+ return JSON.stringify(left) === JSON.stringify(right);
396
+ }
397
+ function normalizeConversationEvent(message) {
398
+ if (isLegacyToolCallEvent(message)) {
399
+ return normalizeLegacyToolCallEvent(message);
400
+ }
401
+ if (isLegacyToolResultEvent(message)) {
402
+ return normalizeLegacyToolResultEvent(message);
403
+ }
404
+ if (isLegacyUserMessage(message)) {
405
+ return normalizeLegacyUserMessage(message);
406
+ }
407
+ if (isLegacyAssistantMessage(message)) {
408
+ return normalizeLegacyAssistantMessage(message);
409
+ }
410
+ if (isConversationUserMessage(message)) {
411
+ return normalizeConversationUserMessage(message);
412
+ }
413
+ if (isConversationAssistantMessage(message)) {
414
+ return normalizeConversationAssistantMessage(message);
415
+ }
416
+ if (isConversationToolResultMessage(message)) {
417
+ return normalizeConversationToolResultMessage(message);
418
+ }
419
+ throw new Error("Unsupported conversation event shape");
420
+ }
421
+ function normalizeConversationUserMessage(message) {
422
+ return {
423
+ ...message,
424
+ kind: "message",
425
+ content: normalizeUserContent(message.content),
426
+ };
427
+ }
428
+ function normalizeConversationAssistantMessage(message) {
429
+ return {
430
+ ...message,
431
+ kind: "message",
432
+ content: message.content.map((content) => content.type === "toolCall"
433
+ ? {
434
+ ...content,
435
+ arguments: content.arguments ?? {},
436
+ }
437
+ : content),
438
+ usage: message.usage ?? EMPTY_USAGE,
439
+ stopReason: message.stopReason ?? inferAssistantStopReason(message),
440
+ timestamp: normalizeTimestamp(message.timestamp, message.createdAt),
441
+ ...(message.api ? {} : LEGACY_ASSISTANT_REPLAY_SOURCE),
442
+ ...(message.provider ? {} : { provider: LEGACY_ASSISTANT_REPLAY_SOURCE.provider }),
443
+ ...(message.model ? {} : { model: LEGACY_ASSISTANT_REPLAY_SOURCE.model }),
444
+ };
445
+ }
446
+ function normalizeConversationToolResultMessage(message) {
447
+ return {
448
+ ...message,
449
+ kind: "message",
450
+ content: normalizeToolResultContent(message.content),
451
+ timestamp: normalizeTimestamp(message.timestamp, message.createdAt),
452
+ isError: message.isError ?? false,
453
+ };
454
+ }
455
+ function normalizeLegacyUserMessage(message) {
456
+ return {
457
+ kind: "message",
458
+ id: message.id,
459
+ role: "user",
460
+ content: message.text,
461
+ createdAt: message.createdAt,
462
+ timestamp: normalizeTimestamp(undefined, message.createdAt),
463
+ ...(message.correlationId ? { correlationId: message.correlationId } : {}),
464
+ ...(message.source ? { source: message.source } : {}),
465
+ };
466
+ }
467
+ function normalizeLegacyAssistantMessage(message) {
468
+ return {
469
+ kind: "message",
470
+ id: message.id,
471
+ role: "assistant",
472
+ content: [{ type: "text", text: message.text }],
473
+ createdAt: message.createdAt,
474
+ timestamp: normalizeTimestamp(undefined, message.createdAt),
475
+ ...(message.correlationId ? { correlationId: message.correlationId } : {}),
476
+ ...LEGACY_ASSISTANT_REPLAY_SOURCE,
477
+ usage: EMPTY_USAGE,
478
+ stopReason: "stop",
479
+ };
480
+ }
481
+ function normalizeLegacyToolCallEvent(message) {
482
+ return {
483
+ kind: "message",
484
+ id: message.id,
485
+ role: "assistant",
486
+ createdAt: message.createdAt,
487
+ timestamp: normalizeTimestamp(undefined, message.createdAt),
488
+ ...(message.correlationId ? { correlationId: message.correlationId } : {}),
489
+ ...LEGACY_ASSISTANT_REPLAY_SOURCE,
490
+ usage: EMPTY_USAGE,
491
+ stopReason: "toolUse",
492
+ content: [
493
+ ...(message.text ? [{ type: "text", text: message.text }] : []),
494
+ ...message.toolCalls.map((toolCall) => ({
495
+ type: "toolCall",
496
+ id: toolCall.id,
497
+ name: toolCall.name,
498
+ arguments: toolCall.arguments ?? {},
499
+ })),
500
+ ],
501
+ };
502
+ }
503
+ function normalizeLegacyToolResultEvent(message) {
504
+ return {
505
+ kind: "message",
506
+ id: message.id,
507
+ role: "toolResult",
508
+ createdAt: message.createdAt,
509
+ timestamp: normalizeTimestamp(undefined, message.createdAt),
510
+ ...(message.correlationId ? { correlationId: message.correlationId } : {}),
511
+ toolCallId: message.toolCallId,
512
+ toolName: message.toolName,
513
+ content: normalizeToolResultContent(message.content),
514
+ ...(message.details !== undefined ? { details: message.details } : {}),
515
+ isError: message.isError ?? false,
516
+ };
517
+ }
518
+ function normalizeUserContent(content) {
519
+ if (content === undefined) {
520
+ return "";
521
+ }
522
+ return typeof content === "string" ? content : normalizeToolResultContent(content);
523
+ }
524
+ function normalizeToolResultContent(content) {
525
+ if (!Array.isArray(content)) {
526
+ return [];
527
+ }
528
+ return content;
529
+ }
530
+ function normalizeTimestamp(timestamp, createdAt) {
531
+ if (typeof timestamp === "number" && Number.isFinite(timestamp)) {
532
+ return timestamp;
533
+ }
534
+ const parsed = Date.parse(createdAt);
535
+ return Number.isNaN(parsed) ? Date.now() : parsed;
536
+ }
537
+ function inferAssistantStopReason(message) {
538
+ if (message.errorMessage !== undefined) {
539
+ return "error";
540
+ }
541
+ return message.content.some((content) => content.type === "toolCall") ? "toolUse" : "stop";
542
+ }
543
+ function isLegacyUserMessage(message) {
544
+ return hasMessageRole(message, "user") && hasProperty(message, "text");
545
+ }
546
+ function isLegacyAssistantMessage(message) {
547
+ return hasMessageRole(message, "assistant") && hasProperty(message, "text");
548
+ }
549
+ function isLegacyToolCallEvent(message) {
550
+ return hasKind(message, "tool-call");
551
+ }
552
+ function isLegacyToolResultEvent(message) {
553
+ return hasKind(message, "tool-result");
554
+ }
555
+ function isConversationUserMessage(message) {
556
+ return hasMessageRole(message, "user") && hasProperty(message, "content");
557
+ }
558
+ function isConversationAssistantMessage(message) {
559
+ return hasMessageRole(message, "assistant") && hasProperty(message, "content");
560
+ }
561
+ function isConversationToolResultMessage(message) {
562
+ return hasMessageRole(message, "toolResult") && hasProperty(message, "content");
563
+ }
564
+ function hasKind(message, kind) {
565
+ return typeof message === "object" && message !== null && "kind" in message && message.kind === kind;
566
+ }
567
+ function hasMessageRole(message, role) {
568
+ return typeof message === "object" && message !== null && "role" in message && message.role === role;
569
+ }
570
+ function hasProperty(message, property) {
571
+ return typeof message === "object" && message !== null && property in message;
572
+ }
573
+ function pickLatestTimestamp(left, right) {
574
+ const leftTime = Date.parse(left);
575
+ const rightTime = Date.parse(right);
576
+ if (Number.isNaN(leftTime)) {
577
+ return right;
578
+ }
579
+ if (Number.isNaN(rightTime)) {
580
+ return left;
581
+ }
582
+ return rightTime >= leftTime ? right : left;
583
+ }
584
+ async function withConversationWriteQueue(ref, action) {
585
+ const queueKey = getConversationKey(ref);
586
+ const previous = conversationWriteQueues.get(queueKey) ?? Promise.resolve();
587
+ let releaseQueue;
588
+ const current = new Promise((resolve) => {
589
+ releaseQueue = resolve;
590
+ });
591
+ const next = previous.catch(() => undefined).then(() => current);
592
+ conversationWriteQueues.set(queueKey, next);
593
+ await previous.catch(() => undefined);
594
+ try {
595
+ return await action();
596
+ }
597
+ finally {
598
+ releaseQueue?.();
599
+ if (conversationWriteQueues.get(queueKey) === next) {
600
+ conversationWriteQueues.delete(queueKey);
601
+ }
602
+ }
603
+ }
604
+ async function withConversationLock(conversationsDir, ref, action) {
605
+ const lockPath = getConversationLockPath(conversationsDir, ref);
606
+ await mkdir(dirname(lockPath), { recursive: true });
607
+ const release = await acquireConversationLock(lockPath);
608
+ try {
609
+ return await action();
610
+ }
611
+ finally {
612
+ await release();
613
+ }
614
+ }
615
+ async function acquireConversationLock(lockPath) {
616
+ for (let attempt = 0; attempt < lockRetryCount; attempt += 1) {
617
+ try {
618
+ const lockHandle = await open(lockPath, "wx");
619
+ const lockPayload = {
620
+ pid: process.pid,
621
+ acquiredAt: new Date().toISOString(),
622
+ expiresAt: new Date(Date.now() + lockTtlMs).toISOString(),
623
+ };
624
+ await lockHandle.writeFile(`${JSON.stringify(lockPayload, null, 2)}\n`, "utf8");
625
+ await lockHandle.close();
626
+ return async () => {
627
+ try {
628
+ await rm(lockPath);
629
+ }
630
+ catch (error) {
631
+ if (!isMissingFile(error)) {
632
+ throw error;
633
+ }
634
+ }
635
+ };
636
+ }
637
+ catch (error) {
638
+ if (!isAlreadyExistsError(error)) {
639
+ throw error;
640
+ }
641
+ if (await tryCleanupExpiredLock(lockPath)) {
642
+ continue;
643
+ }
644
+ await delay(lockRetryDelayMs);
645
+ }
646
+ }
647
+ throw new Error(`Conversation write lock timed out: ${lockPath}`);
648
+ }
649
+ async function tryCleanupExpiredLock(lockPath) {
650
+ try {
651
+ const raw = await readFile(lockPath, "utf8");
652
+ const expiresAt = parseLockExpiry(raw);
653
+ if (expiresAt !== undefined && expiresAt > Date.now()) {
654
+ return false;
655
+ }
656
+ await rm(lockPath);
657
+ return true;
658
+ }
659
+ catch (error) {
660
+ if (isMissingFile(error)) {
661
+ return true;
662
+ }
663
+ if (isAlreadyExistsError(error)) {
664
+ return false;
665
+ }
666
+ throw error;
667
+ }
668
+ }
669
+ function parseLockExpiry(raw) {
670
+ try {
671
+ const parsed = JSON.parse(raw);
672
+ if (typeof parsed.expiresAt !== "string") {
673
+ return undefined;
674
+ }
675
+ const expiresAt = Date.parse(parsed.expiresAt);
676
+ return Number.isNaN(expiresAt) ? undefined : expiresAt;
677
+ }
678
+ catch {
679
+ return undefined;
680
+ }
681
+ }
682
+ async function delay(ms) {
683
+ await new Promise((resolve) => {
684
+ setTimeout(resolve, ms);
685
+ });
686
+ }
687
+ function getConversationKey(ref) {
688
+ return "sessionId" in ref
689
+ ? `${ref.transport}/${ref.externalId}/${ref.sessionId}`
690
+ : `${ref.transport}/${ref.externalId}`;
691
+ }
692
+ function isMissingFile(error) {
693
+ return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
694
+ }
695
+ function isAlreadyExistsError(error) {
696
+ return typeof error === "object" && error !== null && "code" in error && error.code === "EEXIST";
697
+ }
698
+ function sanitizePathSegment(value) {
699
+ return value.replaceAll("/", "_");
700
+ }
701
+ function requireSessionId(ref) {
702
+ if (!ref.sessionId) {
703
+ throw new Error("Conversation sessionId is required");
704
+ }
705
+ return ref.sessionId;
706
+ }
707
+ //# sourceMappingURL=fs-store.js.map