@cydm/happy-elves 0.1.0-beta.31 → 0.1.0-beta.310

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 (342) hide show
  1. package/apps/cli/dist/commands/app.js +10 -0
  2. package/apps/cli/dist/commands/daemon.js +92 -4
  3. package/apps/cli/dist/commands/gateway.d.ts +2 -0
  4. package/apps/cli/dist/commands/gateway.js +429 -0
  5. package/apps/cli/dist/commands/lib/args.js +49 -0
  6. package/apps/cli/dist/commands/lib/bootstrap-daemon.d.ts +2 -2
  7. package/apps/cli/dist/commands/lib/bootstrap-daemon.js +2 -2
  8. package/apps/cli/dist/commands/lib/doctor.d.ts +11 -0
  9. package/apps/cli/dist/commands/lib/doctor.js +276 -11
  10. package/apps/cli/dist/commands/lib/gateway-store.d.ts +38 -0
  11. package/apps/cli/dist/commands/lib/gateway-store.js +33 -0
  12. package/apps/cli/dist/commands/lib/index.d.ts +4 -2
  13. package/apps/cli/dist/commands/lib/index.js +2 -0
  14. package/apps/cli/dist/commands/lib/json.d.ts +2 -2
  15. package/apps/cli/dist/commands/lib/json.js +5 -2
  16. package/apps/cli/dist/commands/lib/local-daemon.d.ts +1 -0
  17. package/apps/cli/dist/commands/lib/local-daemon.js +14 -0
  18. package/apps/cli/dist/commands/lib/paths.d.ts +2 -0
  19. package/apps/cli/dist/commands/lib/paths.js +2 -0
  20. package/apps/cli/dist/commands/lib/relay-db-audit.d.ts +37 -0
  21. package/apps/cli/dist/commands/lib/relay-db-audit.js +300 -0
  22. package/apps/cli/dist/commands/lib/relay-http.d.ts +1 -1
  23. package/apps/cli/dist/commands/lib/scope.d.ts +1 -1
  24. package/apps/cli/dist/commands/lib/session-output.d.ts +15 -1
  25. package/apps/cli/dist/commands/lib/session-output.js +214 -6
  26. package/apps/cli/dist/commands/lib/session-view.d.ts +2 -2
  27. package/apps/cli/dist/commands/lib/status.js +5 -1
  28. package/apps/cli/dist/commands/lib/types.d.ts +4 -2
  29. package/apps/cli/dist/commands/lib/usage.js +123 -10
  30. package/apps/cli/dist/commands/lib/workspace.d.ts +2 -2
  31. package/apps/cli/dist/commands/lib/workspace.js +63 -10
  32. package/apps/cli/dist/commands/machine.js +50 -9
  33. package/apps/cli/dist/commands/memory.d.ts +2 -0
  34. package/apps/cli/dist/commands/memory.js +179 -0
  35. package/apps/cli/dist/commands/relay.js +29 -2
  36. package/apps/cli/dist/commands/release.d.ts +2 -0
  37. package/apps/cli/dist/commands/release.js +197 -0
  38. package/apps/cli/dist/commands/remote.js +10 -2
  39. package/apps/cli/dist/commands/schedule.d.ts +2 -0
  40. package/apps/cli/dist/commands/schedule.js +273 -0
  41. package/apps/cli/dist/commands/session.js +517 -5
  42. package/apps/cli/dist/commands/turn.js +225 -9
  43. package/apps/cli/dist/commands/workspace.d.ts +2 -0
  44. package/apps/cli/dist/commands/workspace.js +68 -0
  45. package/apps/cli/package.json +6 -0
  46. package/apps/daemon/dist/gateway/adapters.d.ts +38 -0
  47. package/apps/daemon/dist/gateway/adapters.js +2 -0
  48. package/apps/daemon/dist/gateway/feishu.d.ts +65 -0
  49. package/apps/daemon/dist/gateway/feishu.js +493 -0
  50. package/apps/daemon/dist/gateway/format.d.ts +2 -0
  51. package/apps/daemon/dist/gateway/format.js +19 -0
  52. package/apps/daemon/dist/gateway/handler.d.ts +7 -0
  53. package/apps/daemon/dist/gateway/handler.js +151 -0
  54. package/apps/daemon/dist/gateway/runtime.d.ts +33 -0
  55. package/apps/daemon/dist/gateway/runtime.js +850 -0
  56. package/apps/daemon/dist/gateway/store.d.ts +92 -0
  57. package/apps/daemon/dist/gateway/store.js +268 -0
  58. package/apps/daemon/dist/memory/handler.d.ts +7 -0
  59. package/apps/daemon/dist/memory/handler.js +105 -0
  60. package/apps/daemon/dist/memory/manager.d.ts +44 -0
  61. package/apps/daemon/dist/memory/manager.js +359 -0
  62. package/apps/daemon/dist/paths.d.ts +3 -0
  63. package/apps/daemon/dist/paths.js +3 -0
  64. package/apps/daemon/dist/relay/admission.d.ts +30 -0
  65. package/apps/daemon/dist/relay/admission.js +31 -0
  66. package/apps/daemon/dist/relay/connection.js +37 -1
  67. package/apps/daemon/dist/relay/devtools.d.ts +1 -1
  68. package/apps/daemon/dist/relay/register.js +2 -0
  69. package/apps/daemon/dist/relay/send.d.ts +2 -2
  70. package/apps/daemon/dist/relay/send.js +2 -0
  71. package/apps/daemon/dist/relay-http.d.ts +1 -1
  72. package/apps/daemon/dist/runtime/external-provider.d.ts +1 -1
  73. package/apps/daemon/dist/runtime/external-provider.js +187 -17
  74. package/apps/daemon/dist/runtime.d.ts +1 -2
  75. package/apps/daemon/dist/runtime.js +0 -3
  76. package/apps/daemon/dist/schedule/handler.d.ts +9 -0
  77. package/apps/daemon/dist/schedule/handler.js +148 -0
  78. package/apps/daemon/dist/schedule/runner.d.ts +21 -0
  79. package/apps/daemon/dist/schedule/runner.js +547 -0
  80. package/apps/daemon/dist/schedule/store.d.ts +9 -0
  81. package/apps/daemon/dist/schedule/store.js +143 -0
  82. package/apps/daemon/dist/session/create-session.d.ts +1 -1
  83. package/apps/daemon/dist/session/directory.d.ts +1 -1
  84. package/apps/daemon/dist/session/events.d.ts +15 -2
  85. package/apps/daemon/dist/session/events.js +76 -24
  86. package/apps/daemon/dist/session/file-preview.d.ts +1 -1
  87. package/apps/daemon/dist/session/lifecycle.d.ts +26 -3
  88. package/apps/daemon/dist/session/lifecycle.js +161 -22
  89. package/apps/daemon/dist/session/metadata.d.ts +2 -2
  90. package/apps/daemon/dist/session/metadata.js +42 -1
  91. package/apps/daemon/dist/session/primitives.d.ts +1 -1
  92. package/apps/daemon/dist/session/primitives.js +37 -6
  93. package/apps/daemon/dist/session/prompt.d.ts +28 -1
  94. package/apps/daemon/dist/session/prompt.js +340 -5
  95. package/apps/daemon/dist/session/reconcile.d.ts +3 -0
  96. package/apps/daemon/dist/session/reconcile.js +235 -0
  97. package/apps/daemon/dist/session/runtime-handle.d.ts +1 -1
  98. package/apps/daemon/dist/start.js +5 -1
  99. package/apps/daemon/dist/state.d.ts +2 -2
  100. package/apps/daemon/dist/turn-coordinator.d.ts +1 -0
  101. package/apps/daemon/dist/turn-coordinator.js +4 -0
  102. package/apps/daemon/dist/types.d.ts +16 -1
  103. package/apps/daemon/package.json +3 -25
  104. package/apps/relay/dist/connections.d.ts +2 -0
  105. package/apps/relay/dist/controller-handlers.d.ts +42 -2
  106. package/apps/relay/dist/controller-handlers.js +349 -37
  107. package/apps/relay/dist/db.js +296 -2
  108. package/apps/relay/dist/errors.d.ts +2 -1
  109. package/apps/relay/dist/errors.js +3 -1
  110. package/apps/relay/dist/http-routes.js +659 -33
  111. package/apps/relay/dist/http-schemas.d.ts +156 -0
  112. package/apps/relay/dist/http-schemas.js +85 -0
  113. package/apps/relay/dist/index.js +31 -1
  114. package/apps/relay/dist/machine-handlers.d.ts +15 -7
  115. package/apps/relay/dist/machine-handlers.js +567 -27
  116. package/apps/relay/dist/projections.d.ts +1 -1
  117. package/apps/relay/dist/projections.js +13 -0
  118. package/apps/relay/dist/relay-context.d.ts +41 -3
  119. package/apps/relay/dist/relay-context.js +244 -19
  120. package/apps/relay/dist/repositories.d.ts +2 -2
  121. package/apps/relay/dist/repositories.js +28 -1
  122. package/apps/relay/dist/runtime-bindings.d.ts +25 -0
  123. package/apps/relay/dist/runtime-bindings.js +49 -0
  124. package/apps/relay/dist/scope.d.ts +1 -1
  125. package/apps/relay/dist/types.d.ts +71 -1
  126. package/apps/relay/dist/websocket.js +2 -0
  127. package/package.json +5 -11
  128. package/packages/agent-sdk/dist/index.d.ts +80 -0
  129. package/packages/agent-sdk/dist/index.js +16 -0
  130. package/packages/client/dist/account.d.ts +35 -2
  131. package/packages/client/dist/account.js +98 -1
  132. package/packages/client/dist/client.d.ts +59 -6
  133. package/packages/client/dist/client.js +312 -58
  134. package/packages/client/dist/index.d.ts +1 -1
  135. package/packages/client/dist/parsers.d.ts +28 -2
  136. package/packages/client/dist/parsers.js +167 -0
  137. package/packages/client/dist/transport.d.ts +21 -1
  138. package/packages/client/dist/transport.js +69 -6
  139. package/packages/client/dist/types.d.ts +177 -1
  140. package/packages/client/dist/validation.d.ts +1 -1
  141. package/packages/runtime/dist/index.d.ts +66 -0
  142. package/packages/runtime-acpx/dist/index.d.ts +1 -1
  143. package/packages/runtime-acpx/dist/index.js +8 -0
  144. package/packages/runtime-cli/dist/claude-history.d.ts +1 -2
  145. package/packages/runtime-cli/dist/claude-history.js +0 -3
  146. package/packages/runtime-cli/dist/claude-session-store.d.ts +0 -1
  147. package/packages/runtime-cli/dist/claude-session-store.js +1 -1
  148. package/packages/runtime-cli/dist/claude-turn.d.ts +2 -1
  149. package/packages/runtime-cli/dist/claude-turn.js +29 -9
  150. package/packages/runtime-cli/dist/codex-app-server.d.ts +8 -3
  151. package/packages/runtime-cli/dist/codex-app-server.js +122 -25
  152. package/packages/runtime-cli/dist/codex-history.d.ts +1 -2
  153. package/packages/runtime-cli/dist/codex-history.js +11 -5
  154. package/packages/runtime-cli/dist/codex-protocol.d.ts +2 -2
  155. package/packages/runtime-cli/dist/codex-protocol.js +4 -4
  156. package/packages/runtime-cli/dist/history-merge.d.ts +1 -1
  157. package/packages/runtime-cli/dist/index.d.ts +1 -1
  158. package/packages/runtime-cli/dist/index.js +108 -2
  159. package/packages/runtime-cli/dist/permissions.d.ts +1 -1
  160. package/packages/runtime-cli/dist/session-store.d.ts +2 -1
  161. package/packages/runtime-cli/dist/session-store.js +9 -8
  162. package/packages/shared/dist/index.d.ts +2 -0
  163. package/packages/shared/dist/index.js +2 -0
  164. package/packages/shared/dist/protocol-schemas.d.ts +1077 -0
  165. package/packages/shared/dist/protocol-schemas.js +273 -0
  166. package/packages/shared/dist/protocol-types.d.ts +371 -3
  167. package/packages/shared/dist/protocol.d.ts +181 -2
  168. package/packages/shared/dist/protocol.js +197 -1
  169. package/packages/shared/dist/session-tags.d.ts +12 -0
  170. package/packages/shared/dist/session-tags.js +50 -0
  171. package/apps/cli/dist/commands/account.js.map +0 -1
  172. package/apps/cli/dist/commands/app.js.map +0 -1
  173. package/apps/cli/dist/commands/collect.js.map +0 -1
  174. package/apps/cli/dist/commands/command.js.map +0 -1
  175. package/apps/cli/dist/commands/config.js.map +0 -1
  176. package/apps/cli/dist/commands/daemon.js.map +0 -1
  177. package/apps/cli/dist/commands/lib/args.js.map +0 -1
  178. package/apps/cli/dist/commands/lib/audit.js.map +0 -1
  179. package/apps/cli/dist/commands/lib/bootstrap-config.js.map +0 -1
  180. package/apps/cli/dist/commands/lib/bootstrap-daemon.js.map +0 -1
  181. package/apps/cli/dist/commands/lib/bootstrap-output.js.map +0 -1
  182. package/apps/cli/dist/commands/lib/bootstrap.js.map +0 -1
  183. package/apps/cli/dist/commands/lib/config.js.map +0 -1
  184. package/apps/cli/dist/commands/lib/doctor.js.map +0 -1
  185. package/apps/cli/dist/commands/lib/exit.js.map +0 -1
  186. package/apps/cli/dist/commands/lib/index.js.map +0 -1
  187. package/apps/cli/dist/commands/lib/json.js.map +0 -1
  188. package/apps/cli/dist/commands/lib/local-daemon.js.map +0 -1
  189. package/apps/cli/dist/commands/lib/loop-store.js.map +0 -1
  190. package/apps/cli/dist/commands/lib/paths.js.map +0 -1
  191. package/apps/cli/dist/commands/lib/relay-http.js.map +0 -1
  192. package/apps/cli/dist/commands/lib/scope.js.map +0 -1
  193. package/apps/cli/dist/commands/lib/session-output.js.map +0 -1
  194. package/apps/cli/dist/commands/lib/session-view.js.map +0 -1
  195. package/apps/cli/dist/commands/lib/status.js.map +0 -1
  196. package/apps/cli/dist/commands/lib/types.js.map +0 -1
  197. package/apps/cli/dist/commands/lib/usage.js.map +0 -1
  198. package/apps/cli/dist/commands/lib/workspace.js.map +0 -1
  199. package/apps/cli/dist/commands/loop.js.map +0 -1
  200. package/apps/cli/dist/commands/machine.js.map +0 -1
  201. package/apps/cli/dist/commands/relay.js.map +0 -1
  202. package/apps/cli/dist/commands/remote.js.map +0 -1
  203. package/apps/cli/dist/commands/session.js.map +0 -1
  204. package/apps/cli/dist/commands/token.js.map +0 -1
  205. package/apps/cli/dist/commands/turn.js.map +0 -1
  206. package/apps/cli/dist/errors.js.map +0 -1
  207. package/apps/cli/dist/index.js.map +0 -1
  208. package/apps/daemon/dist/app.js.map +0 -1
  209. package/apps/daemon/dist/args.js.map +0 -1
  210. package/apps/daemon/dist/audit.js.map +0 -1
  211. package/apps/daemon/dist/cli.js.map +0 -1
  212. package/apps/daemon/dist/config.js.map +0 -1
  213. package/apps/daemon/dist/errors.js.map +0 -1
  214. package/apps/daemon/dist/loop/runner.js.map +0 -1
  215. package/apps/daemon/dist/loop/store.js.map +0 -1
  216. package/apps/daemon/dist/pair.js.map +0 -1
  217. package/apps/daemon/dist/paths.js.map +0 -1
  218. package/apps/daemon/dist/process.js.map +0 -1
  219. package/apps/daemon/dist/relay/connection.js.map +0 -1
  220. package/apps/daemon/dist/relay/devtools.js.map +0 -1
  221. package/apps/daemon/dist/relay/register.js.map +0 -1
  222. package/apps/daemon/dist/relay/send.js.map +0 -1
  223. package/apps/daemon/dist/relay-http.js.map +0 -1
  224. package/apps/daemon/dist/runtime/external-provider.js.map +0 -1
  225. package/apps/daemon/dist/runtime.js.map +0 -1
  226. package/apps/daemon/dist/session/create-session.js.map +0 -1
  227. package/apps/daemon/dist/session/directory.js.map +0 -1
  228. package/apps/daemon/dist/session/events.js.map +0 -1
  229. package/apps/daemon/dist/session/file-preview.js.map +0 -1
  230. package/apps/daemon/dist/session/lifecycle.js.map +0 -1
  231. package/apps/daemon/dist/session/metadata.js.map +0 -1
  232. package/apps/daemon/dist/session/primitives.js.map +0 -1
  233. package/apps/daemon/dist/session/prompt.js.map +0 -1
  234. package/apps/daemon/dist/session/runtime-handle.js.map +0 -1
  235. package/apps/daemon/dist/start.js.map +0 -1
  236. package/apps/daemon/dist/state.js.map +0 -1
  237. package/apps/daemon/dist/turn-coordinator.js.map +0 -1
  238. package/apps/daemon/dist/types.js.map +0 -1
  239. package/apps/daemon/dist/utils.js.map +0 -1
  240. package/apps/relay/dist/auth.js.map +0 -1
  241. package/apps/relay/dist/connections.js.map +0 -1
  242. package/apps/relay/dist/controller-handlers.js.map +0 -1
  243. package/apps/relay/dist/db.js.map +0 -1
  244. package/apps/relay/dist/errors.js.map +0 -1
  245. package/apps/relay/dist/http-routes.js.map +0 -1
  246. package/apps/relay/dist/http-schemas.js.map +0 -1
  247. package/apps/relay/dist/index.js.map +0 -1
  248. package/apps/relay/dist/machine-handlers.js.map +0 -1
  249. package/apps/relay/dist/projections.js.map +0 -1
  250. package/apps/relay/dist/relay-context.js.map +0 -1
  251. package/apps/relay/dist/repositories.js.map +0 -1
  252. package/apps/relay/dist/retention.js.map +0 -1
  253. package/apps/relay/dist/scope.js.map +0 -1
  254. package/apps/relay/dist/security.js.map +0 -1
  255. package/apps/relay/dist/types.js.map +0 -1
  256. package/apps/relay/dist/websocket.js.map +0 -1
  257. package/apps/relay/package.json +0 -29
  258. package/packages/agent-sdk/dist/index.d.ts.map +0 -1
  259. package/packages/agent-sdk/dist/index.js.map +0 -1
  260. package/packages/agent-sdk/package.json +0 -20
  261. package/packages/client/dist/account.d.ts.map +0 -1
  262. package/packages/client/dist/account.js.map +0 -1
  263. package/packages/client/dist/client.d.ts.map +0 -1
  264. package/packages/client/dist/client.js.map +0 -1
  265. package/packages/client/dist/errors.d.ts.map +0 -1
  266. package/packages/client/dist/errors.js.map +0 -1
  267. package/packages/client/dist/http.d.ts.map +0 -1
  268. package/packages/client/dist/http.js.map +0 -1
  269. package/packages/client/dist/index.d.ts.map +0 -1
  270. package/packages/client/dist/index.js.map +0 -1
  271. package/packages/client/dist/parsers.d.ts.map +0 -1
  272. package/packages/client/dist/parsers.js.map +0 -1
  273. package/packages/client/dist/transport.d.ts.map +0 -1
  274. package/packages/client/dist/transport.js.map +0 -1
  275. package/packages/client/dist/types.d.ts.map +0 -1
  276. package/packages/client/dist/types.js.map +0 -1
  277. package/packages/client/dist/validation.d.ts.map +0 -1
  278. package/packages/client/dist/validation.js.map +0 -1
  279. package/packages/client/package.json +0 -22
  280. package/packages/pie-provider/dist/index.d.ts.map +0 -1
  281. package/packages/pie-provider/dist/index.js.map +0 -1
  282. package/packages/pie-provider/package.json +0 -26
  283. package/packages/runtime/dist/index.d.ts.map +0 -1
  284. package/packages/runtime/dist/index.js.map +0 -1
  285. package/packages/runtime/package.json +0 -19
  286. package/packages/runtime-acpx/dist/index.d.ts.map +0 -1
  287. package/packages/runtime-acpx/dist/index.js.map +0 -1
  288. package/packages/runtime-acpx/package.json +0 -23
  289. package/packages/runtime-cli/dist/async-queue.d.ts.map +0 -1
  290. package/packages/runtime-cli/dist/async-queue.js.map +0 -1
  291. package/packages/runtime-cli/dist/claude-history.d.ts.map +0 -1
  292. package/packages/runtime-cli/dist/claude-history.js.map +0 -1
  293. package/packages/runtime-cli/dist/claude-json.d.ts.map +0 -1
  294. package/packages/runtime-cli/dist/claude-json.js.map +0 -1
  295. package/packages/runtime-cli/dist/claude-rename.d.ts.map +0 -1
  296. package/packages/runtime-cli/dist/claude-rename.js.map +0 -1
  297. package/packages/runtime-cli/dist/claude-session-store.d.ts.map +0 -1
  298. package/packages/runtime-cli/dist/claude-session-store.js.map +0 -1
  299. package/packages/runtime-cli/dist/claude-turn.d.ts.map +0 -1
  300. package/packages/runtime-cli/dist/claude-turn.js.map +0 -1
  301. package/packages/runtime-cli/dist/codex-app-server.d.ts.map +0 -1
  302. package/packages/runtime-cli/dist/codex-app-server.js.map +0 -1
  303. package/packages/runtime-cli/dist/codex-approvals.d.ts.map +0 -1
  304. package/packages/runtime-cli/dist/codex-approvals.js.map +0 -1
  305. package/packages/runtime-cli/dist/codex-history.d.ts.map +0 -1
  306. package/packages/runtime-cli/dist/codex-history.js.map +0 -1
  307. package/packages/runtime-cli/dist/codex-json-rpc.d.ts.map +0 -1
  308. package/packages/runtime-cli/dist/codex-json-rpc.js.map +0 -1
  309. package/packages/runtime-cli/dist/codex-lifecycle.d.ts.map +0 -1
  310. package/packages/runtime-cli/dist/codex-lifecycle.js.map +0 -1
  311. package/packages/runtime-cli/dist/codex-protocol.d.ts.map +0 -1
  312. package/packages/runtime-cli/dist/codex-protocol.js.map +0 -1
  313. package/packages/runtime-cli/dist/command-resolver.d.ts.map +0 -1
  314. package/packages/runtime-cli/dist/command-resolver.js.map +0 -1
  315. package/packages/runtime-cli/dist/history-merge.d.ts.map +0 -1
  316. package/packages/runtime-cli/dist/history-merge.js.map +0 -1
  317. package/packages/runtime-cli/dist/index.d.ts.map +0 -1
  318. package/packages/runtime-cli/dist/index.js.map +0 -1
  319. package/packages/runtime-cli/dist/permissions.d.ts.map +0 -1
  320. package/packages/runtime-cli/dist/permissions.js.map +0 -1
  321. package/packages/runtime-cli/dist/session-store.d.ts.map +0 -1
  322. package/packages/runtime-cli/dist/session-store.js.map +0 -1
  323. package/packages/runtime-cli/package.json +0 -22
  324. package/packages/shared/dist/args.d.ts.map +0 -1
  325. package/packages/shared/dist/args.js.map +0 -1
  326. package/packages/shared/dist/crypto.d.ts.map +0 -1
  327. package/packages/shared/dist/crypto.js.map +0 -1
  328. package/packages/shared/dist/ids.d.ts.map +0 -1
  329. package/packages/shared/dist/ids.js.map +0 -1
  330. package/packages/shared/dist/index.d.ts.map +0 -1
  331. package/packages/shared/dist/index.js.map +0 -1
  332. package/packages/shared/dist/protocol-schemas.d.ts.map +0 -1
  333. package/packages/shared/dist/protocol-schemas.js.map +0 -1
  334. package/packages/shared/dist/protocol-types.d.ts.map +0 -1
  335. package/packages/shared/dist/protocol-types.js.map +0 -1
  336. package/packages/shared/dist/protocol.d.ts.map +0 -1
  337. package/packages/shared/dist/protocol.js.map +0 -1
  338. package/packages/shared/dist/session-name.d.ts.map +0 -1
  339. package/packages/shared/dist/session-name.js.map +0 -1
  340. package/packages/shared/dist/session-state.d.ts.map +0 -1
  341. package/packages/shared/dist/session-state.js.map +0 -1
  342. package/packages/shared/package.json +0 -24
