@paperclipai_dld/server 2026.319.0-canary.3

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 (702) hide show
  1. package/LICENSE +21 -0
  2. package/dist/adapters/codex-models.d.ts +4 -0
  3. package/dist/adapters/codex-models.d.ts.map +1 -0
  4. package/dist/adapters/codex-models.js +98 -0
  5. package/dist/adapters/codex-models.js.map +1 -0
  6. package/dist/adapters/cursor-models.d.ts +13 -0
  7. package/dist/adapters/cursor-models.d.ts.map +1 -0
  8. package/dist/adapters/cursor-models.js +148 -0
  9. package/dist/adapters/cursor-models.js.map +1 -0
  10. package/dist/adapters/http/execute.d.ts +3 -0
  11. package/dist/adapters/http/execute.d.ts.map +1 -0
  12. package/dist/adapters/http/execute.js +39 -0
  13. package/dist/adapters/http/execute.js.map +1 -0
  14. package/dist/adapters/http/index.d.ts +3 -0
  15. package/dist/adapters/http/index.d.ts.map +1 -0
  16. package/dist/adapters/http/index.js +20 -0
  17. package/dist/adapters/http/index.js.map +1 -0
  18. package/dist/adapters/http/test.d.ts +3 -0
  19. package/dist/adapters/http/test.d.ts.map +1 -0
  20. package/dist/adapters/http/test.js +106 -0
  21. package/dist/adapters/http/test.js.map +1 -0
  22. package/dist/adapters/index.d.ts +4 -0
  23. package/dist/adapters/index.d.ts.map +1 -0
  24. package/dist/adapters/index.js +3 -0
  25. package/dist/adapters/index.js.map +1 -0
  26. package/dist/adapters/process/execute.d.ts +3 -0
  27. package/dist/adapters/process/execute.d.ts.map +1 -0
  28. package/dist/adapters/process/execute.js +63 -0
  29. package/dist/adapters/process/execute.js.map +1 -0
  30. package/dist/adapters/process/index.d.ts +3 -0
  31. package/dist/adapters/process/index.d.ts.map +1 -0
  32. package/dist/adapters/process/index.js +23 -0
  33. package/dist/adapters/process/index.js.map +1 -0
  34. package/dist/adapters/process/test.d.ts +3 -0
  35. package/dist/adapters/process/test.d.ts.map +1 -0
  36. package/dist/adapters/process/test.js +77 -0
  37. package/dist/adapters/process/test.js.map +1 -0
  38. package/dist/adapters/registry.d.ts +9 -0
  39. package/dist/adapters/registry.d.ts.map +1 -0
  40. package/dist/adapters/registry.js +142 -0
  41. package/dist/adapters/registry.js.map +1 -0
  42. package/dist/adapters/types.d.ts +2 -0
  43. package/dist/adapters/types.d.ts.map +1 -0
  44. package/dist/adapters/types.js +2 -0
  45. package/dist/adapters/types.js.map +1 -0
  46. package/dist/adapters/utils.d.ts +10 -0
  47. package/dist/adapters/utils.d.ts.map +1 -0
  48. package/dist/adapters/utils.js +14 -0
  49. package/dist/adapters/utils.js.map +1 -0
  50. package/dist/agent-auth-jwt.d.ts +14 -0
  51. package/dist/agent-auth-jwt.d.ts.map +1 -0
  52. package/dist/agent-auth-jwt.js +117 -0
  53. package/dist/agent-auth-jwt.js.map +1 -0
  54. package/dist/app.d.ts +25 -0
  55. package/dist/app.d.ts.map +1 -0
  56. package/dist/app.js +259 -0
  57. package/dist/app.js.map +1 -0
  58. package/dist/attachment-types.d.ts +33 -0
  59. package/dist/attachment-types.d.ts.map +1 -0
  60. package/dist/attachment-types.js +67 -0
  61. package/dist/attachment-types.js.map +1 -0
  62. package/dist/auth/better-auth.d.ts +24 -0
  63. package/dist/auth/better-auth.d.ts.map +1 -0
  64. package/dist/auth/better-auth.js +108 -0
  65. package/dist/auth/better-auth.js.map +1 -0
  66. package/dist/board-claim.d.ts +23 -0
  67. package/dist/board-claim.d.ts.map +1 -0
  68. package/dist/board-claim.js +115 -0
  69. package/dist/board-claim.js.map +1 -0
  70. package/dist/config-file.d.ts +3 -0
  71. package/dist/config-file.d.ts.map +1 -0
  72. package/dist/config-file.js +16 -0
  73. package/dist/config-file.js.map +1 -0
  74. package/dist/config.d.ts +38 -0
  75. package/dist/config.d.ts.map +1 -0
  76. package/dist/config.js +162 -0
  77. package/dist/config.js.map +1 -0
  78. package/dist/errors.d.ts +12 -0
  79. package/dist/errors.d.ts.map +1 -0
  80. package/dist/errors.js +28 -0
  81. package/dist/errors.js.map +1 -0
  82. package/dist/home-paths.d.ts +17 -0
  83. package/dist/home-paths.d.ts.map +1 -0
  84. package/dist/home-paths.js +75 -0
  85. package/dist/home-paths.js.map +1 -0
  86. package/dist/index.d.ts +10 -0
  87. package/dist/index.d.ts.map +1 -0
  88. package/dist/index.js +581 -0
  89. package/dist/index.js.map +1 -0
  90. package/dist/log-redaction.d.ts +10 -0
  91. package/dist/log-redaction.d.ts.map +1 -0
  92. package/dist/log-redaction.js +110 -0
  93. package/dist/log-redaction.js.map +1 -0
  94. package/dist/middleware/auth.d.ts +12 -0
  95. package/dist/middleware/auth.d.ts.map +1 -0
  96. package/dist/middleware/auth.js +124 -0
  97. package/dist/middleware/auth.js.map +1 -0
  98. package/dist/middleware/board-mutation-guard.d.ts +3 -0
  99. package/dist/middleware/board-mutation-guard.d.ts.map +1 -0
  100. package/dist/middleware/board-mutation-guard.js +60 -0
  101. package/dist/middleware/board-mutation-guard.js.map +1 -0
  102. package/dist/middleware/error-handler.d.ts +17 -0
  103. package/dist/middleware/error-handler.d.ts.map +1 -0
  104. package/dist/middleware/error-handler.js +37 -0
  105. package/dist/middleware/error-handler.js.map +1 -0
  106. package/dist/middleware/index.d.ts +4 -0
  107. package/dist/middleware/index.d.ts.map +1 -0
  108. package/dist/middleware/index.js +4 -0
  109. package/dist/middleware/index.js.map +1 -0
  110. package/dist/middleware/logger.d.ts +4 -0
  111. package/dist/middleware/logger.d.ts.map +1 -0
  112. package/dist/middleware/logger.js +87 -0
  113. package/dist/middleware/logger.js.map +1 -0
  114. package/dist/middleware/private-hostname-guard.d.ts +11 -0
  115. package/dist/middleware/private-hostname-guard.d.ts.map +1 -0
  116. package/dist/middleware/private-hostname-guard.js +78 -0
  117. package/dist/middleware/private-hostname-guard.js.map +1 -0
  118. package/dist/middleware/validate.d.ts +4 -0
  119. package/dist/middleware/validate.d.ts.map +1 -0
  120. package/dist/middleware/validate.js +7 -0
  121. package/dist/middleware/validate.js.map +1 -0
  122. package/dist/paths.d.ts +3 -0
  123. package/dist/paths.d.ts.map +1 -0
  124. package/dist/paths.js +31 -0
  125. package/dist/paths.js.map +1 -0
  126. package/dist/realtime/live-events-ws.d.ts +28 -0
  127. package/dist/realtime/live-events-ws.d.ts.map +1 -0
  128. package/dist/realtime/live-events-ws.js +187 -0
  129. package/dist/realtime/live-events-ws.js.map +1 -0
  130. package/dist/redaction.d.ts +4 -0
  131. package/dist/redaction.d.ts.map +1 -0
  132. package/dist/redaction.js +63 -0
  133. package/dist/redaction.js.map +1 -0
  134. package/dist/routes/access.d.ts +56 -0
  135. package/dist/routes/access.d.ts.map +1 -0
  136. package/dist/routes/access.js +2124 -0
  137. package/dist/routes/access.js.map +1 -0
  138. package/dist/routes/activity.d.ts +3 -0
  139. package/dist/routes/activity.d.ts.map +1 -0
  140. package/dist/routes/activity.js +78 -0
  141. package/dist/routes/activity.js.map +1 -0
  142. package/dist/routes/agents.d.ts +4 -0
  143. package/dist/routes/agents.d.ts.map +1 -0
  144. package/dist/routes/agents.js +1390 -0
  145. package/dist/routes/agents.js.map +1 -0
  146. package/dist/routes/approvals.d.ts +3 -0
  147. package/dist/routes/approvals.d.ts.map +1 -0
  148. package/dist/routes/approvals.js +275 -0
  149. package/dist/routes/approvals.js.map +1 -0
  150. package/dist/routes/assets.d.ts +4 -0
  151. package/dist/routes/assets.d.ts.map +1 -0
  152. package/dist/routes/assets.js +309 -0
  153. package/dist/routes/assets.js.map +1 -0
  154. package/dist/routes/authz.d.ts +15 -0
  155. package/dist/routes/authz.d.ts.map +1 -0
  156. package/dist/routes/authz.js +40 -0
  157. package/dist/routes/authz.js.map +1 -0
  158. package/dist/routes/companies.d.ts +3 -0
  159. package/dist/routes/companies.d.ts.map +1 -0
  160. package/dist/routes/companies.js +174 -0
  161. package/dist/routes/companies.js.map +1 -0
  162. package/dist/routes/costs.d.ts +3 -0
  163. package/dist/routes/costs.d.ts.map +1 -0
  164. package/dist/routes/costs.js +268 -0
  165. package/dist/routes/costs.js.map +1 -0
  166. package/dist/routes/dashboard.d.ts +3 -0
  167. package/dist/routes/dashboard.d.ts.map +1 -0
  168. package/dist/routes/dashboard.js +15 -0
  169. package/dist/routes/dashboard.js.map +1 -0
  170. package/dist/routes/execution-workspaces.d.ts +3 -0
  171. package/dist/routes/execution-workspaces.d.ts.map +1 -0
  172. package/dist/routes/execution-workspaces.js +165 -0
  173. package/dist/routes/execution-workspaces.js.map +1 -0
  174. package/dist/routes/goals.d.ts +3 -0
  175. package/dist/routes/goals.d.ts.map +1 -0
  176. package/dist/routes/goals.js +95 -0
  177. package/dist/routes/goals.js.map +1 -0
  178. package/dist/routes/health.d.ts +9 -0
  179. package/dist/routes/health.d.ts.map +1 -0
  180. package/dist/routes/health.js +51 -0
  181. package/dist/routes/health.js.map +1 -0
  182. package/dist/routes/index.d.ts +16 -0
  183. package/dist/routes/index.d.ts.map +1 -0
  184. package/dist/routes/index.js +16 -0
  185. package/dist/routes/index.js.map +1 -0
  186. package/dist/routes/instance-settings.d.ts +3 -0
  187. package/dist/routes/instance-settings.d.ts.map +1 -0
  188. package/dist/routes/instance-settings.js +46 -0
  189. package/dist/routes/instance-settings.js.map +1 -0
  190. package/dist/routes/issues-checkout-wakeup.d.ts +9 -0
  191. package/dist/routes/issues-checkout-wakeup.d.ts.map +1 -0
  192. package/dist/routes/issues-checkout-wakeup.js +12 -0
  193. package/dist/routes/issues-checkout-wakeup.js.map +1 -0
  194. package/dist/routes/issues.d.ts +4 -0
  195. package/dist/routes/issues.d.ts.map +1 -0
  196. package/dist/routes/issues.js +1431 -0
  197. package/dist/routes/issues.js.map +1 -0
  198. package/dist/routes/llms.d.ts +3 -0
  199. package/dist/routes/llms.d.ts.map +1 -0
  200. package/dist/routes/llms.js +78 -0
  201. package/dist/routes/llms.js.map +1 -0
  202. package/dist/routes/plugin-ui-static.d.ts +69 -0
  203. package/dist/routes/plugin-ui-static.d.ts.map +1 -0
  204. package/dist/routes/plugin-ui-static.js +411 -0
  205. package/dist/routes/plugin-ui-static.js.map +1 -0
  206. package/dist/routes/plugins.d.ts +120 -0
  207. package/dist/routes/plugins.d.ts.map +1 -0
  208. package/dist/routes/plugins.js +1784 -0
  209. package/dist/routes/plugins.js.map +1 -0
  210. package/dist/routes/projects.d.ts +3 -0
  211. package/dist/routes/projects.d.ts.map +1 -0
  212. package/dist/routes/projects.js +257 -0
  213. package/dist/routes/projects.js.map +1 -0
  214. package/dist/routes/secrets.d.ts +3 -0
  215. package/dist/routes/secrets.d.ts.map +1 -0
  216. package/dist/routes/secrets.js +128 -0
  217. package/dist/routes/secrets.js.map +1 -0
  218. package/dist/routes/sidebar-badges.d.ts +3 -0
  219. package/dist/routes/sidebar-badges.d.ts.map +1 -0
  220. package/dist/routes/sidebar-badges.js +45 -0
  221. package/dist/routes/sidebar-badges.js.map +1 -0
  222. package/dist/secrets/external-stub-providers.d.ts +5 -0
  223. package/dist/secrets/external-stub-providers.d.ts.map +1 -0
  224. package/dist/secrets/external-stub-providers.js +21 -0
  225. package/dist/secrets/external-stub-providers.js.map +1 -0
  226. package/dist/secrets/local-encrypted-provider.d.ts +3 -0
  227. package/dist/secrets/local-encrypted-provider.d.ts.map +1 -0
  228. package/dist/secrets/local-encrypted-provider.js +116 -0
  229. package/dist/secrets/local-encrypted-provider.js.map +1 -0
  230. package/dist/secrets/provider-registry.d.ts +5 -0
  231. package/dist/secrets/provider-registry.d.ts.map +1 -0
  232. package/dist/secrets/provider-registry.js +20 -0
  233. package/dist/secrets/provider-registry.js.map +1 -0
  234. package/dist/secrets/types.d.ts +21 -0
  235. package/dist/secrets/types.d.ts.map +1 -0
  236. package/dist/secrets/types.js +2 -0
  237. package/dist/secrets/types.js.map +1 -0
  238. package/dist/services/access.d.ts +81 -0
  239. package/dist/services/access.d.ts.map +1 -0
  240. package/dist/services/access.js +187 -0
  241. package/dist/services/access.js.map +1 -0
  242. package/dist/services/activity-log.d.ts +17 -0
  243. package/dist/services/activity-log.d.ts.map +1 -0
  244. package/dist/services/activity-log.js +68 -0
  245. package/dist/services/activity-log.js.map +1 -0
  246. package/dist/services/activity.d.ts +764 -0
  247. package/dist/services/activity.d.ts.map +1 -0
  248. package/dist/services/activity.js +105 -0
  249. package/dist/services/activity.js.map +1 -0
  250. package/dist/services/agent-permissions.d.ts +6 -0
  251. package/dist/services/agent-permissions.d.ts.map +1 -0
  252. package/dist/services/agent-permissions.js +18 -0
  253. package/dist/services/agent-permissions.js.map +1 -0
  254. package/dist/services/agents.d.ts +1530 -0
  255. package/dist/services/agents.d.ts.map +1 -0
  256. package/dist/services/agents.js +566 -0
  257. package/dist/services/agents.js.map +1 -0
  258. package/dist/services/approvals.d.ts +546 -0
  259. package/dist/services/approvals.d.ts.map +1 -0
  260. package/dist/services/approvals.js +206 -0
  261. package/dist/services/approvals.js.map +1 -0
  262. package/dist/services/assets.d.ts +33 -0
  263. package/dist/services/assets.d.ts.map +1 -0
  264. package/dist/services/assets.js +17 -0
  265. package/dist/services/assets.js.map +1 -0
  266. package/dist/services/budgets.d.ts +38 -0
  267. package/dist/services/budgets.d.ts.map +1 -0
  268. package/dist/services/budgets.js +784 -0
  269. package/dist/services/budgets.js.map +1 -0
  270. package/dist/services/companies.d.ts +124 -0
  271. package/dist/services/companies.d.ts.map +1 -0
  272. package/dist/services/companies.js +256 -0
  273. package/dist/services/companies.js.map +1 -0
  274. package/dist/services/company-portability.d.ts +8 -0
  275. package/dist/services/company-portability.d.ts.map +1 -0
  276. package/dist/services/company-portability.js +867 -0
  277. package/dist/services/company-portability.js.map +1 -0
  278. package/dist/services/costs.d.ts +114 -0
  279. package/dist/services/costs.d.ts.map +1 -0
  280. package/dist/services/costs.js +294 -0
  281. package/dist/services/costs.js.map +1 -0
  282. package/dist/services/cron.d.ts +80 -0
  283. package/dist/services/cron.d.ts.map +1 -0
  284. package/dist/services/cron.js +300 -0
  285. package/dist/services/cron.js.map +1 -0
  286. package/dist/services/dashboard.d.ts +26 -0
  287. package/dist/services/dashboard.d.ts.map +1 -0
  288. package/dist/services/dashboard.js +98 -0
  289. package/dist/services/dashboard.js.map +1 -0
  290. package/dist/services/documents.d.ts +164 -0
  291. package/dist/services/documents.d.ts.map +1 -0
  292. package/dist/services/documents.js +382 -0
  293. package/dist/services/documents.js.map +1 -0
  294. package/dist/services/execution-workspace-policy.d.ts +20 -0
  295. package/dist/services/execution-workspace-policy.d.ts.map +1 -0
  296. package/dist/services/execution-workspace-policy.js +165 -0
  297. package/dist/services/execution-workspace-policy.js.map +1 -0
  298. package/dist/services/execution-workspaces.d.ts +19 -0
  299. package/dist/services/execution-workspaces.d.ts.map +1 -0
  300. package/dist/services/execution-workspaces.js +87 -0
  301. package/dist/services/execution-workspaces.js.map +1 -0
  302. package/dist/services/finance.d.ts +93 -0
  303. package/dist/services/finance.d.ts.map +1 -0
  304. package/dist/services/finance.js +120 -0
  305. package/dist/services/finance.js.map +1 -0
  306. package/dist/services/goals.d.ts +433 -0
  307. package/dist/services/goals.d.ts.map +1 -0
  308. package/dist/services/goals.js +54 -0
  309. package/dist/services/goals.js.map +1 -0
  310. package/dist/services/heartbeat-run-summary.d.ts +2 -0
  311. package/dist/services/heartbeat-run-summary.d.ts.map +1 -0
  312. package/dist/services/heartbeat-run-summary.js +30 -0
  313. package/dist/services/heartbeat-run-summary.js.map +1 -0
  314. package/dist/services/heartbeat.d.ts +766 -0
  315. package/dist/services/heartbeat.d.ts.map +1 -0
  316. package/dist/services/heartbeat.js +3024 -0
  317. package/dist/services/heartbeat.js.map +1 -0
  318. package/dist/services/hire-hook.d.ts +14 -0
  319. package/dist/services/hire-hook.d.ts.map +1 -0
  320. package/dist/services/hire-hook.js +85 -0
  321. package/dist/services/hire-hook.js.map +1 -0
  322. package/dist/services/index.d.ts +29 -0
  323. package/dist/services/index.d.ts.map +1 -0
  324. package/dist/services/index.js +29 -0
  325. package/dist/services/index.js.map +1 -0
  326. package/dist/services/instance-settings.d.ts +9 -0
  327. package/dist/services/instance-settings.d.ts.map +1 -0
  328. package/dist/services/instance-settings.js +80 -0
  329. package/dist/services/instance-settings.js.map +1 -0
  330. package/dist/services/issue-approvals.d.ts +56 -0
  331. package/dist/services/issue-approvals.d.ts.map +1 -0
  332. package/dist/services/issue-approvals.js +153 -0
  333. package/dist/services/issue-approvals.js.map +1 -0
  334. package/dist/services/issue-goal-fallback.d.ts +15 -0
  335. package/dist/services/issue-goal-fallback.d.ts.map +1 -0
  336. package/dist/services/issue-goal-fallback.js +15 -0
  337. package/dist/services/issue-goal-fallback.js.map +1 -0
  338. package/dist/services/issues.d.ts +532 -0
  339. package/dist/services/issues.d.ts.map +1 -0
  340. package/dist/services/issues.js +1308 -0
  341. package/dist/services/issues.js.map +1 -0
  342. package/dist/services/live-events.d.ts +17 -0
  343. package/dist/services/live-events.d.ts.map +1 -0
  344. package/dist/services/live-events.js +33 -0
  345. package/dist/services/live-events.js.map +1 -0
  346. package/dist/services/plugin-capability-validator.d.ts +108 -0
  347. package/dist/services/plugin-capability-validator.d.ts.map +1 -0
  348. package/dist/services/plugin-capability-validator.js +268 -0
  349. package/dist/services/plugin-capability-validator.js.map +1 -0
  350. package/dist/services/plugin-config-validator.d.ts +26 -0
  351. package/dist/services/plugin-config-validator.d.ts.map +1 -0
  352. package/dist/services/plugin-config-validator.js +41 -0
  353. package/dist/services/plugin-config-validator.js.map +1 -0
  354. package/dist/services/plugin-dev-watcher.d.ts +30 -0
  355. package/dist/services/plugin-dev-watcher.d.ts.map +1 -0
  356. package/dist/services/plugin-dev-watcher.js +241 -0
  357. package/dist/services/plugin-dev-watcher.js.map +1 -0
  358. package/dist/services/plugin-event-bus.d.ts +149 -0
  359. package/dist/services/plugin-event-bus.d.ts.map +1 -0
  360. package/dist/services/plugin-event-bus.js +258 -0
  361. package/dist/services/plugin-event-bus.js.map +1 -0
  362. package/dist/services/plugin-host-service-cleanup.d.ts +14 -0
  363. package/dist/services/plugin-host-service-cleanup.d.ts.map +1 -0
  364. package/dist/services/plugin-host-service-cleanup.js +37 -0
  365. package/dist/services/plugin-host-service-cleanup.js.map +1 -0
  366. package/dist/services/plugin-host-services.d.ts +13 -0
  367. package/dist/services/plugin-host-services.d.ts.map +1 -0
  368. package/dist/services/plugin-host-services.js +969 -0
  369. package/dist/services/plugin-host-services.js.map +1 -0
  370. package/dist/services/plugin-job-coordinator.d.ts +81 -0
  371. package/dist/services/plugin-job-coordinator.d.ts.map +1 -0
  372. package/dist/services/plugin-job-coordinator.js +172 -0
  373. package/dist/services/plugin-job-coordinator.js.map +1 -0
  374. package/dist/services/plugin-job-scheduler.d.ts +163 -0
  375. package/dist/services/plugin-job-scheduler.d.ts.map +1 -0
  376. package/dist/services/plugin-job-scheduler.js +454 -0
  377. package/dist/services/plugin-job-scheduler.js.map +1 -0
  378. package/dist/services/plugin-job-store.d.ts +208 -0
  379. package/dist/services/plugin-job-store.d.ts.map +1 -0
  380. package/dist/services/plugin-job-store.js +350 -0
  381. package/dist/services/plugin-job-store.js.map +1 -0
  382. package/dist/services/plugin-lifecycle.d.ts +203 -0
  383. package/dist/services/plugin-lifecycle.d.ts.map +1 -0
  384. package/dist/services/plugin-lifecycle.js +476 -0
  385. package/dist/services/plugin-lifecycle.js.map +1 -0
  386. package/dist/services/plugin-loader.d.ts +441 -0
  387. package/dist/services/plugin-loader.d.ts.map +1 -0
  388. package/dist/services/plugin-loader.js +1192 -0
  389. package/dist/services/plugin-loader.js.map +1 -0
  390. package/dist/services/plugin-log-retention.d.ts +20 -0
  391. package/dist/services/plugin-log-retention.d.ts.map +1 -0
  392. package/dist/services/plugin-log-retention.js +63 -0
  393. package/dist/services/plugin-log-retention.js.map +1 -0
  394. package/dist/services/plugin-manifest-validator.d.ts +79 -0
  395. package/dist/services/plugin-manifest-validator.d.ts.map +1 -0
  396. package/dist/services/plugin-manifest-validator.js +84 -0
  397. package/dist/services/plugin-manifest-validator.js.map +1 -0
  398. package/dist/services/plugin-registry.d.ts +2542 -0
  399. package/dist/services/plugin-registry.d.ts.map +1 -0
  400. package/dist/services/plugin-registry.js +539 -0
  401. package/dist/services/plugin-registry.js.map +1 -0
  402. package/dist/services/plugin-runtime-sandbox.d.ts +40 -0
  403. package/dist/services/plugin-runtime-sandbox.d.ts.map +1 -0
  404. package/dist/services/plugin-runtime-sandbox.js +154 -0
  405. package/dist/services/plugin-runtime-sandbox.js.map +1 -0
  406. package/dist/services/plugin-secrets-handler.d.ts +81 -0
  407. package/dist/services/plugin-secrets-handler.d.ts.map +1 -0
  408. package/dist/services/plugin-secrets-handler.js +275 -0
  409. package/dist/services/plugin-secrets-handler.js.map +1 -0
  410. package/dist/services/plugin-state-store.d.ts +92 -0
  411. package/dist/services/plugin-state-store.d.ts.map +1 -0
  412. package/dist/services/plugin-state-store.js +190 -0
  413. package/dist/services/plugin-state-store.js.map +1 -0
  414. package/dist/services/plugin-stream-bus.d.ts +29 -0
  415. package/dist/services/plugin-stream-bus.d.ts.map +1 -0
  416. package/dist/services/plugin-stream-bus.js +48 -0
  417. package/dist/services/plugin-stream-bus.js.map +1 -0
  418. package/dist/services/plugin-tool-dispatcher.d.ts +180 -0
  419. package/dist/services/plugin-tool-dispatcher.d.ts.map +1 -0
  420. package/dist/services/plugin-tool-dispatcher.js +224 -0
  421. package/dist/services/plugin-tool-dispatcher.js.map +1 -0
  422. package/dist/services/plugin-tool-registry.d.ts +192 -0
  423. package/dist/services/plugin-tool-registry.d.ts.map +1 -0
  424. package/dist/services/plugin-tool-registry.js +224 -0
  425. package/dist/services/plugin-tool-registry.js.map +1 -0
  426. package/dist/services/plugin-worker-manager.d.ts +260 -0
  427. package/dist/services/plugin-worker-manager.d.ts.map +1 -0
  428. package/dist/services/plugin-worker-manager.js +835 -0
  429. package/dist/services/plugin-worker-manager.js.map +1 -0
  430. package/dist/services/projects.d.ts +87 -0
  431. package/dist/services/projects.d.ts.map +1 -0
  432. package/dist/services/projects.js +656 -0
  433. package/dist/services/projects.js.map +1 -0
  434. package/dist/services/quota-windows.d.ts +9 -0
  435. package/dist/services/quota-windows.d.ts.map +1 -0
  436. package/dist/services/quota-windows.js +56 -0
  437. package/dist/services/quota-windows.js.map +1 -0
  438. package/dist/services/run-log-store.d.ts +34 -0
  439. package/dist/services/run-log-store.d.ts.map +1 -0
  440. package/dist/services/run-log-store.js +109 -0
  441. package/dist/services/run-log-store.js.map +1 -0
  442. package/dist/services/secrets.d.ts +510 -0
  443. package/dist/services/secrets.d.ts.map +1 -0
  444. package/dist/services/secrets.js +288 -0
  445. package/dist/services/secrets.js.map +1 -0
  446. package/dist/services/sidebar-badges.d.ts +9 -0
  447. package/dist/services/sidebar-badges.d.ts.map +1 -0
  448. package/dist/services/sidebar-badges.js +33 -0
  449. package/dist/services/sidebar-badges.js.map +1 -0
  450. package/dist/services/work-products.d.ts +14 -0
  451. package/dist/services/work-products.d.ts.map +1 -0
  452. package/dist/services/work-products.js +100 -0
  453. package/dist/services/work-products.js.map +1 -0
  454. package/dist/services/workspace-operation-log-store.d.ts +33 -0
  455. package/dist/services/workspace-operation-log-store.d.ts.map +1 -0
  456. package/dist/services/workspace-operation-log-store.js +110 -0
  457. package/dist/services/workspace-operation-log-store.js.map +1 -0
  458. package/dist/services/workspace-operations.d.ts +44 -0
  459. package/dist/services/workspace-operations.d.ts.map +1 -0
  460. package/dist/services/workspace-operations.js +204 -0
  461. package/dist/services/workspace-operations.js.map +1 -0
  462. package/dist/services/workspace-runtime.d.ts +164 -0
  463. package/dist/services/workspace-runtime.d.ts.map +1 -0
  464. package/dist/services/workspace-runtime.js +1235 -0
  465. package/dist/services/workspace-runtime.js.map +1 -0
  466. package/dist/startup-banner.d.ts +31 -0
  467. package/dist/startup-banner.d.ts.map +1 -0
  468. package/dist/startup-banner.js +117 -0
  469. package/dist/startup-banner.js.map +1 -0
  470. package/dist/storage/index.d.ts +6 -0
  471. package/dist/storage/index.d.ts.map +1 -0
  472. package/dist/storage/index.js +29 -0
  473. package/dist/storage/index.js.map +1 -0
  474. package/dist/storage/local-disk-provider.d.ts +3 -0
  475. package/dist/storage/local-disk-provider.d.ts.map +1 -0
  476. package/dist/storage/local-disk-provider.js +79 -0
  477. package/dist/storage/local-disk-provider.js.map +1 -0
  478. package/dist/storage/provider-registry.d.ts +4 -0
  479. package/dist/storage/provider-registry.d.ts.map +1 -0
  480. package/dist/storage/provider-registry.js +15 -0
  481. package/dist/storage/provider-registry.js.map +1 -0
  482. package/dist/storage/s3-provider.d.ts +11 -0
  483. package/dist/storage/s3-provider.d.ts.map +1 -0
  484. package/dist/storage/s3-provider.js +123 -0
  485. package/dist/storage/s3-provider.js.map +1 -0
  486. package/dist/storage/service.d.ts +3 -0
  487. package/dist/storage/service.d.ts.map +1 -0
  488. package/dist/storage/service.js +120 -0
  489. package/dist/storage/service.js.map +1 -0
  490. package/dist/storage/types.d.ts +55 -0
  491. package/dist/storage/types.d.ts.map +1 -0
  492. package/dist/storage/types.js +2 -0
  493. package/dist/storage/types.js.map +1 -0
  494. package/dist/ui-branding.d.ts +13 -0
  495. package/dist/ui-branding.d.ts.map +1 -0
  496. package/dist/ui-branding.js +187 -0
  497. package/dist/ui-branding.js.map +1 -0
  498. package/dist/version.d.ts +2 -0
  499. package/dist/version.d.ts.map +1 -0
  500. package/dist/version.js +5 -0
  501. package/dist/version.js.map +1 -0
  502. package/dist/wallet/connie-wallet.d.ts +8 -0
  503. package/dist/wallet/connie-wallet.d.ts.map +1 -0
  504. package/dist/wallet/connie-wallet.js +28 -0
  505. package/dist/wallet/connie-wallet.js.map +1 -0
  506. package/dist/wallet/signer-service.d.ts +46 -0
  507. package/dist/wallet/signer-service.d.ts.map +1 -0
  508. package/dist/wallet/signer-service.js +51 -0
  509. package/dist/wallet/signer-service.js.map +1 -0
  510. package/package.json +89 -0
  511. package/skills/paperclip/SKILL.md +310 -0
  512. package/skills/paperclip/references/api-reference.md +561 -0
  513. package/skills/paperclip-create-agent/SKILL.md +139 -0
  514. package/skills/paperclip-create-agent/references/api-reference.md +95 -0
  515. package/skills/paperclip-create-plugin/SKILL.md +101 -0
  516. package/skills/para-memory-files/SKILL.md +104 -0
  517. package/skills/para-memory-files/references/schemas.md +35 -0
  518. package/ui-dist/android-chrome-192x192.png +0 -0
  519. package/ui-dist/android-chrome-512x512.png +0 -0
  520. package/ui-dist/apple-touch-icon.png +0 -0
  521. package/ui-dist/assets/_basePickBy-C3SapTV-.js +1 -0
  522. package/ui-dist/assets/_baseUniq-tAa_W7wz.js +1 -0
  523. package/ui-dist/assets/apl-B4CMkyY2.js +1 -0
  524. package/ui-dist/assets/arc-1aKL9N1m.js +1 -0
  525. package/ui-dist/assets/architectureDiagram-VXUJARFQ-C-BWMHir.js +36 -0
  526. package/ui-dist/assets/asciiarmor-Df11BRmG.js +1 -0
  527. package/ui-dist/assets/asn1-EdZsLKOL.js +1 -0
  528. package/ui-dist/assets/asterisk-B-8jnY81.js +1 -0
  529. package/ui-dist/assets/blockDiagram-VD42YOAC-DiZzNyCp.js +122 -0
  530. package/ui-dist/assets/brainfuck-C4LP7Hcl.js +1 -0
  531. package/ui-dist/assets/c4Diagram-YG6GDRKO-BxkN6K8Z.js +10 -0
  532. package/ui-dist/assets/channel-g6sWbOF_.js +1 -0
  533. package/ui-dist/assets/chunk-4BX2VUAB-dEQLWXMP.js +1 -0
  534. package/ui-dist/assets/chunk-55IACEB6-DOlf6bGB.js +1 -0
  535. package/ui-dist/assets/chunk-B4BG7PRW-D82iJIh4.js +165 -0
  536. package/ui-dist/assets/chunk-DI55MBZ5-Dvu2BjZt.js +220 -0
  537. package/ui-dist/assets/chunk-FMBD7UC4-zLtBDqIV.js +15 -0
  538. package/ui-dist/assets/chunk-QN33PNHL-BsDTCIez.js +1 -0
  539. package/ui-dist/assets/chunk-QZHKN3VN-CVk0cJt0.js +1 -0
  540. package/ui-dist/assets/chunk-TZMSLE5B-BkF4gaW2.js +1 -0
  541. package/ui-dist/assets/classDiagram-2ON5EDUG-D_SGf4i1.js +1 -0
  542. package/ui-dist/assets/classDiagram-v2-WZHVMYZB-D_SGf4i1.js +1 -0
  543. package/ui-dist/assets/clike-B9uivgTg.js +1 -0
  544. package/ui-dist/assets/clojure-BMjYHr_A.js +1 -0
  545. package/ui-dist/assets/clone-CvdGFEjL.js +1 -0
  546. package/ui-dist/assets/cmake-BQqOBYOt.js +1 -0
  547. package/ui-dist/assets/cobol-CWcv1MsR.js +1 -0
  548. package/ui-dist/assets/coffeescript-S37ZYGWr.js +1 -0
  549. package/ui-dist/assets/commonlisp-DBKNyK5s.js +1 -0
  550. package/ui-dist/assets/cose-bilkent-S5V4N54A-DhXJS1Wr.js +1 -0
  551. package/ui-dist/assets/crystal-SjHAIU92.js +1 -0
  552. package/ui-dist/assets/css-BnMrqG3P.js +1 -0
  553. package/ui-dist/assets/cypher-C_CwsFkJ.js +1 -0
  554. package/ui-dist/assets/cytoscape.esm-BQaXIfA_.js +331 -0
  555. package/ui-dist/assets/d-pRatUO7H.js +1 -0
  556. package/ui-dist/assets/dagre-6UL2VRFP-BgSr9M5y.js +4 -0
  557. package/ui-dist/assets/defaultLocale-DX6XiGOO.js +1 -0
  558. package/ui-dist/assets/diagram-PSM6KHXK-RVw3ElKR.js +24 -0
  559. package/ui-dist/assets/diagram-QEK2KX5R-C3K5KNnR.js +43 -0
  560. package/ui-dist/assets/diagram-S2PKOQOG-L2G5bc-i.js +24 -0
  561. package/ui-dist/assets/diff-DbItnlRl.js +1 -0
  562. package/ui-dist/assets/dockerfile-BKs6k2Af.js +1 -0
  563. package/ui-dist/assets/dtd-DF_7sFjM.js +1 -0
  564. package/ui-dist/assets/dylan-DwRh75JA.js +1 -0
  565. package/ui-dist/assets/ebnf-CDyGwa7X.js +1 -0
  566. package/ui-dist/assets/ecl-Cabwm37j.js +1 -0
  567. package/ui-dist/assets/eiffel-CnydiIhH.js +1 -0
  568. package/ui-dist/assets/elm-vLlmbW-K.js +1 -0
  569. package/ui-dist/assets/erDiagram-Q2GNP2WA-B-fEopEW.js +60 -0
  570. package/ui-dist/assets/erlang-BNw1qcRV.js +1 -0
  571. package/ui-dist/assets/factor-kuTfRLto.js +1 -0
  572. package/ui-dist/assets/fcl-Kvtd6kyn.js +1 -0
  573. package/ui-dist/assets/flowDiagram-NV44I4VS-B67TmiaU.js +162 -0
  574. package/ui-dist/assets/forth-Ffai-XNe.js +1 -0
  575. package/ui-dist/assets/fortran-DYz_wnZ1.js +1 -0
  576. package/ui-dist/assets/ganttDiagram-JELNMOA3-DyPUX6hg.js +267 -0
  577. package/ui-dist/assets/gas-Bneqetm1.js +1 -0
  578. package/ui-dist/assets/gherkin-heZmZLOM.js +1 -0
  579. package/ui-dist/assets/gitGraphDiagram-V2S2FVAM--OZSHgOs.js +65 -0
  580. package/ui-dist/assets/graph-BuZXpro8.js +1 -0
  581. package/ui-dist/assets/groovy-D9Dt4D0W.js +1 -0
  582. package/ui-dist/assets/haskell-Cw1EW3IL.js +1 -0
  583. package/ui-dist/assets/haxe-H-WmDvRZ.js +1 -0
  584. package/ui-dist/assets/http-DBlCnlav.js +1 -0
  585. package/ui-dist/assets/idl-BEugSyMb.js +1 -0
  586. package/ui-dist/assets/index-1BDMTcZo.css +1 -0
  587. package/ui-dist/assets/index-5qKrPxpA.js +1 -0
  588. package/ui-dist/assets/index-64g9Tgj8.js +1 -0
  589. package/ui-dist/assets/index-B2BWcxI3.js +1 -0
  590. package/ui-dist/assets/index-BBnLF7s9.js +1 -0
  591. package/ui-dist/assets/index-BcGr-t2T.js +3 -0
  592. package/ui-dist/assets/index-BvFjgW1S.js +1 -0
  593. package/ui-dist/assets/index-Bwu5E0tU.js +1120 -0
  594. package/ui-dist/assets/index-CWdVcwY-.js +7 -0
  595. package/ui-dist/assets/index-CcZ3jKyu.js +1 -0
  596. package/ui-dist/assets/index-CftQkMtr.js +1 -0
  597. package/ui-dist/assets/index-CiuvxTr1.js +13 -0
  598. package/ui-dist/assets/index-CvJFaRY-.js +1 -0
  599. package/ui-dist/assets/index-D2Vi4KQ1.js +1 -0
  600. package/ui-dist/assets/index-D9ei6U88.js +6 -0
  601. package/ui-dist/assets/index-DJHKsR5g.js +1 -0
  602. package/ui-dist/assets/index-DSGN-qDr.js +1 -0
  603. package/ui-dist/assets/index-DVPlvZkH.js +1 -0
  604. package/ui-dist/assets/index-DowQVAaY.js +1 -0
  605. package/ui-dist/assets/index-Dr_PpBCY.js +1 -0
  606. package/ui-dist/assets/index-Ivk3iEUq.js +2 -0
  607. package/ui-dist/assets/index-OVXAhlMW.js +1 -0
  608. package/ui-dist/assets/index-ff_wE0-y.js +1 -0
  609. package/ui-dist/assets/index-tIsOsmOg.js +1 -0
  610. package/ui-dist/assets/infoDiagram-HS3SLOUP-CorcfaW7.js +2 -0
  611. package/ui-dist/assets/init-Gi6I4Gst.js +1 -0
  612. package/ui-dist/assets/javascript-iXu5QeM3.js +1 -0
  613. package/ui-dist/assets/journeyDiagram-XKPGCS4Q-D3K55-qo.js +139 -0
  614. package/ui-dist/assets/julia-DuME0IfC.js +1 -0
  615. package/ui-dist/assets/kanban-definition-3W4ZIXB7-47soc494.js +89 -0
  616. package/ui-dist/assets/katex-O9d3_IXG.js +261 -0
  617. package/ui-dist/assets/layout-mxhxSLBl.js +1 -0
  618. package/ui-dist/assets/linear-D5Y7uAaH.js +1 -0
  619. package/ui-dist/assets/livescript-BwQOo05w.js +1 -0
  620. package/ui-dist/assets/lua-BgMRiT3U.js +1 -0
  621. package/ui-dist/assets/mathematica-DTrFuWx2.js +1 -0
  622. package/ui-dist/assets/mbox-CNhZ1qSd.js +1 -0
  623. package/ui-dist/assets/mermaid.core-7SNUCoBq.js +256 -0
  624. package/ui-dist/assets/mindmap-definition-VGOIOE7T-C37ba4sG.js +68 -0
  625. package/ui-dist/assets/mirc-CjQqDB4T.js +1 -0
  626. package/ui-dist/assets/mllike-CXdrOF99.js +1 -0
  627. package/ui-dist/assets/modelica-Dc1JOy9r.js +1 -0
  628. package/ui-dist/assets/mscgen-BA5vi2Kp.js +1 -0
  629. package/ui-dist/assets/mumps-BT43cFF4.js +1 -0
  630. package/ui-dist/assets/nginx-DdIZxoE0.js +1 -0
  631. package/ui-dist/assets/nsis-LdVXkNf5.js +1 -0
  632. package/ui-dist/assets/ntriples-BfvgReVJ.js +1 -0
  633. package/ui-dist/assets/octave-Ck1zUtKM.js +1 -0
  634. package/ui-dist/assets/ordinal-Cboi1Yqb.js +1 -0
  635. package/ui-dist/assets/oz-BzwKVEFT.js +1 -0
  636. package/ui-dist/assets/pascal--L3eBynH.js +1 -0
  637. package/ui-dist/assets/perl-CdXCOZ3F.js +1 -0
  638. package/ui-dist/assets/pieDiagram-ADFJNKIX-D78e-tVE.js +30 -0
  639. package/ui-dist/assets/pig-CevX1Tat.js +1 -0
  640. package/ui-dist/assets/powershell-CFHJl5sT.js +1 -0
  641. package/ui-dist/assets/properties-C78fOPTZ.js +1 -0
  642. package/ui-dist/assets/protobuf-ChK-085T.js +1 -0
  643. package/ui-dist/assets/pug-DeIclll2.js +1 -0
  644. package/ui-dist/assets/puppet-DMA9R1ak.js +1 -0
  645. package/ui-dist/assets/python-BuPzkPfP.js +1 -0
  646. package/ui-dist/assets/q-pXgVlZs6.js +1 -0
  647. package/ui-dist/assets/quadrantDiagram-AYHSOK5B-CGERMcfi.js +7 -0
  648. package/ui-dist/assets/r-B6wPVr8A.js +1 -0
  649. package/ui-dist/assets/requirementDiagram-UZGBJVZJ-vZWAyhU1.js +64 -0
  650. package/ui-dist/assets/rpm-CTu-6PCP.js +1 -0
  651. package/ui-dist/assets/ruby-B2Rjki9n.js +1 -0
  652. package/ui-dist/assets/sankeyDiagram-TZEHDZUN-C51wCQX7.js +10 -0
  653. package/ui-dist/assets/sas-B4kiWyti.js +1 -0
  654. package/ui-dist/assets/scheme-C41bIUwD.js +1 -0
  655. package/ui-dist/assets/sequenceDiagram-WL72ISMW-eCSbKUaE.js +145 -0
  656. package/ui-dist/assets/shell-CjFT_Tl9.js +1 -0
  657. package/ui-dist/assets/sieve-C3Gn_uJK.js +1 -0
  658. package/ui-dist/assets/simple-mode-GW_nhZxv.js +1 -0
  659. package/ui-dist/assets/smalltalk-CnHTOXQT.js +1 -0
  660. package/ui-dist/assets/solr-DehyRSwq.js +1 -0
  661. package/ui-dist/assets/sparql-DkYu6x3z.js +1 -0
  662. package/ui-dist/assets/spreadsheet-BCZA_wO0.js +1 -0
  663. package/ui-dist/assets/sql-D0XecflT.js +1 -0
  664. package/ui-dist/assets/stateDiagram-FKZM4ZOC-H3lI51qU.js +1 -0
  665. package/ui-dist/assets/stateDiagram-v2-4FDKWEC3--Q50auwn.js +1 -0
  666. package/ui-dist/assets/stex-C3f8Ysf7.js +1 -0
  667. package/ui-dist/assets/stylus-B533Al4x.js +1 -0
  668. package/ui-dist/assets/swift-BzpIVaGY.js +1 -0
  669. package/ui-dist/assets/tcl-DVfN8rqt.js +1 -0
  670. package/ui-dist/assets/textile-CnDTJFAw.js +1 -0
  671. package/ui-dist/assets/tiddlywiki-DO-Gjzrf.js +1 -0
  672. package/ui-dist/assets/tiki-DGYXhP31.js +1 -0
  673. package/ui-dist/assets/timeline-definition-IT6M3QCI-CP9CNQLU.js +61 -0
  674. package/ui-dist/assets/toml-Bm5Em-hy.js +1 -0
  675. package/ui-dist/assets/treemap-GDKQZRPO-BfRhErz4.js +162 -0
  676. package/ui-dist/assets/troff-wAsdV37c.js +1 -0
  677. package/ui-dist/assets/ttcn-CfJYG6tj.js +1 -0
  678. package/ui-dist/assets/ttcn-cfg-B9xdYoR4.js +1 -0
  679. package/ui-dist/assets/turtle-B1tBg_DP.js +1 -0
  680. package/ui-dist/assets/vb-CmGdzxic.js +1 -0
  681. package/ui-dist/assets/vbscript-BuJXcnF6.js +1 -0
  682. package/ui-dist/assets/velocity-D8B20fx6.js +1 -0
  683. package/ui-dist/assets/verilog-C6RDOZhf.js +1 -0
  684. package/ui-dist/assets/vhdl-lSbBsy5d.js +1 -0
  685. package/ui-dist/assets/webidl-ZXfAyPTL.js +1 -0
  686. package/ui-dist/assets/xquery-DzFWVndE.js +1 -0
  687. package/ui-dist/assets/xychartDiagram-PRI3JC2R-BSy7gZ_Q.js +7 -0
  688. package/ui-dist/assets/yacas-BJ4BC0dw.js +1 -0
  689. package/ui-dist/assets/z80-Hz9HOZM7.js +1 -0
  690. package/ui-dist/brands/opencode-logo-dark-square.svg +18 -0
  691. package/ui-dist/brands/opencode-logo-light-square.svg +18 -0
  692. package/ui-dist/favicon-16x16.png +0 -0
  693. package/ui-dist/favicon-32x32.png +0 -0
  694. package/ui-dist/favicon.ico +0 -0
  695. package/ui-dist/favicon.svg +9 -0
  696. package/ui-dist/index.html +48 -0
  697. package/ui-dist/site.webmanifest +30 -0
  698. package/ui-dist/sw.js +42 -0
  699. package/ui-dist/worktree-favicon-16x16.png +0 -0
  700. package/ui-dist/worktree-favicon-32x32.png +0 -0
  701. package/ui-dist/worktree-favicon.ico +0 -0
  702. package/ui-dist/worktree-favicon.svg +9 -0