@@ -1,25 +1,9 @@
1
1
  import { z } from "zod";
2
- import { encryptedEnvelopeSchema, pairingCode, randomId, } from "../../../packages/shared/dist/index.js";
3
- import { controllerDeviceSchema, controllerInviteClaimSchema, controllerInviteCreateSchema, deviceRevokeSchema, pairingClaimSchema, pairingStartSchema, scopedTokenCreateSchema, scopedTokenRevokeSchema, sessionEventsQuerySchema, } from "./http-schemas.js";
2
+ import { controllerSessionCommandDecision, encryptedEnvelopeSchema, pairingCode, randomId, } from "../../../packages/shared/dist/index.js";
3
+ import { controllerDeviceSchema, controllerInviteClaimSchema, controllerInviteCreateSchema, deviceRenameSchema, deviceRevokeSchema, memoryDeleteSchema, memoryListQuerySchema, memorySaveSchema, memorySettingsSchema, machineMemoryDocumentSaveSchema, machineMemoryDocumentsQuerySchema, machineCreateSessionAdmissionSchema, machinePromptAdmissionSchema, pairingClaimSchema, pairingStartSchema, scopedTokenCreateSchema, scopedTokenRevokeSchema, sessionMetadataUpdateSchema, sessionHeadAdvanceSchema, sessionEventsQuerySchema, workspaceCwdPinSchema, workspaceCwdUnpinSchema, workspaceProjectPreferenceSchema, workspaceSessionRecentSchema, workspaceSessionStarSchema, } from "./http-schemas.js";
4
4
  import { HttpError } from "./errors.js";