@@ -0,0 +1,1308 @@
1
+ import { and, asc, desc, eq, inArray, isNull, or, sql } from "drizzle-orm";
2
+ import { agents, assets, companies, companyMemberships, documents, goals, heartbeatRuns, executionWorkspaces, issueAttachments, issueLabels, issueComments, issueDocuments, issueReadStates, issues, labels, projectWorkspaces, projects, } from "@paperclipai_dld/db";
3
+ import { extractProjectMentionIds } from "@paperclipai_dld/shared";
4
+ import { conflict, notFound, unprocessable } from "../errors.js";
5
+ import { defaultIssueExecutionWorkspaceSettingsForProject, gateProjectExecutionWorkspacePolicy, parseProjectExecutionWorkspacePolicy, } from "./execution-workspace-policy.js";
6
+ import { instanceSettingsService } from "./instance-settings.js";
7
+ import { redactCurrentUserText } from "../log-redaction.js";
8
+ import { resolveIssueGoalId, resolveNextIssueGoalId } from "./issue-goal-fallback.js";
9
+ import { getDefaultCompanyGoal } from "./goals.js";
10
+ const ALL_ISSUE_STATUSES = ["backlog", "todo", "in_progress", "in_review", "blocked", "done", "cancelled"];
11
+ const MAX_ISSUE_COMMENT_PAGE_LIMIT = 500;
12
+ const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
13
+ function assertTransition(from, to) {
14
+ if (from === to)
15
+ return;
16
+ if (!ALL_ISSUE_STATUSES.includes(to)) {
17
+ throw conflict(`Unknown issue status: ${to}`);
18
+ }
19
+ }
20
+ function applyStatusSideEffects(status, patch) {
21
+ if (!status)
22
+ return patch;
23
+ if (status === "in_progress" && !patch.startedAt) {
24
+ patch.startedAt = new Date();
25
+ }
26
+ if (status === "done") {
27
+ patch.completedAt = new Date();
28
+ }
29
+ if (status === "cancelled") {
30
+ patch.cancelledAt = new Date();
31
+ }
32
+ return patch;
33
+ }
34
+ function redactIssueComment(comment) {
35
+ return {
36
+ ...comment,
37
+ body: redactCurrentUserText(comment.body),
38
+ };
39
+ }
40
+ function sameRunLock(checkoutRunId, actorRunId) {
41
+ if (actorRunId)
42
+ return checkoutRunId === actorRunId;
43
+ return checkoutRunId == null;
44
+ }
45
+ const TERMINAL_HEARTBEAT_RUN_STATUSES = new Set(["succeeded", "failed", "cancelled", "timed_out"]);
46
+ function escapeLikePattern(value) {
47
+ return value.replace(/[\\%_]/g, "\\$&");
48
+ }
49
+ function touchedByUserCondition(companyId, userId) {
50
+ return sql `
51
+ (
52
+ ${issues.createdByUserId} = ${userId}
53
+ OR ${issues.assigneeUserId} = ${userId}
54
+ OR EXISTS (
55
+ SELECT 1
56
+ FROM ${issueReadStates}
57
+ WHERE ${issueReadStates.issueId} = ${issues.id}
58
+ AND ${issueReadStates.companyId} = ${companyId}
59
+ AND ${issueReadStates.userId} = ${userId}
60
+ )
61
+ OR EXISTS (
62
+ SELECT 1
63
+ FROM ${issueComments}
64
+ WHERE ${issueComments.issueId} = ${issues.id}
65
+ AND ${issueComments.companyId} = ${companyId}
66
+ AND ${issueComments.authorUserId} = ${userId}
67
+ )
68
+ )
69
+ `;
70
+ }
71
+ function myLastCommentAtExpr(companyId, userId) {
72
+ return sql `
73
+ (
74
+ SELECT MAX(${issueComments.createdAt})
75
+ FROM ${issueComments}
76
+ WHERE ${issueComments.issueId} = ${issues.id}
77
+ AND ${issueComments.companyId} = ${companyId}
78
+ AND ${issueComments.authorUserId} = ${userId}
79
+ )
80
+ `;
81
+ }
82
+ function myLastReadAtExpr(companyId, userId) {
83
+ return sql `
84
+ (
85
+ SELECT MAX(${issueReadStates.lastReadAt})
86
+ FROM ${issueReadStates}
87
+ WHERE ${issueReadStates.issueId} = ${issues.id}
88
+ AND ${issueReadStates.companyId} = ${companyId}
89
+ AND ${issueReadStates.userId} = ${userId}
90
+ )
91
+ `;
92
+ }
93
+ function myLastTouchAtExpr(companyId, userId) {
94
+ const myLastCommentAt = myLastCommentAtExpr(companyId, userId);
95
+ const myLastReadAt = myLastReadAtExpr(companyId, userId);
96
+ return sql `
97
+ GREATEST(
98
+ COALESCE(${myLastCommentAt}, to_timestamp(0)),
99
+ COALESCE(${myLastReadAt}, to_timestamp(0)),
100
+ COALESCE(CASE WHEN ${issues.createdByUserId} = ${userId} THEN ${issues.createdAt} ELSE NULL END, to_timestamp(0)),
101
+ COALESCE(CASE WHEN ${issues.assigneeUserId} = ${userId} THEN ${issues.updatedAt} ELSE NULL END, to_timestamp(0))
102
+ )
103
+ `;
104
+ }
105
+ function unreadForUserCondition(companyId, userId) {
106
+ const touchedCondition = touchedByUserCondition(companyId, userId);
107
+ const myLastTouchAt = myLastTouchAtExpr(companyId, userId);
108
+ return sql `
109
+ (
110
+ ${touchedCondition}
111
+ AND EXISTS (
112
+ SELECT 1
113
+ FROM ${issueComments}
114
+ WHERE ${issueComments.issueId} = ${issues.id}
115
+ AND ${issueComments.companyId} = ${companyId}
116
+ AND (
117
+ ${issueComments.authorUserId} IS NULL
118
+ OR ${issueComments.authorUserId} <> ${userId}
119
+ )
120
+ AND ${issueComments.createdAt} > ${myLastTouchAt}
121
+ )
122
+ )
123
+ `;
124
+ }
125
+ export function deriveIssueUserContext(issue, userId, stats) {
126
+ const normalizeDate = (value) => {
127
+ if (!value)
128
+ return null;
129
+ if (value instanceof Date)
130
+ return Number.isNaN(value.getTime()) ? null : value;
131
+ const parsed = new Date(value);
132
+ return Number.isNaN(parsed.getTime()) ? null : parsed;
133
+ };
134
+ const myLastCommentAt = normalizeDate(stats?.myLastCommentAt);
135
+ const myLastReadAt = normalizeDate(stats?.myLastReadAt);
136
+ const createdTouchAt = issue.createdByUserId === userId ? normalizeDate(issue.createdAt) : null;
137
+ const assignedTouchAt = issue.assigneeUserId === userId ? normalizeDate(issue.updatedAt) : null;
138
+ const myLastTouchAt = [myLastCommentAt, myLastReadAt, createdTouchAt, assignedTouchAt]
139
+ .filter((value) => value instanceof Date)
140
+ .sort((a, b) => b.getTime() - a.getTime())[0] ?? null;
141
+ const lastExternalCommentAt = normalizeDate(stats?.lastExternalCommentAt);
142
+ const isUnreadForMe = Boolean(myLastTouchAt &&
143
+ lastExternalCommentAt &&
144
+ lastExternalCommentAt.getTime() > myLastTouchAt.getTime());
145
+ return {
146
+ myLastTouchAt,
147
+ lastExternalCommentAt,
148
+ isUnreadForMe,
149
+ };
150
+ }
151
+ async function labelMapForIssues(dbOrTx, issueIds) {
152
+ const map = new Map();
153
+ if (issueIds.length === 0)
154
+ return map;
155
+ const rows = await dbOrTx
156
+ .select({
157
+ issueId: issueLabels.issueId,
158
+ label: labels,
159
+ })
160
+ .from(issueLabels)
161
+ .innerJoin(labels, eq(issueLabels.labelId, labels.id))
162
+ .where(inArray(issueLabels.issueId, issueIds))
163
+ .orderBy(asc(labels.name), asc(labels.id));
164
+ for (const row of rows) {
165
+ const existing = map.get(row.issueId);
166
+ if (existing)
167
+ existing.push(row.label);
168
+ else
169
+ map.set(row.issueId, [row.label]);
170
+ }
171
+ return map;
172
+ }
173
+ async function withIssueLabels(dbOrTx, rows) {
174
+ if (rows.length === 0)
175
+ return [];
176
+ const labelsByIssueId = await labelMapForIssues(dbOrTx, rows.map((row) => row.id));
177
+ return rows.map((row) => {
178
+ const issueLabels = labelsByIssueId.get(row.id) ?? [];
179
+ return {
180
+ ...row,
181
+ labels: issueLabels,
182
+ labelIds: issueLabels.map((label) => label.id),
183
+ };
184
+ });
185
+ }
186
+ const ACTIVE_RUN_STATUSES = ["queued", "running"];
187
+ async function activeRunMapForIssues(dbOrTx, issueRows) {
188
+ const map = new Map();
189
+ const runIds = issueRows
190
+ .map((row) => row.executionRunId)
191
+ .filter((id) => id != null);
192
+ if (runIds.length === 0)
193
+ return map;
194
+ const rows = await dbOrTx
195
+ .select({
196
+ id: heartbeatRuns.id,
197
+ status: heartbeatRuns.status,
198
+ agentId: heartbeatRuns.agentId,
199
+ invocationSource: heartbeatRuns.invocationSource,
200
+ triggerDetail: heartbeatRuns.triggerDetail,
201
+ startedAt: heartbeatRuns.startedAt,
202
+ finishedAt: heartbeatRuns.finishedAt,
203
+ createdAt: heartbeatRuns.createdAt,
204
+ })
205
+ .from(heartbeatRuns)
206
+ .where(and(inArray(heartbeatRuns.id, runIds), inArray(heartbeatRuns.status, ACTIVE_RUN_STATUSES)));
207
+ for (const row of rows) {
208
+ map.set(row.id, row);
209
+ }
210
+ return map;
211
+ }
212
+ function withActiveRuns(issueRows, runMap) {
213
+ return issueRows.map((row) => ({
214
+ ...row,
215
+ activeRun: row.executionRunId ? (runMap.get(row.executionRunId) ?? null) : null,
216
+ }));
217
+ }
218
+ export function issueService(db) {
219
+ const instanceSettings = instanceSettingsService(db);
220
+ async function assertAssignableAgent(companyId, agentId) {
221
+ const assignee = await db
222
+ .select({
223
+ id: agents.id,
224
+ companyId: agents.companyId,
225
+ status: agents.status,
226
+ })
227
+ .from(agents)
228
+ .where(eq(agents.id, agentId))
229
+ .then((rows) => rows[0] ?? null);
230
+ if (!assignee)
231
+ throw notFound("Assignee agent not found");
232
+ if (assignee.companyId !== companyId) {
233
+ throw unprocessable("Assignee must belong to same company");
234
+ }
235
+ if (assignee.status === "pending_approval") {
236
+ throw conflict("Cannot assign work to pending approval agents");
237
+ }
238
+ if (assignee.status === "terminated") {
239
+ throw conflict("Cannot assign work to terminated agents");
240
+ }
241
+ }
242
+ async function assertAssignableUser(companyId, userId) {
243
+ const membership = await db
244
+ .select({ id: companyMemberships.id })
245
+ .from(companyMemberships)
246
+ .where(and(eq(companyMemberships.companyId, companyId), eq(companyMemberships.principalType, "user"), eq(companyMemberships.principalId, userId), eq(companyMemberships.status, "active")))
247
+ .then((rows) => rows[0] ?? null);
248
+ if (!membership) {
249
+ throw notFound("Assignee user not found");
250
+ }
251
+ }
252
+ async function assertValidProjectWorkspace(companyId, projectId, projectWorkspaceId) {
253
+ const workspace = await db
254
+ .select({
255
+ id: projectWorkspaces.id,
256
+ companyId: projectWorkspaces.companyId,
257
+ projectId: projectWorkspaces.projectId,
258
+ })
259
+ .from(projectWorkspaces)
260
+ .where(eq(projectWorkspaces.id, projectWorkspaceId))
261
+ .then((rows) => rows[0] ?? null);
262
+ if (!workspace)
263
+ throw notFound("Project workspace not found");
264
+ if (workspace.companyId !== companyId)
265
+ throw unprocessable("Project workspace must belong to same company");
266
+ if (projectId && workspace.projectId !== projectId) {
267
+ throw unprocessable("Project workspace must belong to the selected project");
268
+ }
269
+ }
270
+ async function assertValidExecutionWorkspace(companyId, projectId, executionWorkspaceId) {
271
+ const workspace = await db
272
+ .select({
273
+ id: executionWorkspaces.id,
274
+ companyId: executionWorkspaces.companyId,
275
+ projectId: executionWorkspaces.projectId,
276
+ })
277
+ .from(executionWorkspaces)
278
+ .where(eq(executionWorkspaces.id, executionWorkspaceId))
279
+ .then((rows) => rows[0] ?? null);
280
+ if (!workspace)
281
+ throw notFound("Execution workspace not found");
282
+ if (workspace.companyId !== companyId)
283
+ throw unprocessable("Execution workspace must belong to same company");
284
+ if (projectId && workspace.projectId !== projectId) {
285
+ throw unprocessable("Execution workspace must belong to the selected project");
286
+ }
287
+ }
288
+ async function assertValidLabelIds(companyId, labelIds, dbOrTx = db) {
289
+ if (labelIds.length === 0)
290
+ return;
291
+ const existing = await dbOrTx
292
+ .select({ id: labels.id })
293
+ .from(labels)
294
+ .where(and(eq(labels.companyId, companyId), inArray(labels.id, labelIds)));
295
+ if (existing.length !== new Set(labelIds).size) {
296
+ throw unprocessable("One or more labels are invalid for this company");
297
+ }
298
+ }
299
+ async function syncIssueLabels(issueId, companyId, labelIds, dbOrTx = db) {
300
+ const deduped = [...new Set(labelIds)];
301
+ await assertValidLabelIds(companyId, deduped, dbOrTx);
302
+ await dbOrTx.delete(issueLabels).where(eq(issueLabels.issueId, issueId));
303
+ if (deduped.length === 0)
304
+ return;
305
+ await dbOrTx.insert(issueLabels).values(deduped.map((labelId) => ({
306
+ issueId,
307
+ labelId,
308
+ companyId,
309
+ })));
310
+ }
311
+ async function isTerminalOrMissingHeartbeatRun(runId) {
312
+ const run = await db
313
+ .select({ status: heartbeatRuns.status })
314
+ .from(heartbeatRuns)
315
+ .where(eq(heartbeatRuns.id, runId))
316
+ .then((rows) => rows[0] ?? null);
317
+ if (!run)
318
+ return true;
319
+ return TERMINAL_HEARTBEAT_RUN_STATUSES.has(run.status);
320
+ }
321
+ async function adoptStaleCheckoutRun(input) {
322
+ const stale = await isTerminalOrMissingHeartbeatRun(input.expectedCheckoutRunId);
323
+ if (!stale)
324
+ return null;
325
+ const now = new Date();
326
+ const adopted = await db
327
+ .update(issues)
328
+ .set({
329
+ checkoutRunId: input.actorRunId,
330
+ executionRunId: input.actorRunId,
331
+ executionLockedAt: now,
332
+ updatedAt: now,
333
+ })
334
+ .where(and(eq(issues.id, input.issueId), eq(issues.status, "in_progress"), eq(issues.assigneeAgentId, input.actorAgentId), eq(issues.checkoutRunId, input.expectedCheckoutRunId)))
335
+ .returning({
336
+ id: issues.id,
337
+ status: issues.status,
338
+ assigneeAgentId: issues.assigneeAgentId,
339
+ checkoutRunId: issues.checkoutRunId,
340
+ executionRunId: issues.executionRunId,
341
+ })
342
+ .then((rows) => rows[0] ?? null);
343
+ return adopted;
344
+ }
345
+ return {
346
+ list: async (companyId, filters) => {
347
+ const conditions = [eq(issues.companyId, companyId)];
348
+ const touchedByUserId = filters?.touchedByUserId?.trim() || undefined;
349
+ const unreadForUserId = filters?.unreadForUserId?.trim() || undefined;
350
+ const contextUserId = unreadForUserId ?? touchedByUserId;
351
+ const rawSearch = filters?.q?.trim() ?? "";
352
+ const hasSearch = rawSearch.length > 0;
353
+ const escapedSearch = hasSearch ? escapeLikePattern(rawSearch) : "";
354
+ const startsWithPattern = `${escapedSearch}%`;
355
+ const containsPattern = `%${escapedSearch}%`;
356
+ const titleStartsWithMatch = sql `${issues.title} ILIKE ${startsWithPattern} ESCAPE '\\'`;
357
+ const titleContainsMatch = sql `${issues.title} ILIKE ${containsPattern} ESCAPE '\\'`;
358
+ const identifierStartsWithMatch = sql `${issues.identifier} ILIKE ${startsWithPattern} ESCAPE '\\'`;
359
+ const identifierContainsMatch = sql `${issues.identifier} ILIKE ${containsPattern} ESCAPE '\\'`;
360
+ const descriptionContainsMatch = sql `${issues.description} ILIKE ${containsPattern} ESCAPE '\\'`;
361
+ const commentContainsMatch = sql `
362
+ EXISTS (
363
+ SELECT 1
364
+ FROM ${issueComments}
365
+ WHERE ${issueComments.issueId} = ${issues.id}
366
+ AND ${issueComments.companyId} = ${companyId}
367
+ AND ${issueComments.body} ILIKE ${containsPattern} ESCAPE '\\'
368
+ )
369
+ `;
370
+ if (filters?.status) {
371
+ const statuses = filters.status.split(",").map((s) => s.trim());
372
+ conditions.push(statuses.length === 1 ? eq(issues.status, statuses[0]) : inArray(issues.status, statuses));
373
+ }
374
+ if (filters?.assigneeAgentId) {
375
+ conditions.push(eq(issues.assigneeAgentId, filters.assigneeAgentId));
376
+ }
377
+ if (filters?.assigneeUserId) {
378
+ conditions.push(eq(issues.assigneeUserId, filters.assigneeUserId));
379
+ }
380
+ if (touchedByUserId) {
381
+ conditions.push(touchedByUserCondition(companyId, touchedByUserId));
382
+ }
383
+ if (unreadForUserId) {
384
+ conditions.push(unreadForUserCondition(companyId, unreadForUserId));
385
+ }
386
+ if (filters?.projectId)
387
+ conditions.push(eq(issues.projectId, filters.projectId));
388
+ if (filters?.parentId)
389
+ conditions.push(eq(issues.parentId, filters.parentId));
390
+ if (filters?.labelId) {
391
+ const labeledIssueIds = await db
392
+ .select({ issueId: issueLabels.issueId })
393
+ .from(issueLabels)
394
+ .where(and(eq(issueLabels.companyId, companyId), eq(issueLabels.labelId, filters.labelId)));
395
+ if (labeledIssueIds.length === 0)
396
+ return [];
397
+ conditions.push(inArray(issues.id, labeledIssueIds.map((row) => row.issueId)));
398
+ }
399
+ if (hasSearch) {
400
+ conditions.push(or(titleContainsMatch, identifierContainsMatch, descriptionContainsMatch, commentContainsMatch));
401
+ }
402
+ conditions.push(isNull(issues.hiddenAt));
403
+ const priorityOrder = sql `CASE ${issues.priority} WHEN 'critical' THEN 0 WHEN 'high' THEN 1 WHEN 'medium' THEN 2 WHEN 'low' THEN 3 ELSE 4 END`;
404
+ const searchOrder = sql `
405
+ CASE
406
+ WHEN ${titleStartsWithMatch} THEN 0
407
+ WHEN ${titleContainsMatch} THEN 1
408
+ WHEN ${identifierStartsWithMatch} THEN 2
409
+ WHEN ${identifierContainsMatch} THEN 3
410
+ WHEN ${descriptionContainsMatch} THEN 4
411
+ WHEN ${commentContainsMatch} THEN 5
412
+ ELSE 6
413
+ END
414
+ `;
415
+ const rows = await db
416
+ .select()
417
+ .from(issues)
418
+ .where(and(...conditions))
419
+ .orderBy(hasSearch ? asc(searchOrder) : asc(priorityOrder), asc(priorityOrder), desc(issues.updatedAt));
420
+ const withLabels = await withIssueLabels(db, rows);
421
+ const runMap = await activeRunMapForIssues(db, withLabels);
422
+ const withRuns = withActiveRuns(withLabels, runMap);
423
+ if (!contextUserId || withRuns.length === 0) {
424
+ return withRuns;
425
+ }
426
+ const issueIds = withRuns.map((row) => row.id);
427
+ const statsRows = await db
428
+ .select({
429
+ issueId: issueComments.issueId,
430
+ myLastCommentAt: sql `
431
+ MAX(CASE WHEN ${issueComments.authorUserId} = ${contextUserId} THEN ${issueComments.createdAt} END)
432
+ `,
433
+ lastExternalCommentAt: sql `
434
+ MAX(
435
+ CASE
436
+ WHEN ${issueComments.authorUserId} IS NULL OR ${issueComments.authorUserId} <> ${contextUserId}
437
+ THEN ${issueComments.createdAt}
438
+ END
439
+ )
440
+ `,
441
+ })
442
+ .from(issueComments)
443
+ .where(and(eq(issueComments.companyId, companyId), inArray(issueComments.issueId, issueIds)))
444
+ .groupBy(issueComments.issueId);
445
+ const readRows = await db
446
+ .select({
447
+ issueId: issueReadStates.issueId,
448
+ myLastReadAt: issueReadStates.lastReadAt,
449
+ })
450
+ .from(issueReadStates)
451
+ .where(and(eq(issueReadStates.companyId, companyId), eq(issueReadStates.userId, contextUserId), inArray(issueReadStates.issueId, issueIds)));
452
+ const statsByIssueId = new Map(statsRows.map((row) => [row.issueId, row]));
453
+ const readByIssueId = new Map(readRows.map((row) => [row.issueId, row.myLastReadAt]));
454
+ return withRuns.map((row) => ({
455
+ ...row,
456
+ ...deriveIssueUserContext(row, contextUserId, {
457
+ myLastCommentAt: statsByIssueId.get(row.id)?.myLastCommentAt ?? null,
458
+ myLastReadAt: readByIssueId.get(row.id) ?? null,
459
+ lastExternalCommentAt: statsByIssueId.get(row.id)?.lastExternalCommentAt ?? null,
460
+ }),
461
+ }));
462
+ },
463
+ countUnreadTouchedByUser: async (companyId, userId, status) => {
464
+ const conditions = [
465
+ eq(issues.companyId, companyId),
466
+ isNull(issues.hiddenAt),
467
+ unreadForUserCondition(companyId, userId),
468
+ ];
469
+ if (status) {
470
+ const statuses = status.split(",").map((s) => s.trim()).filter(Boolean);
471
+ if (statuses.length === 1) {
472
+ conditions.push(eq(issues.status, statuses[0]));
473
+ }
474
+ else if (statuses.length > 1) {
475
+ conditions.push(inArray(issues.status, statuses));
476
+ }
477
+ }
478
+ const [row] = await db
479
+ .select({ count: sql `count(*)` })
480
+ .from(issues)
481
+ .where(and(...conditions));
482
+ return Number(row?.count ?? 0);
483
+ },
484
+ markRead: async (companyId, issueId, userId, readAt = new Date()) => {
485
+ const now = new Date();
486
+ const [row] = await db
487
+ .insert(issueReadStates)
488
+ .values({
489
+ companyId,
490
+ issueId,
491
+ userId,
492
+ lastReadAt: readAt,
493
+ updatedAt: now,
494
+ })
495
+ .onConflictDoUpdate({
496
+ target: [issueReadStates.companyId, issueReadStates.issueId, issueReadStates.userId],
497
+ set: {
498
+ lastReadAt: readAt,
499
+ updatedAt: now,
500
+ },
501
+ })
502
+ .returning();
503
+ return row;
504
+ },
505
+ getById: async (id) => {
506
+ const row = await db
507
+ .select()
508
+ .from(issues)
509
+ .where(eq(issues.id, id))
510
+ .then((rows) => rows[0] ?? null);
511
+ if (!row)
512
+ return null;
513
+ const [enriched] = await withIssueLabels(db, [row]);
514
+ return enriched;
515
+ },
516
+ getByIdentifier: async (identifier) => {
517
+ const row = await db
518
+ .select()
519
+ .from(issues)
520
+ .where(eq(issues.identifier, identifier.toUpperCase()))
521
+ .then((rows) => rows[0] ?? null);
522
+ if (!row)
523
+ return null;
524
+ const [enriched] = await withIssueLabels(db, [row]);
525
+ return enriched;
526
+ },
527
+ create: async (companyId, data) => {
528
+ const { labelIds: inputLabelIds, ...issueData } = data;
529
+ const isolatedWorkspacesEnabled = (await instanceSettings.getExperimental()).enableIsolatedWorkspaces;
530
+ if (!isolatedWorkspacesEnabled) {
531
+ delete issueData.executionWorkspaceId;
532
+ delete issueData.executionWorkspacePreference;
533
+ delete issueData.executionWorkspaceSettings;
534
+ }
535
+ if (data.assigneeAgentId && data.assigneeUserId) {
536
+ throw unprocessable("Issue can only have one assignee");
537
+ }
538
+ if (data.assigneeAgentId) {
539
+ await assertAssignableAgent(companyId, data.assigneeAgentId);
540
+ }
541
+ if (data.assigneeUserId) {
542
+ await assertAssignableUser(companyId, data.assigneeUserId);
543
+ }
544
+ if (data.projectWorkspaceId) {
545
+ await assertValidProjectWorkspace(companyId, data.projectId, data.projectWorkspaceId);
546
+ }
547
+ if (data.executionWorkspaceId) {
548
+ await assertValidExecutionWorkspace(companyId, data.projectId, data.executionWorkspaceId);
549
+ }
550
+ if (data.status === "in_progress" && !data.assigneeAgentId && !data.assigneeUserId) {
551
+ throw unprocessable("in_progress issues require an assignee");
552
+ }
553
+ return db.transaction(async (tx) => {
554
+ const defaultCompanyGoal = await getDefaultCompanyGoal(tx, companyId);
555
+ let executionWorkspaceSettings = issueData.executionWorkspaceSettings ?? null;
556
+ if (executionWorkspaceSettings == null && issueData.projectId) {
557
+ const project = await tx
558
+ .select({ executionWorkspacePolicy: projects.executionWorkspacePolicy })
559
+ .from(projects)
560
+ .where(and(eq(projects.id, issueData.projectId), eq(projects.companyId, companyId)))
561
+ .then((rows) => rows[0] ?? null);
562
+ executionWorkspaceSettings =
563
+ defaultIssueExecutionWorkspaceSettingsForProject(gateProjectExecutionWorkspacePolicy(parseProjectExecutionWorkspacePolicy(project?.executionWorkspacePolicy), isolatedWorkspacesEnabled));
564
+ }
565
+ let projectWorkspaceId = issueData.projectWorkspaceId ?? null;
566
+ if (!projectWorkspaceId && issueData.projectId) {
567
+ const project = await tx
568
+ .select({
569
+ executionWorkspacePolicy: projects.executionWorkspacePolicy,
570
+ })
571
+ .from(projects)
572
+ .where(and(eq(projects.id, issueData.projectId), eq(projects.companyId, companyId)))
573
+ .then((rows) => rows[0] ?? null);
574
+ const projectPolicy = parseProjectExecutionWorkspacePolicy(project?.executionWorkspacePolicy);
575
+ projectWorkspaceId = projectPolicy?.defaultProjectWorkspaceId ?? null;
576
+ if (!projectWorkspaceId) {
577
+ projectWorkspaceId = await tx
578
+ .select({ id: projectWorkspaces.id })
579
+ .from(projectWorkspaces)
580
+ .where(and(eq(projectWorkspaces.projectId, issueData.projectId), eq(projectWorkspaces.companyId, companyId)))
581
+ .orderBy(desc(projectWorkspaces.isPrimary), asc(projectWorkspaces.createdAt), asc(projectWorkspaces.id))
582
+ .then((rows) => rows[0]?.id ?? null);
583
+ }
584
+ }
585
+ const [company] = await tx
586
+ .update(companies)
587
+ .set({ issueCounter: sql `${companies.issueCounter} + 1` })
588
+ .where(eq(companies.id, companyId))
589
+ .returning({ issueCounter: companies.issueCounter, issuePrefix: companies.issuePrefix });
590
+ const issueNumber = company.issueCounter;
591
+ const identifier = `${company.issuePrefix}-${issueNumber}`;
592
+ const values = {
593
+ ...issueData,
594
+ goalId: resolveIssueGoalId({
595
+ projectId: issueData.projectId,
596
+ goalId: issueData.goalId,
597
+ defaultGoalId: defaultCompanyGoal?.id ?? null,
598
+ }),
599
+ ...(projectWorkspaceId ? { projectWorkspaceId } : {}),
600
+ ...(executionWorkspaceSettings ? { executionWorkspaceSettings } : {}),
601
+ companyId,
602
+ issueNumber,
603
+ identifier,
604
+ };
605
+ if (values.status === "in_progress" && !values.startedAt) {
606
+ values.startedAt = new Date();
607
+ }
608
+ if (values.status === "done") {
609
+ values.completedAt = new Date();
610
+ }
611
+ if (values.status === "cancelled") {
612
+ values.cancelledAt = new Date();
613
+ }
614
+ const [issue] = await tx.insert(issues).values(values).returning();
615
+ if (inputLabelIds) {
616
+ await syncIssueLabels(issue.id, companyId, inputLabelIds, tx);
617
+ }
618
+ const [enriched] = await withIssueLabels(tx, [issue]);
619
+ return enriched;
620
+ });
621
+ },
622
+ update: async (id, data) => {
623
+ const existing = await db
624
+ .select()
625
+ .from(issues)
626
+ .where(eq(issues.id, id))
627
+ .then((rows) => rows[0] ?? null);
628
+ if (!existing)
629
+ return null;
630
+ const { labelIds: nextLabelIds, ...issueData } = data;
631
+ const isolatedWorkspacesEnabled = (await instanceSettings.getExperimental()).enableIsolatedWorkspaces;
632
+ if (!isolatedWorkspacesEnabled) {
633
+ delete issueData.executionWorkspaceId;
634
+ delete issueData.executionWorkspacePreference;
635
+ delete issueData.executionWorkspaceSettings;
636
+ }
637
+ if (issueData.status) {
638
+ assertTransition(existing.status, issueData.status);
639
+ }
640
+ const patch = {
641
+ ...issueData,
642
+ updatedAt: new Date(),
643
+ };
644
+ const nextAssigneeAgentId = issueData.assigneeAgentId !== undefined ? issueData.assigneeAgentId : existing.assigneeAgentId;
645
+ const nextAssigneeUserId = issueData.assigneeUserId !== undefined ? issueData.assigneeUserId : existing.assigneeUserId;
646
+ if (nextAssigneeAgentId && nextAssigneeUserId) {
647
+ throw unprocessable("Issue can only have one assignee");
648
+ }
649
+ if (patch.status === "in_progress" && !nextAssigneeAgentId && !nextAssigneeUserId) {
650
+ throw unprocessable("in_progress issues require an assignee");
651
+ }
652
+ if (issueData.assigneeAgentId) {
653
+ await assertAssignableAgent(existing.companyId, issueData.assigneeAgentId);
654
+ }
655
+ if (issueData.assigneeUserId) {
656
+ await assertAssignableUser(existing.companyId, issueData.assigneeUserId);
657
+ }
658
+ const nextProjectId = issueData.projectId !== undefined ? issueData.projectId : existing.projectId;
659
+ const nextProjectWorkspaceId = issueData.projectWorkspaceId !== undefined ? issueData.projectWorkspaceId : existing.projectWorkspaceId;
660
+ const nextExecutionWorkspaceId = issueData.executionWorkspaceId !== undefined ? issueData.executionWorkspaceId : existing.executionWorkspaceId;
661
+ if (nextProjectWorkspaceId) {
662
+ await assertValidProjectWorkspace(existing.companyId, nextProjectId, nextProjectWorkspaceId);
663
+ }
664
+ if (nextExecutionWorkspaceId) {
665
+ await assertValidExecutionWorkspace(existing.companyId, nextProjectId, nextExecutionWorkspaceId);
666
+ }
667
+ applyStatusSideEffects(issueData.status, patch);
668
+ if (issueData.status && issueData.status !== "done") {
669
+ patch.completedAt = null;
670
+ }
671
+ if (issueData.status && issueData.status !== "cancelled") {
672
+ patch.cancelledAt = null;
673
+ }
674
+ if (issueData.status && issueData.status !== "in_progress") {
675
+ patch.checkoutRunId = null;
676
+ }
677
+ if ((issueData.assigneeAgentId !== undefined && issueData.assigneeAgentId !== existing.assigneeAgentId) ||
678
+ (issueData.assigneeUserId !== undefined && issueData.assigneeUserId !== existing.assigneeUserId)) {
679
+ patch.checkoutRunId = null;
680
+ }
681
+ return db.transaction(async (tx) => {
682
+ const defaultCompanyGoal = await getDefaultCompanyGoal(tx, existing.companyId);
683
+ patch.goalId = resolveNextIssueGoalId({
684
+ currentProjectId: existing.projectId,
685
+ currentGoalId: existing.goalId,
686
+ projectId: issueData.projectId,
687
+ goalId: issueData.goalId,
688
+ defaultGoalId: defaultCompanyGoal?.id ?? null,
689
+ });
690
+ const updated = await tx
691
+ .update(issues)
692
+ .set(patch)
693
+ .where(eq(issues.id, id))
694
+ .returning()
695
+ .then((rows) => rows[0] ?? null);
696
+ if (!updated)
697
+ return null;
698
+ if (nextLabelIds !== undefined) {
699
+ await syncIssueLabels(updated.id, existing.companyId, nextLabelIds, tx);
700
+ }
701
+ const [enriched] = await withIssueLabels(tx, [updated]);
702
+ return enriched;
703
+ });
704
+ },
705
+ remove: (id) => db.transaction(async (tx) => {
706
+ const attachmentAssetIds = await tx
707
+ .select({ assetId: issueAttachments.assetId })
708
+ .from(issueAttachments)
709
+ .where(eq(issueAttachments.issueId, id));
710
+ const issueDocumentIds = await tx
711
+ .select({ documentId: issueDocuments.documentId })
712
+ .from(issueDocuments)
713
+ .where(eq(issueDocuments.issueId, id));
714
+ const removedIssue = await tx
715
+ .delete(issues)
716
+ .where(eq(issues.id, id))
717
+ .returning()
718
+ .then((rows) => rows[0] ?? null);
719
+ if (removedIssue && attachmentAssetIds.length > 0) {
720
+ await tx
721
+ .delete(assets)
722
+ .where(inArray(assets.id, attachmentAssetIds.map((row) => row.assetId)));
723
+ }
724
+ if (removedIssue && issueDocumentIds.length > 0) {
725
+ await tx
726
+ .delete(documents)
727
+ .where(inArray(documents.id, issueDocumentIds.map((row) => row.documentId)));
728
+ }
729
+ if (!removedIssue)
730
+ return null;
731
+ const [enriched] = await withIssueLabels(tx, [removedIssue]);
732
+ return enriched;
733
+ }),
734
+ checkout: async (id, agentId, expectedStatuses, checkoutRunId) => {
735
+ const issueCompany = await db
736
+ .select({ companyId: issues.companyId })
737
+ .from(issues)
738
+ .where(eq(issues.id, id))
739
+ .then((rows) => rows[0] ?? null);
740
+ if (!issueCompany)
741
+ throw notFound("Issue not found");
742
+ await assertAssignableAgent(issueCompany.companyId, agentId);
743
+ const now = new Date();
744
+ const sameRunAssigneeCondition = checkoutRunId
745
+ ? and(eq(issues.assigneeAgentId, agentId), or(isNull(issues.checkoutRunId), eq(issues.checkoutRunId, checkoutRunId)))
746
+ : and(eq(issues.assigneeAgentId, agentId), isNull(issues.checkoutRunId));
747
+ const executionLockCondition = checkoutRunId
748
+ ? or(isNull(issues.executionRunId), eq(issues.executionRunId, checkoutRunId))
749
+ : isNull(issues.executionRunId);
750
+ const updated = await db
751
+ .update(issues)
752
+ .set({
753
+ assigneeAgentId: agentId,
754
+ assigneeUserId: null,
755
+ checkoutRunId,
756
+ executionRunId: checkoutRunId,
757
+ status: "in_progress",
758
+ startedAt: now,
759
+ updatedAt: now,
760
+ })
761
+ .where(and(eq(issues.id, id), inArray(issues.status, expectedStatuses), or(isNull(issues.assigneeAgentId), sameRunAssigneeCondition), executionLockCondition))
762
+ .returning()
763
+ .then((rows) => rows[0] ?? null);
764
+ if (updated) {
765
+ const [enriched] = await withIssueLabels(db, [updated]);
766
+ return enriched;
767
+ }
768
+ const current = await db
769
+ .select({
770
+ id: issues.id,
771
+ status: issues.status,
772
+ assigneeAgentId: issues.assigneeAgentId,
773
+ checkoutRunId: issues.checkoutRunId,
774
+ executionRunId: issues.executionRunId,
775
+ })
776
+ .from(issues)
777
+ .where(eq(issues.id, id))
778
+ .then((rows) => rows[0] ?? null);
779
+ if (!current)
780
+ throw notFound("Issue not found");
781
+ if (current.assigneeAgentId === agentId &&
782
+ current.status === "in_progress" &&
783
+ current.checkoutRunId == null &&
784
+ (current.executionRunId == null || current.executionRunId === checkoutRunId) &&
785
+ checkoutRunId) {
786
+ const adopted = await db
787
+ .update(issues)
788
+ .set({
789
+ checkoutRunId,
790
+ executionRunId: checkoutRunId,
791
+ updatedAt: new Date(),
792
+ })
793
+ .where(and(eq(issues.id, id), eq(issues.status, "in_progress"), eq(issues.assigneeAgentId, agentId), isNull(issues.checkoutRunId), or(isNull(issues.executionRunId), eq(issues.executionRunId, checkoutRunId))))
794
+ .returning()
795
+ .then((rows) => rows[0] ?? null);
796
+ if (adopted)
797
+ return adopted;
798
+ }
799
+ if (checkoutRunId &&
800
+ current.assigneeAgentId === agentId &&
801
+ current.status === "in_progress" &&
802
+ current.checkoutRunId &&
803
+ current.checkoutRunId !== checkoutRunId) {
804
+ const adopted = await adoptStaleCheckoutRun({
805
+ issueId: id,
806
+ actorAgentId: agentId,
807
+ actorRunId: checkoutRunId,
808
+ expectedCheckoutRunId: current.checkoutRunId,
809
+ });
810
+ if (adopted) {
811
+ const row = await db.select().from(issues).where(eq(issues.id, id)).then((rows) => rows[0]);
812
+ const [enriched] = await withIssueLabels(db, [row]);
813
+ return enriched;
814
+ }
815
+ }
816
+ // If this run already owns it and it's in_progress, return it (no self-409)
817
+ if (current.assigneeAgentId === agentId &&
818
+ current.status === "in_progress" &&
819
+ sameRunLock(current.checkoutRunId, checkoutRunId)) {
820
+ const row = await db.select().from(issues).where(eq(issues.id, id)).then((rows) => rows[0]);
821
+ const [enriched] = await withIssueLabels(db, [row]);
822
+ return enriched;
823
+ }
824
+ throw conflict("Issue checkout conflict", {
825
+ issueId: current.id,
826
+ status: current.status,
827
+ assigneeAgentId: current.assigneeAgentId,
828
+ checkoutRunId: current.checkoutRunId,
829
+ executionRunId: current.executionRunId,
830
+ });
831
+ },
832
+ assertCheckoutOwner: async (id, actorAgentId, actorRunId) => {
833
+ const current = await db
834
+ .select({
835
+ id: issues.id,
836
+ status: issues.status,
837
+ assigneeAgentId: issues.assigneeAgentId,
838
+ checkoutRunId: issues.checkoutRunId,
839
+ })
840
+ .from(issues)
841
+ .where(eq(issues.id, id))
842
+ .then((rows) => rows[0] ?? null);
843
+ if (!current)
844
+ throw notFound("Issue not found");
845
+ if (current.status === "in_progress" &&
846
+ current.assigneeAgentId === actorAgentId &&
847
+ sameRunLock(current.checkoutRunId, actorRunId)) {
848
+ return { ...current, adoptedFromRunId: null };
849
+ }
850
+ if (actorRunId &&
851
+ current.status === "in_progress" &&
852
+ current.assigneeAgentId === actorAgentId &&
853
+ current.checkoutRunId &&
854
+ current.checkoutRunId !== actorRunId) {
855
+ const adopted = await adoptStaleCheckoutRun({
856
+ issueId: id,
857
+ actorAgentId,
858
+ actorRunId,
859
+ expectedCheckoutRunId: current.checkoutRunId,
860
+ });
861
+ if (adopted) {
862
+ return {
863
+ ...adopted,
864
+ adoptedFromRunId: current.checkoutRunId,
865
+ };
866
+ }
867
+ }
868
+ throw conflict("Issue run ownership conflict", {
869
+ issueId: current.id,
870
+ status: current.status,
871
+ assigneeAgentId: current.assigneeAgentId,
872
+ checkoutRunId: current.checkoutRunId,
873
+ actorAgentId,
874
+ actorRunId,
875
+ });
876
+ },
877
+ release: async (id, actorAgentId, actorRunId) => {
878
+ const existing = await db
879
+ .select()
880
+ .from(issues)
881
+ .where(eq(issues.id, id))
882
+ .then((rows) => rows[0] ?? null);
883
+ if (!existing)
884
+ return null;
885
+ if (actorAgentId && existing.assigneeAgentId && existing.assigneeAgentId !== actorAgentId) {
886
+ throw conflict("Only assignee can release issue");
887
+ }
888
+ if (actorAgentId &&
889
+ existing.status === "in_progress" &&
890
+ existing.assigneeAgentId === actorAgentId &&
891
+ existing.checkoutRunId &&
892
+ !sameRunLock(existing.checkoutRunId, actorRunId ?? null)) {
893
+ throw conflict("Only checkout run can release issue", {
894
+ issueId: existing.id,
895
+ assigneeAgentId: existing.assigneeAgentId,
896
+ checkoutRunId: existing.checkoutRunId,
897
+ actorRunId: actorRunId ?? null,
898
+ });
899
+ }
900
+ const updated = await db
901
+ .update(issues)
902
+ .set({
903
+ status: "todo",
904
+ assigneeAgentId: null,
905
+ checkoutRunId: null,
906
+ executionRunId: null,
907
+ executionAgentNameKey: null,
908
+ executionLockedAt: null,
909
+ updatedAt: new Date(),
910
+ })
911
+ .where(eq(issues.id, id))
912
+ .returning()
913
+ .then((rows) => rows[0] ?? null);
914
+ if (!updated)
915
+ return null;
916
+ const [enriched] = await withIssueLabels(db, [updated]);
917
+ return enriched;
918
+ },
919
+ listLabels: (companyId) => db.select().from(labels).where(eq(labels.companyId, companyId)).orderBy(asc(labels.name), asc(labels.id)),
920
+ getLabelById: (id) => db
921
+ .select()
922
+ .from(labels)
923
+ .where(eq(labels.id, id))
924
+ .then((rows) => rows[0] ?? null),
925
+ createLabel: async (companyId, data) => {
926
+ const [created] = await db
927
+ .insert(labels)
928
+ .values({
929
+ companyId,
930
+ name: data.name.trim(),
931
+ color: data.color,
932
+ })
933
+ .returning();
934
+ return created;
935
+ },
936
+ deleteLabel: async (id) => db
937
+ .delete(labels)
938
+ .where(eq(labels.id, id))
939
+ .returning()
940
+ .then((rows) => rows[0] ?? null),
941
+ listComments: async (issueId, opts) => {
942
+ const order = opts?.order === "asc" ? "asc" : "desc";
943
+ const afterCommentId = opts?.afterCommentId?.trim() || null;
944
+ const limit = opts?.limit && opts.limit > 0
945
+ ? Math.min(Math.floor(opts.limit), MAX_ISSUE_COMMENT_PAGE_LIMIT)
946
+ : null;
947
+ const conditions = [eq(issueComments.issueId, issueId)];
948
+ if (afterCommentId) {
949
+ const anchor = await db
950
+ .select({
951
+ id: issueComments.id,
952
+ createdAt: issueComments.createdAt,
953
+ })
954
+ .from(issueComments)
955
+ .where(and(eq(issueComments.issueId, issueId), eq(issueComments.id, afterCommentId)))
956
+ .then((rows) => rows[0] ?? null);
957
+ if (!anchor)
958
+ return [];
959
+ conditions.push(order === "asc"
960
+ ? sql `(
961
+ ${issueComments.createdAt} > ${anchor.createdAt}
962
+ OR (${issueComments.createdAt} = ${anchor.createdAt} AND ${issueComments.id} > ${anchor.id})
963
+ )`
964
+ : sql `(
965
+ ${issueComments.createdAt} < ${anchor.createdAt}
966
+ OR (${issueComments.createdAt} = ${anchor.createdAt} AND ${issueComments.id} < ${anchor.id})
967
+ )`);
968
+ }
969
+ const query = db
970
+ .select()
971
+ .from(issueComments)
972
+ .where(and(...conditions))
973
+ .orderBy(order === "asc" ? asc(issueComments.createdAt) : desc(issueComments.createdAt), order === "asc" ? asc(issueComments.id) : desc(issueComments.id));
974
+ const comments = limit ? await query.limit(limit) : await query;
975
+ return comments.map(redactIssueComment);
976
+ },
977
+ getCommentCursor: async (issueId) => {
978
+ const [latest, countRow] = await Promise.all([
979
+ db
980
+ .select({
981
+ latestCommentId: issueComments.id,
982
+ latestCommentAt: issueComments.createdAt,
983
+ })
984
+ .from(issueComments)
985
+ .where(eq(issueComments.issueId, issueId))
986
+ .orderBy(desc(issueComments.createdAt), desc(issueComments.id))
987
+ .limit(1)
988
+ .then((rows) => rows[0] ?? null),
989
+ db
990
+ .select({
991
+ totalComments: sql `count(*)::int`,
992
+ })
993
+ .from(issueComments)
994
+ .where(eq(issueComments.issueId, issueId))
995
+ .then((rows) => rows[0] ?? null),
996
+ ]);
997
+ return {
998
+ totalComments: Number(countRow?.totalComments ?? 0),
999
+ latestCommentId: latest?.latestCommentId ?? null,
1000
+ latestCommentAt: latest?.latestCommentAt ?? null,
1001
+ };
1002
+ },
1003
+ getComment: (commentId) => db
1004
+ .select()
1005
+ .from(issueComments)
1006
+ .where(eq(issueComments.id, commentId))
1007
+ .then((rows) => {
1008
+ const comment = rows[0] ?? null;
1009
+ return comment ? redactIssueComment(comment) : null;
1010
+ }),
1011
+ addComment: async (issueId, body, actor) => {
1012
+ const issue = await db
1013
+ .select({ companyId: issues.companyId })
1014
+ .from(issues)
1015
+ .where(eq(issues.id, issueId))
1016
+ .then((rows) => rows[0] ?? null);
1017
+ if (!issue)
1018
+ throw notFound("Issue not found");
1019
+ const redactedBody = redactCurrentUserText(body);
1020
+ const [comment] = await db
1021
+ .insert(issueComments)
1022
+ .values({
1023
+ companyId: issue.companyId,
1024
+ issueId,
1025
+ authorAgentId: actor.agentId ?? null,
1026
+ authorUserId: actor.userId ?? null,
1027
+ body: redactedBody,
1028
+ })
1029
+ .returning();
1030
+ // Update issue's updatedAt so comment activity is reflected in recency sorting
1031
+ await db
1032
+ .update(issues)
1033
+ .set({ updatedAt: new Date() })
1034
+ .where(eq(issues.id, issueId));
1035
+ return redactIssueComment(comment);
1036
+ },
1037
+ createAttachment: async (input) => {
1038
+ const issue = await db
1039
+ .select({ id: issues.id, companyId: issues.companyId })
1040
+ .from(issues)
1041
+ .where(eq(issues.id, input.issueId))
1042
+ .then((rows) => rows[0] ?? null);
1043
+ if (!issue)
1044
+ throw notFound("Issue not found");
1045
+ if (input.issueCommentId) {
1046
+ const comment = await db
1047
+ .select({ id: issueComments.id, companyId: issueComments.companyId, issueId: issueComments.issueId })
1048
+ .from(issueComments)
1049
+ .where(eq(issueComments.id, input.issueCommentId))
1050
+ .then((rows) => rows[0] ?? null);
1051
+ if (!comment)
1052
+ throw notFound("Issue comment not found");
1053
+ if (comment.companyId !== issue.companyId || comment.issueId !== issue.id) {
1054
+ throw unprocessable("Attachment comment must belong to same issue and company");
1055
+ }
1056
+ }
1057
+ return db.transaction(async (tx) => {
1058
+ const [asset] = await tx
1059
+ .insert(assets)
1060
+ .values({
1061
+ companyId: issue.companyId,
1062
+ provider: input.provider,
1063
+ objectKey: input.objectKey,
1064
+ contentType: input.contentType,
1065
+ byteSize: input.byteSize,
1066
+ sha256: input.sha256,
1067
+ originalFilename: input.originalFilename ?? null,
1068
+ createdByAgentId: input.createdByAgentId ?? null,
1069
+ createdByUserId: input.createdByUserId ?? null,
1070
+ })
1071
+ .returning();
1072
+ const [attachment] = await tx
1073
+ .insert(issueAttachments)
1074
+ .values({
1075
+ companyId: issue.companyId,
1076
+ issueId: issue.id,
1077
+ assetId: asset.id,
1078
+ issueCommentId: input.issueCommentId ?? null,
1079
+ })
1080
+ .returning();
1081
+ return {
1082
+ id: attachment.id,
1083
+ companyId: attachment.companyId,
1084
+ issueId: attachment.issueId,
1085
+ issueCommentId: attachment.issueCommentId,
1086
+ assetId: attachment.assetId,
1087
+ provider: asset.provider,
1088
+ objectKey: asset.objectKey,
1089
+ contentType: asset.contentType,
1090
+ byteSize: asset.byteSize,
1091
+ sha256: asset.sha256,
1092
+ originalFilename: asset.originalFilename,
1093
+ createdByAgentId: asset.createdByAgentId,
1094
+ createdByUserId: asset.createdByUserId,
1095
+ createdAt: attachment.createdAt,
1096
+ updatedAt: attachment.updatedAt,
1097
+ };
1098
+ });
1099
+ },
1100
+ listAttachments: async (issueId) => db
1101
+ .select({
1102
+ id: issueAttachments.id,
1103
+ companyId: issueAttachments.companyId,
1104
+ issueId: issueAttachments.issueId,
1105
+ issueCommentId: issueAttachments.issueCommentId,
1106
+ assetId: issueAttachments.assetId,
1107
+ provider: assets.provider,
1108
+ objectKey: assets.objectKey,
1109
+ contentType: assets.contentType,
1110
+ byteSize: assets.byteSize,
1111
+ sha256: assets.sha256,
1112
+ originalFilename: assets.originalFilename,
1113
+ createdByAgentId: assets.createdByAgentId,
1114
+ createdByUserId: assets.createdByUserId,
1115
+ createdAt: issueAttachments.createdAt,
1116
+ updatedAt: issueAttachments.updatedAt,
1117
+ })
1118
+ .from(issueAttachments)
1119
+ .innerJoin(assets, eq(issueAttachments.assetId, assets.id))
1120
+ .where(eq(issueAttachments.issueId, issueId))
1121
+ .orderBy(desc(issueAttachments.createdAt)),
1122
+ getAttachmentById: async (id) => db
1123
+ .select({
1124
+ id: issueAttachments.id,
1125
+ companyId: issueAttachments.companyId,
1126
+ issueId: issueAttachments.issueId,
1127
+ issueCommentId: issueAttachments.issueCommentId,
1128
+ assetId: issueAttachments.assetId,
1129
+ provider: assets.provider,
1130
+ objectKey: assets.objectKey,
1131
+ contentType: assets.contentType,
1132
+ byteSize: assets.byteSize,
1133
+ sha256: assets.sha256,
1134
+ originalFilename: assets.originalFilename,
1135
+ createdByAgentId: assets.createdByAgentId,
1136
+ createdByUserId: assets.createdByUserId,
1137
+ createdAt: issueAttachments.createdAt,
1138
+ updatedAt: issueAttachments.updatedAt,
1139
+ })
1140
+ .from(issueAttachments)
1141
+ .innerJoin(assets, eq(issueAttachments.assetId, assets.id))
1142
+ .where(eq(issueAttachments.id, id))
1143
+ .then((rows) => rows[0] ?? null),
1144
+ removeAttachment: async (id) => db.transaction(async (tx) => {
1145
+ const existing = await tx
1146
+ .select({
1147
+ id: issueAttachments.id,
1148
+ companyId: issueAttachments.companyId,
1149
+ issueId: issueAttachments.issueId,
1150
+ issueCommentId: issueAttachments.issueCommentId,
1151
+ assetId: issueAttachments.assetId,
1152
+ provider: assets.provider,
1153
+ objectKey: assets.objectKey,
1154
+ contentType: assets.contentType,
1155
+ byteSize: assets.byteSize,
1156
+ sha256: assets.sha256,
1157
+ originalFilename: assets.originalFilename,
1158
+ createdByAgentId: assets.createdByAgentId,
1159
+ createdByUserId: assets.createdByUserId,
1160
+ createdAt: issueAttachments.createdAt,
1161
+ updatedAt: issueAttachments.updatedAt,
1162
+ })
1163
+ .from(issueAttachments)
1164
+ .innerJoin(assets, eq(issueAttachments.assetId, assets.id))
1165
+ .where(eq(issueAttachments.id, id))
1166
+ .then((rows) => rows[0] ?? null);
1167
+ if (!existing)
1168
+ return null;
1169
+ await tx.delete(issueAttachments).where(eq(issueAttachments.id, id));
1170
+ await tx.delete(assets).where(eq(assets.id, existing.assetId));
1171
+ return existing;
1172
+ }),
1173
+ findMentionedAgents: async (companyId, body) => {
1174
+ const re = /\B@([^\s@,!?.]+)/g;
1175
+ const tokens = new Set();
1176
+ let m;
1177
+ while ((m = re.exec(body)) !== null)
1178
+ tokens.add(m[1].toLowerCase());
1179
+ if (tokens.size === 0)
1180
+ return [];
1181
+ const rows = await db.select({ id: agents.id, name: agents.name })
1182
+ .from(agents).where(eq(agents.companyId, companyId));
1183
+ return rows.filter(a => tokens.has(a.name.toLowerCase())).map(a => a.id);
1184
+ },
1185
+ findMentionedProjectIds: async (issueId) => {
1186
+ const issue = await db
1187
+ .select({
1188
+ companyId: issues.companyId,
1189
+ title: issues.title,
1190
+ description: issues.description,
1191
+ })
1192
+ .from(issues)
1193
+ .where(eq(issues.id, issueId))
1194
+ .then((rows) => rows[0] ?? null);
1195
+ if (!issue)
1196
+ return [];
1197
+ const comments = await db
1198
+ .select({ body: issueComments.body })
1199
+ .from(issueComments)
1200
+ .where(eq(issueComments.issueId, issueId));
1201
+ const mentionedIds = new Set();
1202
+ for (const source of [
1203
+ issue.title,
1204
+ issue.description ?? "",
1205
+ ...comments.map((comment) => comment.body),
1206
+ ]) {
1207
+ for (const projectId of extractProjectMentionIds(source)) {
1208
+ mentionedIds.add(projectId);
1209
+ }
1210
+ }
1211
+ if (mentionedIds.size === 0)
1212
+ return [];
1213
+ const rows = await db
1214
+ .select({ id: projects.id })
1215
+ .from(projects)
1216
+ .where(and(eq(projects.companyId, issue.companyId), inArray(projects.id, [...mentionedIds])));
1217
+ const valid = new Set(rows.map((row) => row.id));
1218
+ return [...mentionedIds].filter((projectId) => valid.has(projectId));
1219
+ },
1220
+ getAncestors: async (issueId) => {
1221
+ const raw = [];
1222
+ const visited = new Set([issueId]);
1223
+ const start = await db.select().from(issues).where(eq(issues.id, issueId)).then(r => r[0] ?? null);
1224
+ let currentId = start?.parentId ?? null;
1225
+ while (currentId && !visited.has(currentId) && raw.length < 50) {
1226
+ visited.add(currentId);
1227
+ const parent = await db.select({
1228
+ id: issues.id, identifier: issues.identifier, title: issues.title, description: issues.description,
1229
+ status: issues.status, priority: issues.priority,
1230
+ assigneeAgentId: issues.assigneeAgentId, projectId: issues.projectId,
1231
+ goalId: issues.goalId, parentId: issues.parentId,
1232
+ }).from(issues).where(eq(issues.id, currentId)).then(r => r[0] ?? null);
1233
+ if (!parent)
1234
+ break;
1235
+ raw.push({
1236
+ id: parent.id, identifier: parent.identifier ?? null, title: parent.title, description: parent.description ?? null,
1237
+ status: parent.status, priority: parent.priority,
1238
+ assigneeAgentId: parent.assigneeAgentId ?? null,
1239
+ projectId: parent.projectId ?? null, goalId: parent.goalId ?? null,
1240
+ });
1241
+ currentId = parent.parentId ?? null;
1242
+ }
1243
+ // Batch-fetch referenced projects and goals
1244
+ const projectIds = [...new Set(raw.map(a => a.projectId).filter((id) => id != null))];
1245
+ const goalIds = [...new Set(raw.map(a => a.goalId).filter((id) => id != null))];
1246
+ const projectMap = new Map();
1247
+ const goalMap = new Map();
1248
+ if (projectIds.length > 0) {
1249
+ const workspaceRows = await db
1250
+ .select()
1251
+ .from(projectWorkspaces)
1252
+ .where(inArray(projectWorkspaces.projectId, projectIds))
1253
+ .orderBy(desc(projectWorkspaces.isPrimary), asc(projectWorkspaces.createdAt), asc(projectWorkspaces.id));
1254
+ const workspaceMap = new Map();
1255
+ for (const workspace of workspaceRows) {
1256
+ const existing = workspaceMap.get(workspace.projectId);
1257
+ if (existing)
1258
+ existing.push(workspace);
1259
+ else
1260
+ workspaceMap.set(workspace.projectId, [workspace]);
1261
+ }
1262
+ const rows = await db.select({
1263
+ id: projects.id, name: projects.name, description: projects.description,
1264
+ status: projects.status, goalId: projects.goalId,
1265
+ }).from(projects).where(inArray(projects.id, projectIds));
1266
+ for (const r of rows) {
1267
+ const projectWorkspaceRows = workspaceMap.get(r.id) ?? [];
1268
+ const workspaces = projectWorkspaceRows.map((workspace) => ({
1269
+ id: workspace.id,
1270
+ companyId: workspace.companyId,
1271
+ projectId: workspace.projectId,
1272
+ name: workspace.name,
1273
+ cwd: workspace.cwd,
1274
+ repoUrl: workspace.repoUrl ?? null,
1275
+ repoRef: workspace.repoRef ?? null,
1276
+ metadata: workspace.metadata ?? null,
1277
+ isPrimary: workspace.isPrimary,
1278
+ createdAt: workspace.createdAt,
1279
+ updatedAt: workspace.updatedAt,
1280
+ }));
1281
+ const primaryWorkspace = workspaces.find((workspace) => workspace.isPrimary) ?? workspaces[0] ?? null;
1282
+ projectMap.set(r.id, {
1283
+ ...r,
1284
+ workspaces,
1285
+ primaryWorkspace,
1286
+ });
1287
+ // Also collect goalIds from projects
1288
+ if (r.goalId && !goalIds.includes(r.goalId))
1289
+ goalIds.push(r.goalId);
1290
+ }
1291
+ }
1292
+ if (goalIds.length > 0) {
1293
+ const rows = await db.select({
1294
+ id: goals.id, title: goals.title, description: goals.description,
1295
+ level: goals.level, status: goals.status,
1296
+ }).from(goals).where(inArray(goals.id, goalIds));
1297
+ for (const r of rows)
1298
+ goalMap.set(r.id, r);
1299
+ }
1300
+ return raw.map(a => ({
1301
+ ...a,
1302
+ project: a.projectId ? projectMap.get(a.projectId) ?? null : null,
1303
+ goal: a.goalId ? goalMap.get(a.goalId) ?? null : null,
1304
+ }));
1305
+ },
1306
+ };
1307
+ }
1308
+ //# sourceMappingURL=issues.js.map