5
5
  import { encodePairingCode, isPairingCodeMatch, tokenDigest } from "./security.js";
6
- const visibleSessionEventWhere = `
7
- NOT EXISTS (
8
- SELECT 1 FROM session_turn_aliases turn_alias
9
- WHERE turn_alias.account_id = session_events.account_id
10
- AND turn_alias.session_id = session_events.session_id
11
- AND turn_alias.machine_id = session_events.machine_id
12
- AND turn_alias.runtime_turn_id = session_events.turn_id
13
- AND turn_alias.request_turn_id <> session_events.turn_id
14
- AND (
15
- SELECT COUNT(*) FROM session_events request_events
16
- WHERE request_events.account_id = turn_alias.account_id
17
- AND request_events.session_id = turn_alias.session_id
18
- AND request_events.machine_id = turn_alias.machine_id
19
- AND request_events.turn_id = turn_alias.request_turn_id
20
- ) >= 3
21
- )
22
- `;
6
+ const visibleSessionEventWhere = "1 = 1";
23
7
  const sessionEventLogicalTime = `
24
8
  CASE
25
9
  WHEN occurred_at IS NOT NULL THEN occurred_at
@@ -91,6 +75,97 @@ function sessionEventLogicalSeqValue(row) {
91
75
  return row.turn_seq;
92
76
  return row.seq;
93
77
  }
78
+ function canonicalEventRows(context, accountId, rows) {
79
+ const cache = new Map();
80
+ const seenHistorical = new Set();
81
+ const canonicalRows = [];
82
+ for (const row of rows) {
83
+ const key = `${row.session_id}:${row.machine_id}:${row.turn_id}`;
84
+ let requestTurnId = cache.get(key);
85
+ if (requestTurnId === undefined) {
86
+ const alias = context.db
87
+ .prepare(`SELECT request_turn_id FROM session_turn_aliases
88
+ WHERE account_id = ? AND session_id = ? AND machine_id = ? AND runtime_turn_id = ?`)
89
+ .get(accountId, row.session_id, row.machine_id, row.turn_id);
90
+ requestTurnId = alias?.request_turn_id ?? null;
91
+ cache.set(key, requestTurnId);
92
+ }
93
+ const canonicalRow = requestTurnId ? { ...row, turn_id: requestTurnId } : row;
94
+ const historicalKey = canonicalHistoricalEventKey(canonicalRow);
95
+ if (historicalKey) {
96
+ if (seenHistorical.has(historicalKey))
97
+ continue;
98
+ seenHistorical.add(historicalKey);
99
+ }
100
+ canonicalRows.push(canonicalRow);
101
+ }
102
+ return canonicalRows;
103
+ }
104
+ function canonicalHistoricalEventKey(row) {
105
+ if (!row.message_id || row.turn_seq === null || row.turn_seq === undefined)
106
+ return undefined;
107
+ if (!row.message_id.startsWith("hist_") && !row.message_id.startsWith("tail_"))
108
+ return undefined;
109
+ const digestStart = row.message_id.lastIndexOf("_");
110
+ const digest = digestStart >= 0 ? row.message_id.slice(digestStart + 1) : "";
111
+ if (!digest)
112
+ return undefined;
113
+ return `${row.session_id}:${row.machine_id}:${row.turn_id}:${row.turn_seq}:${digest}`;
114
+ }
115
+ function workspaceSyncState(context, accountId) {
116
+ const stars = context.db
117
+ .prepare(`SELECT session_id, starred_at
118
+ FROM session_stars
119
+ WHERE account_id = ?
120
+ ORDER BY starred_at DESC, session_id ASC`)
121
+ .all(accountId);
122
+ const recentSessions = context.db
123
+ .prepare(`SELECT session_id, last_selected_at
124
+ FROM account_recent_sessions
125
+ WHERE account_id = ?
126
+ ORDER BY last_selected_at DESC, session_id ASC`)
127
+ .all(accountId);
128
+ const cwdPins = context.db
129
+ .prepare(`SELECT machine_id, cwd, label, pinned_at
130
+ FROM account_workspace_cwd_pins
131
+ WHERE account_id = ?
132
+ ORDER BY pinned_at DESC, machine_id ASC, cwd ASC`)
133
+ .all(accountId);
134
+ const projectPreferences = context.db
135
+ .prepare(`SELECT project_key, machine_id, cwd, agent, memory_mode, updated_at
136
+ FROM account_project_preferences
137
+ WHERE account_id = ?
138
+ ORDER BY updated_at DESC, project_key ASC`)
139
+ .all(accountId);
140
+ return {
141
+ stars: stars.map((row) => ({ sessionId: row.session_id, starredAt: row.starred_at })),
142
+ recentSessions: recentSessions.map((row) => ({ sessionId: row.session_id, lastSelectedAt: row.last_selected_at })),
143
+ cwdPins: cwdPins.map((row) => ({
144
+ machineId: row.machine_id,
145
+ cwd: row.cwd,
146
+ ...(row.label ? { label: row.label } : {}),
147
+ pinnedAt: row.pinned_at,
148
+ })),
149
+ projectPreferences: projectPreferences.map((row) => ({
150
+ projectKey: row.project_key,
151
+ ...(row.machine_id ? { machineId: row.machine_id } : {}),
152
+ ...(row.cwd ? { cwd: row.cwd } : {}),
153
+ ...(row.agent ? { agent: row.agent } : {}),
154
+ ...(isMemoryMode(row.memory_mode) ? { memoryMode: row.memory_mode } : {}),
155
+ updatedAt: row.updated_at,
156
+ })),
157
+ };
158
+ }
159
+ function isMemoryMode(value) {
160
+ return value === "off" || value === "readonly" || value === "readwrite";
161
+ }
162
+ function assertWorkspaceSession(context, accountId, sessionId) {
163
+ const row = context.db
164
+ .prepare("SELECT id FROM sessions WHERE account_id = ? AND id = ?")
165
+ .get(accountId, sessionId);
166
+ if (!row)
167
+ throw new HttpError(404, "SESSION_NOT_FOUND", "Session not found");
168
+ }
94
169
  export function registerHttpRoutes(app, context, controllerInviteTtlMs) {
95
170
  app.get("/health", async () => ({ ok: true, service: "happy-elves-relay" }));
96
171
  app.post("/api/pairing/start", async (request) => {
@@ -171,6 +246,159 @@ export function registerHttpRoutes(app, context, controllerInviteTtlMs) {
171
246
  context.assertHttpScopedAction(token, "snapshot");
172
247
  return context.filterSnapshotForScope(context.snapshot(token.account_id), context.tokenScope(token));
173
248
  });
249
+ app.post("/api/machine/admissions/create-session", async (request) => {
250
+ const token = context.requireHttpToken(request.headers.authorization, "machine");
251
+ if (!token.machine_id)
252
+ throw new HttpError(403, "MACHINE_TOKEN_INVALID", "Machine token is missing machine id");
253
+ const body = machineCreateSessionAdmissionSchema.parse(request.body);
254
+ const previous = context.previousAdmissionAck(token.account_id, body.requestId);
255
+ if (previous)
256
+ return previous;
257
+ const machine = context.db
258
+ .prepare("SELECT * FROM machines WHERE id = ? AND account_id = ?")
259
+ .get(token.machine_id, token.account_id);
260
+ if (!machine)
261
+ throw new HttpError(404, "MACHINE_NOT_FOUND", "Machine not found");
262
+ const sessionId = body.sessionId ?? randomId("ses");
263
+ const existing = context.db
264
+ .prepare("SELECT * FROM sessions WHERE id = ? AND account_id = ?")
265
+ .get(sessionId, token.account_id);
266
+ if (existing)
267
+ throw new HttpError(409, "SESSION_ID_CONFLICT", "Session id already exists without matching admission");
268
+ const createdAt = context.now();
269
+ const machineCommand = {
270
+ type: "machine:createSession",
271
+ requestId: body.requestId,
272
+ sessionId,
273
+ agent: body.agent,
274
+ cwd: body.cwd,
275
+ name: body.name,
276
+ model: body.model,
277
+ encryptedMetadata: body.encryptedMetadata,
278
+ };
279
+ context.db.exec("BEGIN");
280
+ try {
281
+ context.db.prepare(`INSERT INTO sessions
282
+ (id, account_id, machine_id, agent, cwd, name, status, encrypted_metadata, created_at, updated_at)
283
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(sessionId, token.account_id, token.machine_id, body.agent, body.cwd, body.name, "new", body.encryptedMetadata ? JSON.stringify(body.encryptedMetadata) : null, createdAt, createdAt);
284
+ context.db.prepare(`INSERT INTO relay_admissions
285
+ (account_id, request_id, session_id, machine_id, kind, turn_id, admission, dispatch, created_at, updated_at)
286
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(token.account_id, body.requestId, sessionId, token.machine_id, "createSession", null, "accepted", "queued", createdAt, createdAt);
287
+ context.db.prepare(`INSERT INTO relay_dispatch_outbox
288
+ (account_id, request_id, machine_id, session_id, command_type, message, state, created_at, updated_at)
289
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(token.account_id, body.requestId, token.machine_id, sessionId, machineCommand.type, JSON.stringify(machineCommand), "queued", createdAt, createdAt);
290
+ context.db.exec("COMMIT");
291
+ }
292
+ catch (error) {
293
+ context.db.exec("ROLLBACK");
294
+ throw error;
295
+ }
296
+ const sessionRow = context.db.prepare("SELECT * FROM sessions WHERE id = ?").get(sessionId);
297
+ if (sessionRow)
298
+ context.broadcastControllers(token.account_id, { type: "server:session", session: context.sessionSnapshot(sessionRow) });
299
+ const dispatch = context.deliverOutboxCommand(token.account_id, body.requestId);
300
+ return {
301
+ type: "server:ack",
302
+ requestId: body.requestId,
303
+ ok: true,
304
+ sessionId,
305
+ admission: "accepted",
306
+ dispatch,
307
+ };
308
+ });
309
+ app.post("/api/machine/admissions/prompt", async (request) => {
310
+ const token = context.requireHttpToken(request.headers.authorization, "machine");
311
+ if (!token.machine_id)
312
+ throw new HttpError(403, "MACHINE_TOKEN_INVALID", "Machine token is missing machine id");
313
+ const body = machinePromptAdmissionSchema.parse(request.body);
314
+ const previous = context.previousAdmissionAck(token.account_id, body.requestId);
315
+ if (previous)
316
+ return previous;
317
+ const sessionRow = context.db
318
+ .prepare("SELECT * FROM sessions WHERE id = ? AND account_id = ? AND machine_id = ?")
319
+ .get(body.sessionId, token.account_id, token.machine_id);
320
+ if (!sessionRow)
321
+ throw new HttpError(404, "SESSION_NOT_FOUND", "Session not found");
322
+ const decision = controllerSessionCommandDecision(sessionRow.status, "prompt");
323
+ if (!decision.allowed &&
324
+ !(decision.code === "SESSION_BUSY" &&
325
+ !context.activeSessionClaim(token.account_id, sessionRow.id) &&
326
+ !sessionLooksTranscriptPending(context, sessionRow))) {
327
+ throw new HttpError(409, decision.code, decision.message);
328
+ }
329
+ const createdAt = context.now();
330
+ const machineCommand = {
331
+ type: "machine:prompt",
332
+ requestId: body.requestId,
333
+ sessionId: sessionRow.id,
334
+ turnId: body.turnId,
335
+ encryptedPrompt: body.encryptedPrompt,
336
+ encryptedMetadata: sessionRow.encrypted_metadata ? context.parseJson(sessionRow.encrypted_metadata, undefined) : undefined,
337
+ permissionMode: body.permissionMode,
338
+ model: body.model,
339
+ encryptedMemoryOptions: body.encryptedMemoryOptions,
340
+ timeoutMs: body.timeoutMs,
341
+ };
342
+ let event;
343
+ let busyClaim;
344
+ context.db.exec("BEGIN");
345
+ try {
346
+ const claim = context.claimSessionTurn({
347
+ accountId: token.account_id,
348
+ sessionId: sessionRow.id,
349
+ machineId: token.machine_id,
350
+ requestId: body.requestId,
351
+ turnId: body.turnId,
352
+ commandType: "prompt",
353
+ createdAt,
354
+ });
355
+ if (!claim.ok) {
356
+ busyClaim = claim.claim;
357
+ context.db.exec("ROLLBACK");
358
+ }
359
+ else {
360
+ event = context.insertEvent({
361
+ accountId: token.account_id,
362
+ sessionId: sessionRow.id,
363
+ machineId: token.machine_id,
364
+ source: "controller",
365
+ turnId: body.turnId,
366
+ payload: body.encryptedPrompt,
367
+ });
368
+ context.db.prepare(`INSERT INTO relay_admissions
369
+ (account_id, request_id, session_id, machine_id, kind, turn_id, admission, dispatch, created_at, updated_at)
370
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(token.account_id, body.requestId, sessionRow.id, token.machine_id, "prompt", body.turnId, "accepted", "queued", createdAt, createdAt);
371
+ context.db.prepare(`INSERT INTO relay_dispatch_outbox
372
+ (account_id, request_id, machine_id, session_id, command_type, message, state, created_at, updated_at)
373
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(token.account_id, body.requestId, token.machine_id, sessionRow.id, machineCommand.type, JSON.stringify(machineCommand), "queued", createdAt, createdAt);
374
+ context.db.exec("COMMIT");
375
+ }
376
+ }
377
+ catch (error) {
378
+ context.db.exec("ROLLBACK");
379
+ throw error;
380
+ }
381
+ if (busyClaim) {
382
+ throw new HttpError(409, "SESSION_BUSY", "Session already has a running turn", {
383
+ sessionId: sessionRow.id,
384
+ activeRequestId: busyClaim.request_id,
385
+ activeTurnId: busyClaim.turn_id,
386
+ });
387
+ }
388
+ if (!event)
389
+ throw new HttpError(500, "ADMISSION_FAILED", "Prompt admission did not persist an event");
390
+ context.broadcastControllers(token.account_id, { type: "server:event", event });
391
+ const dispatch = context.deliverOutboxCommand(token.account_id, body.requestId);
392
+ return {
393
+ type: "server:ack",
394
+ requestId: body.requestId,
395
+ ok: true,
396
+ sessionId: sessionRow.id,
397
+ turnId: body.turnId,
398
+ admission: "accepted",
399
+ dispatch,
400
+ };
401
+ });
174
402
  app.get("/api/sessions/:sessionId/events", async (request) => {
175
403
  const token = context.requireHttpToken(request.headers.authorization, "controller");
176
404
  context.assertHttpScopedAction(token, "collect");
@@ -229,8 +457,9 @@ export function registerHttpRoutes(app, context, controllerInviteTtlMs) {
229
457
  rows = page.slice(0, limit).reverse();
230
458
  }
231
459
  const newestRow = rows.at(-1);
460
+ const canonicalRows = canonicalEventRows(context, token.account_id, rows);
232
461
  return {
233
- events: rows.map(context.eventSnapshot),
462
+ events: canonicalRows.map(context.eventSnapshot),
234
463
  hasMoreBefore,
235
464
  oldestCursor: rows[0] ? encodeSessionEventCursor(params.sessionId, rows[0]) : undefined,
236
465
  newestCursor: newestRow ? encodeSessionEventCursor(params.sessionId, newestRow) : undefined,
@@ -238,25 +467,359 @@ export function registerHttpRoutes(app, context, controllerInviteTtlMs) {
238
467
  newestEventId: newestRow?.id,
239
468
  };
240
469
  });
470
+ app.post("/api/sessions/:sessionId/head", async (request) => {
471
+ const token = context.requireHttpToken(request.headers.authorization, "controller");
472
+ context.assertHttpScopedAction(token, "session.repairHead");
473
+ const params = z.object({ sessionId: z.string().min(1) }).parse(request.params);
474
+ const body = sessionHeadAdvanceSchema.parse(request.body);
475
+ context.assertScopedSession(context.tokenScope(token), token.account_id, params.sessionId);
476
+ const session = context.db
477
+ .prepare("SELECT * FROM sessions WHERE account_id = ? AND id = ?")
478
+ .get(token.account_id, params.sessionId);
479
+ if (!session)
480
+ throw new HttpError(404, "SESSION_NOT_FOUND", "Session not found");
481
+ if (session.status === "running" && !body.status) {
482
+ throw new HttpError(409, "SESSION_HEAD_REPAIR_RUNNING", "Cannot repair a running session head");
483
+ }
484
+ const basisEvent = context.db
485
+ .prepare(`SELECT * FROM session_events
486
+ WHERE account_id = ? AND session_id = ? AND machine_id = ? AND message_id = ?`)
487
+ .get(token.account_id, params.sessionId, session.machine_id, body.basis.eventMessageId);
488
+ if (!basisEvent)
489
+ throw new HttpError(409, "SESSION_HEAD_BASIS_MISSING", "Head advance basis event is not available");
490
+ if (basisEvent.turn_id !== body.basis.turnId || basisEvent.turn_seq !== body.basis.turnSeq) {
491
+ throw new HttpError(409, "SESSION_HEAD_BASIS_MISMATCH", "Head advance basis does not match the stored event");
492
+ }
493
+ const nextOrder = logicalEventOrderForRow(basisEvent);
494
+ const currentOrder = sessionHeadBasisOrderForRow(context.db, session);
495
+ const rewriteFenced = typeof session.head_basis_message_id === "string" &&
496
+ session.head_basis_message_id.startsWith("rewrite:");
497
+ const comparison = compareHeadOrders(nextOrder, currentOrder);
498
+ const currentHeadAlreadySettled = session.current_head === `event:${basisEvent.id}` ||
499
+ session.current_head === body.currentHead ||
500
+ session.last_turn_id === body.lastTurnId;
501
+ const advanced = !rewriteFenced && comparison > 0;
502
+ const statusChanged = !rewriteFenced &&
503
+ body.status !== undefined &&
504
+ session.status === "running" &&
505
+ comparison === 0 &&
506
+ currentHeadAlreadySettled;
507
+ if (advanced || statusChanged) {
508
+ const ts = context.now();
509
+ context.db.prepare(`UPDATE sessions
510
+ SET status = COALESCE(?, status),
511
+ current_head = COALESCE(?, current_head),
512
+ last_turn_id = COALESCE(?, last_turn_id),
513
+ head_basis_event_id = COALESCE(?, head_basis_event_id),
514
+ head_basis_message_id = COALESCE(?, head_basis_message_id),
515
+ head_basis_logical_time = COALESCE(?, head_basis_logical_time),
516
+ head_basis_turn_seq = COALESCE(?, head_basis_turn_seq),
517
+ updated_at = ?
518
+ WHERE id = ? AND account_id = ?`).run(body.status && session.status === "running" ? body.status : null, advanced ? body.currentHead : null, advanced ? body.lastTurnId : null, advanced ? basisEvent.id : null, advanced ? basisEvent.message_id : null, advanced ? nextOrder.time : null, advanced ? nextOrder.seq : null, ts, params.sessionId, token.account_id);
519
+ }
520
+ const row = context.db
521
+ .prepare("SELECT * FROM sessions WHERE account_id = ? AND id = ?")
522
+ .get(token.account_id, params.sessionId);
523
+ if (!row)
524
+ throw new HttpError(404, "SESSION_NOT_FOUND", "Session not found");
525
+ const snapshot = context.sessionSnapshot(row);
526
+ context.broadcastControllers(token.account_id, { type: "server:session", session: snapshot });
527
+ return {
528
+ advanced,
529
+ session: snapshot,
530
+ basis: {
531
+ eventId: basisEvent.id,
532
+ messageId: basisEvent.message_id ?? undefined,
533
+ logicalTime: nextOrder.time,
534
+ turnSeq: nextOrder.seq,
535
+ },
536
+ ...(statusChanged ? { settled: true } : {}),
537
+ ...(rewriteFenced ? { reason: "rewrite-fenced" } : {}),
538
+ };
539
+ });
540
+ app.get("/api/workspace-sync", async (request) => {
541
+ const token = context.requireFullControllerToken(request.headers.authorization);
542
+ return workspaceSyncState(context, token.account_id);
543
+ });
544
+ app.post("/api/workspace-sync/session-star", async (request) => {
545
+ const token = context.requireFullControllerToken(request.headers.authorization);
546
+ const body = workspaceSessionStarSchema.parse(request.body);
547
+ assertWorkspaceSession(context, token.account_id, body.sessionId);
548
+ const ts = context.now();
549
+ if (body.starred) {
550
+ context.db
551
+ .prepare(`INSERT INTO session_stars (account_id, session_id, starred_at)
552
+ VALUES (?, ?, ?)
553
+ ON CONFLICT(account_id, session_id) DO UPDATE SET starred_at = excluded.starred_at`)
554
+ .run(token.account_id, body.sessionId, ts);
555
+ return { sessionId: body.sessionId, starred: true, starredAt: ts };
556
+ }
557
+ context.db
558
+ .prepare("DELETE FROM session_stars WHERE account_id = ? AND session_id = ?")
559
+ .run(token.account_id, body.sessionId);
560
+ return { sessionId: body.sessionId, starred: false, unstarredAt: ts };
561
+ });
562
+ app.post("/api/workspace-sync/session-recent", async (request) => {
563
+ const token = context.requireFullControllerToken(request.headers.authorization);
564
+ const body = workspaceSessionRecentSchema.parse(request.body);
565
+ assertWorkspaceSession(context, token.account_id, body.sessionId);
566
+ const ts = context.now();
567
+ context.db
568
+ .prepare(`INSERT INTO account_recent_sessions (account_id, session_id, last_selected_at)
569
+ VALUES (?, ?, ?)
570
+ ON CONFLICT(account_id, session_id) DO UPDATE SET last_selected_at = excluded.last_selected_at`)
571
+ .run(token.account_id, body.sessionId, ts);
572
+ return { sessionId: body.sessionId, lastSelectedAt: ts };
573
+ });
574
+ app.post("/api/workspace-sync/cwd-pin", async (request) => {
575
+ const token = context.requireFullControllerToken(request.headers.authorization);
576
+ const body = workspaceCwdPinSchema.parse(request.body);
577
+ const ts = context.now();
578
+ context.db
579
+ .prepare(`INSERT INTO account_workspace_cwd_pins (account_id, machine_id, cwd, label, pinned_at)
580
+ VALUES (?, ?, ?, ?, ?)
581
+ ON CONFLICT(account_id, machine_id, cwd) DO UPDATE SET
582
+ label = excluded.label,
583
+ pinned_at = excluded.pinned_at`)
584
+ .run(token.account_id, body.machineId, body.cwd, body.label ?? null, ts);
585
+ return {
586
+ machineId: body.machineId,
587
+ cwd: body.cwd,
588
+ ...(body.label ? { label: body.label } : {}),
589
+ pinnedAt: ts,
590
+ };
591
+ });
592
+ app.post("/api/workspace-sync/cwd-unpin", async (request) => {
593
+ const token = context.requireFullControllerToken(request.headers.authorization);
594
+ const body = workspaceCwdUnpinSchema.parse(request.body);
595
+ const ts = context.now();
596
+ context.db
597
+ .prepare("DELETE FROM account_workspace_cwd_pins WHERE account_id = ? AND machine_id = ? AND cwd = ?")
598
+ .run(token.account_id, body.machineId, body.cwd);
599
+ return { machineId: body.machineId, cwd: body.cwd, removed: true, removedAt: ts };
600
+ });
601
+ app.post("/api/workspace-sync/project-preference", async (request) => {
602
+ const token = context.requireFullControllerToken(request.headers.authorization);
603
+ const body = workspaceProjectPreferenceSchema.parse(request.body);
604
+ const ts = context.now();
605
+ context.db
606
+ .prepare(`INSERT INTO account_project_preferences (account_id, project_key, machine_id, cwd, agent, memory_mode, updated_at)
607
+ VALUES (?, ?, ?, ?, ?, ?, ?)
608
+ ON CONFLICT(account_id, project_key) DO UPDATE SET
609
+ machine_id = excluded.machine_id,
610
+ cwd = excluded.cwd,
611
+ agent = excluded.agent,
612
+ memory_mode = excluded.memory_mode,
613
+ updated_at = excluded.updated_at`)
614
+ .run(token.account_id, body.projectKey, body.machineId ?? null, body.cwd ?? null, body.agent ?? null, body.memoryMode ?? null, ts);
615
+ return {
616
+ projectKey: body.projectKey,
617
+ ...(body.machineId ? { machineId: body.machineId } : {}),
618
+ ...(body.cwd ? { cwd: body.cwd } : {}),
619
+ ...(body.agent ? { agent: body.agent } : {}),
620
+ ...(body.memoryMode ? { memoryMode: body.memoryMode } : {}),
621
+ updatedAt: ts,
622
+ };
623
+ });
624
+ app.post("/api/sessions/:sessionId/metadata", async (request) => {
625
+ const token = context.requireFullControllerToken(request.headers.authorization);
626
+ const params = z.object({ sessionId: z.string().min(1) }).parse(request.params);
627
+ const body = sessionMetadataUpdateSchema.parse(request.body);
628
+ const session = context.db
629
+ .prepare("SELECT * FROM sessions WHERE id = ? AND account_id = ?")
630
+ .get(params.sessionId, token.account_id);
631
+ if (!session)
632
+ throw new HttpError(404, "SESSION_NOT_FOUND", "Session not found");
633
+ const ts = context.now();
634
+ context.db
635
+ .prepare("UPDATE sessions SET encrypted_metadata = ?, updated_at = ? WHERE id = ? AND account_id = ?")
636
+ .run(JSON.stringify(body.encryptedMetadata), ts, params.sessionId, token.account_id);
637
+ const updated = context.db
638
+ .prepare("SELECT * FROM sessions WHERE id = ? AND account_id = ?")
639
+ .get(params.sessionId, token.account_id);
640
+ const snapshot = context.sessionSnapshot(updated);
641
+ context.broadcastControllers(token.account_id, { type: "server:session", session: snapshot });
642
+ return { session: snapshot, updatedAt: ts };
643
+ });
644
+ app.get("/api/memory", async (request) => {
645
+ const token = context.requireFullControllerToken(request.headers.authorization);
646
+ const query = memoryListQuerySchema.parse(request.query);
647
+ const rows = query.projectKey
648
+ ? context.db
649
+ .prepare(`SELECT id, project_key, encrypted_memory, created_at, updated_at
650
+ FROM account_memories
651
+ WHERE account_id = ? AND project_key = ?
652
+ ORDER BY updated_at DESC, id ASC`)
653
+ .all(token.account_id, query.projectKey)
654
+ : context.db
655
+ .prepare(`SELECT id, project_key, encrypted_memory, created_at, updated_at
656
+ FROM account_memories
657
+ WHERE account_id = ?
658
+ ORDER BY updated_at DESC, id ASC`)
659
+ .all(token.account_id);
660
+ return {
661
+ memories: rows.map((row) => ({
662
+ id: row.id,
663
+ projectKey: row.project_key,
664
+ encryptedMemory: context.parseJson(row.encrypted_memory, undefined),
665
+ createdAt: row.created_at,
666
+ updatedAt: row.updated_at,
667
+ })),
668
+ };
669
+ });
670
+ app.post("/api/memory", async (request) => {
671
+ const token = context.requireFullControllerToken(request.headers.authorization);
672
+ const body = memorySaveSchema.parse(request.body);
673
+ const encryptedMemory = encryptedEnvelopeSchema.parse(body.encryptedMemory);
674
+ const ts = context.now();
675
+ context.db
676
+ .prepare(`INSERT INTO account_memories (id, account_id, project_key, encrypted_memory, created_at, updated_at)
677
+ VALUES (?, ?, ?, ?, ?, ?)
678
+ ON CONFLICT(id) DO UPDATE SET
679
+ project_key = excluded.project_key,
680
+ encrypted_memory = excluded.encrypted_memory,
681
+ updated_at = excluded.updated_at`)
682
+ .run(body.id, token.account_id, body.projectKey, JSON.stringify(encryptedMemory), ts, ts);
683
+ return {
684
+ id: body.id,
685
+ projectKey: body.projectKey,
686
+ encryptedMemory,
687
+ createdAt: ts,
688
+ updatedAt: ts,
689
+ };
690
+ });
691
+ app.post("/api/memory/delete", async (request) => {
692
+ const token = context.requireFullControllerToken(request.headers.authorization);
693
+ const body = memoryDeleteSchema.parse(request.body);
694
+ const ts = context.now();
695
+ context.db.prepare("DELETE FROM account_memories WHERE account_id = ? AND id = ?").run(token.account_id, body.id);
696
+ return { id: body.id, deleted: true, deletedAt: ts };
697
+ });
698
+ app.get("/api/memory/settings", async (request) => {
699
+ const token = context.requireFullControllerToken(request.headers.authorization);
700
+ const query = memoryListQuerySchema.parse(request.query);
701
+ const rows = query.projectKey
702
+ ? context.db
703
+ .prepare(`SELECT project_key, disabled, injection_enabled, updated_at
704
+ FROM account_memory_settings
705
+ WHERE account_id = ? AND project_key = ?
706
+ ORDER BY updated_at DESC`)
707
+ .all(token.account_id, query.projectKey)
708
+ : context.db
709
+ .prepare(`SELECT project_key, disabled, injection_enabled, updated_at
710
+ FROM account_memory_settings
711
+ WHERE account_id = ?
712
+ ORDER BY updated_at DESC, project_key ASC`)
713
+ .all(token.account_id);
714
+ return {
715
+ settings: rows.map((row) => ({
716
+ projectKey: row.project_key,
717
+ disabled: row.disabled === 1,
718
+ injectionEnabled: row.injection_enabled === 1,
719
+ updatedAt: row.updated_at,
720
+ })),
721
+ };
722
+ });
723
+ app.post("/api/memory/settings", async (request) => {
724
+ const token = context.requireFullControllerToken(request.headers.authorization);
725
+ const body = memorySettingsSchema.parse(request.body);
726
+ const current = context.db
727
+ .prepare("SELECT disabled, injection_enabled FROM account_memory_settings WHERE account_id = ? AND project_key = ?")
728
+ .get(token.account_id, body.projectKey);
729
+ const disabled = body.disabled === undefined ? current?.disabled ?? 0 : body.disabled ? 1 : 0;
730
+ const injectionEnabled = body.injectionEnabled === undefined ? current?.injection_enabled ?? 0 : body.injectionEnabled ? 1 : 0;
731
+ const ts = context.now();
732
+ context.db
733
+ .prepare(`INSERT INTO account_memory_settings (account_id, project_key, disabled, injection_enabled, updated_at)
734
+ VALUES (?, ?, ?, ?, ?)
735
+ ON CONFLICT(account_id, project_key) DO UPDATE SET
736
+ disabled = excluded.disabled,
737
+ injection_enabled = excluded.injection_enabled,
738
+ updated_at = excluded.updated_at`)
739
+ .run(token.account_id, body.projectKey, disabled, injectionEnabled, ts);
740
+ return { projectKey: body.projectKey, disabled: disabled === 1, injectionEnabled: injectionEnabled === 1, updatedAt: ts };
741
+ });
742
+ app.get("/api/machine/memory-documents", async (request) => {
743
+ const token = context.requireHttpToken(request.headers.authorization, "machine");
744
+ const query = machineMemoryDocumentsQuerySchema.parse(request.query);
745
+ const projectKey = `memory-doc:${query.projectKey}`;
746
+ const rows = context.db
747
+ .prepare(`SELECT id, project_key, encrypted_memory, created_at, updated_at
748
+ FROM account_memories
749
+ WHERE account_id = ? AND project_key = ?
750
+ ORDER BY updated_at DESC, id ASC`)
751
+ .all(token.account_id, projectKey);
752
+ return {
753
+ documents: rows.map((row) => ({
754
+ id: row.id,
755
+ projectKey: query.projectKey,
756
+ encryptedDocument: context.parseJson(row.encrypted_memory, undefined),
757
+ createdAt: row.created_at,
758
+ updatedAt: row.updated_at,
759
+ })),
760
+ };
761
+ });
762
+ app.post("/api/machine/memory-documents", async (request) => {
763
+ const token = context.requireHttpToken(request.headers.authorization, "machine");
764
+ const body = machineMemoryDocumentSaveSchema.parse(request.body);
765
+ const projectKey = `memory-doc:${body.projectKey}`;
766
+ const ts = context.now();
767
+ context.db
768
+ .prepare(`INSERT INTO account_memories (id, account_id, project_key, encrypted_memory, created_at, updated_at)
769
+ VALUES (?, ?, ?, ?, ?, ?)
770
+ ON CONFLICT(id) DO UPDATE SET
771
+ project_key = excluded.project_key,
772
+ encrypted_memory = excluded.encrypted_memory,
773
+ updated_at = excluded.updated_at`)
774
+ .run(body.id, token.account_id, projectKey, JSON.stringify(body.encryptedDocument), ts, ts);
775
+ return { id: body.id, projectKey: body.projectKey, encryptedDocument: body.encryptedDocument, createdAt: ts, updatedAt: ts };
776
+ });
241
777
  app.get("/api/devices", async (request) => {
242
778
  const token = context.requireFullControllerToken(request.headers.authorization);
243
779
  const rows = context.db
244
- .prepare(`SELECT kind, machine_id, device_id, device_name, created_at, revoked_at
780
+ .prepare(`SELECT tokens.kind, tokens.machine_id, tokens.device_id, tokens.device_name,
781
+ tokens.created_at, tokens.revoked_at, tokens.last_seen,
782
+ machines.last_seen AS machine_last_seen
245
783
  FROM tokens
246
- WHERE account_id = ? AND device_id IS NOT NULL
247
- ORDER BY created_at DESC`)
784
+ LEFT JOIN machines ON machines.account_id = tokens.account_id AND machines.id = tokens.machine_id
785
+ WHERE tokens.account_id = ? AND tokens.device_id IS NOT NULL
786
+ ORDER BY tokens.created_at DESC`)
248
787
  .all(token.account_id);
788
+ const byDeviceId = new Map();
789
+ for (const row of rows) {
790
+ const existing = byDeviceId.get(row.device_id);
791
+ if (!existing) {
792
+ byDeviceId.set(row.device_id, row);
793
+ continue;
794
+ }
795
+ if (existing.revoked_at !== null && row.revoked_at === null) {
796
+ byDeviceId.set(row.device_id, row);
797
+ }
798
+ }
249
799
  return {
250
- devices: rows.map((row) => ({
251
- deviceId: row.device_id,
252
- name: row.device_name ?? row.device_id,
253
- kind: row.kind,
254
- current: row.kind === "controller" && row.device_id === token.device_id,
255
- revoked: row.revoked_at !== null,
256
- createdAt: row.created_at,
257
- machineId: row.machine_id ?? undefined,
258
- revokedAt: row.revoked_at ?? undefined,
259
- })),
800
+ devices: [...byDeviceId.values()].map((row) => {
801
+ const revoked = row.revoked_at !== null;
802
+ const online = revoked
803
+ ? false
804
+ : row.kind === "machine" && row.machine_id
805
+ ? context.connectedMachine(token.account_id, row.machine_id) !== undefined
806
+ : context.controllerDeviceOnline(token.account_id, row.device_id);
807
+ const lastSeen = row.kind === "machine"
808
+ ? row.machine_last_seen ?? row.last_seen ?? row.created_at
809
+ : row.last_seen ?? row.created_at;
810
+ return {
811
+ deviceId: row.device_id,
812
+ name: row.device_name ?? row.device_id,
813
+ kind: row.kind,
814
+ current: row.kind === "controller" && row.device_id === token.device_id,
815
+ revoked,
816
+ online,
817
+ createdAt: row.created_at,
818
+ lastSeen,
819
+ machineId: row.machine_id ?? undefined,
820
+ revokedAt: row.revoked_at ?? undefined,
821
+ };
822
+ }),
260
823
  };
261
824
  });
262
825
  app.post("/api/devices/controller", async (request) => {
@@ -280,6 +843,20 @@ export function registerHttpRoutes(app, context, controllerInviteTtlMs) {
280
843
  createdAt,
281
844
  };
282
845
  });
846
+ app.post("/api/devices/rename", async (request) => {
847
+ const token = context.requireFullControllerToken(request.headers.authorization);
848
+ const body = deviceRenameSchema.parse(request.body);
849
+ const target = context.db
850
+ .prepare("SELECT * FROM tokens WHERE account_id = ? AND device_id = ? AND revoked_at IS NULL ORDER BY created_at DESC")
851
+ .get(token.account_id, body.deviceId);
852
+ if (!target)
853
+ throw new HttpError(404, "DEVICE_NOT_FOUND", "Device not found");
854
+ context.db.prepare("UPDATE tokens SET device_name = ? WHERE token = ?").run(body.name, target.token);
855
+ if (target.kind === "machine" && target.machine_id) {
856
+ context.db.prepare("UPDATE machines SET name = ?, updated_at = ? WHERE account_id = ? AND id = ?").run(body.name, context.now(), token.account_id, target.machine_id);
857
+ }
858
+ return { deviceId: body.deviceId, name: body.name, renamedAt: context.now() };
859
+ });
283
860
  app.post("/api/controller-invites", async (request) => {
284
861
  const token = context.requireFullControllerToken(request.headers.authorization);
285
862
  const body = controllerInviteCreateSchema.parse(request.body);
@@ -392,4 +969,53 @@ export function registerHttpRoutes(app, context, controllerInviteTtlMs) {
392
969
  return { tokenId: body.tokenId, revokedAt: target.revoked_at ?? ts };
393
970
  });
394
971
  }
972
+ function compareHeadOrders(next, current) {
973
+ if (!current)
974
+ return 1;
975
+ if (next.time !== current.time)
976
+ return next.time > current.time ? 1 : -1;
977
+ if (next.seq !== current.seq)
978
+ return next.seq > current.seq ? 1 : -1;
979
+ if (next.id !== current.id)
980
+ return next.id > current.id ? 1 : -1;
981
+ return 0;
982
+ }
983
+ function logicalEventOrderForRow(row) {
984
+ if (row.occurred_at !== null && row.occurred_at !== undefined) {
985
+ return { time: row.occurred_at, seq: row.turn_seq ?? row.seq, id: row.id };
986
+ }
987
+ if (row.message_id?.startsWith("hist_") && row.turn_seq !== null && row.turn_seq !== undefined) {
988
+ return { time: 0, seq: row.turn_seq, id: row.id };
989
+ }
990
+ return { time: row.created_at, seq: row.turn_seq ?? row.seq, id: row.id };
991
+ }
992
+ function sessionHeadBasisOrderForRow(db, row) {
993
+ if (row.head_basis_logical_time !== null && row.head_basis_turn_seq !== null) {
994
+ return {
995
+ time: row.head_basis_logical_time,
996
+ seq: row.head_basis_turn_seq,
997
+ id: row.head_basis_event_id ?? 0,
998
+ };
999
+ }
1000
+ const eventId = row.current_head ? /^event:(\d+)$/.exec(row.current_head)?.[1] : undefined;
1001
+ if (!eventId)
1002
+ return undefined;
1003
+ const event = db
1004
+ .prepare("SELECT * FROM session_events WHERE account_id = ? AND session_id = ? AND machine_id = ? AND id = ?")
1005
+ .get(row.account_id, row.id, row.machine_id, Number(eventId));
1006
+ return event ? logicalEventOrderForRow(event) : undefined;
1007
+ }
1008
+ function sessionLooksTranscriptPending(context, sessionRow) {
1009
+ if (sessionRow.status !== "running")
1010
+ return false;
1011
+ if (!sessionRow.source_session_id || !sessionRow.source_checkpoint_id)
1012
+ return false;
1013
+ const row = context.db
1014
+ .prepare(`SELECT 1
1015
+ FROM session_events
1016
+ WHERE account_id = ? AND session_id = ? AND machine_id = ?
1017
+ LIMIT 1`)
1018
+ .get(sessionRow.account_id, sessionRow.id, sessionRow.machine_id);
1019
+ return !row;
1020
+ }
395
1021
  //# sourceMappingURL=http-routes.js.map