@kelpie/server 0.12.0 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (383) hide show
  1. package/dist/app.d.ts +13 -0
  2. package/dist/app.d.ts.map +1 -1
  3. package/dist/app.js +8 -1
  4. package/dist/app.js.map +1 -1
  5. package/dist/boot.d.ts.map +1 -1
  6. package/dist/boot.js +1 -0
  7. package/dist/boot.js.map +1 -1
  8. package/dist/lib/apiKeyScopes.d.ts.map +1 -1
  9. package/dist/lib/apiKeyScopes.js +6 -0
  10. package/dist/lib/apiKeyScopes.js.map +1 -1
  11. package/dist/lib/columns.d.ts +6 -0
  12. package/dist/lib/columns.d.ts.map +1 -1
  13. package/dist/lib/columns.js +5 -3
  14. package/dist/lib/columns.js.map +1 -1
  15. package/dist/lib/config.d.ts +17 -0
  16. package/dist/lib/config.d.ts.map +1 -1
  17. package/dist/lib/config.js +3 -0
  18. package/dist/lib/config.js.map +1 -1
  19. package/dist/lib/egress.d.ts.map +1 -1
  20. package/dist/lib/egress.js +8 -4
  21. package/dist/lib/egress.js.map +1 -1
  22. package/dist/lib/errors.d.ts +3 -3
  23. package/dist/lib/errors.d.ts.map +1 -1
  24. package/dist/lib/errors.js +13 -5
  25. package/dist/lib/errors.js.map +1 -1
  26. package/dist/lib/ids.d.ts +11 -0
  27. package/dist/lib/ids.d.ts.map +1 -1
  28. package/dist/lib/ids.js +11 -0
  29. package/dist/lib/ids.js.map +1 -1
  30. package/dist/lib/kelpieConfigFile.d.ts +15 -1
  31. package/dist/lib/kelpieConfigFile.d.ts.map +1 -1
  32. package/dist/lib/kelpieConfigFile.js +33 -1
  33. package/dist/lib/kelpieConfigFile.js.map +1 -1
  34. package/dist/modules/agent-tasks/catalog.d.ts.map +1 -1
  35. package/dist/modules/agent-tasks/catalog.js +32 -1
  36. package/dist/modules/agent-tasks/catalog.js.map +1 -1
  37. package/dist/modules/agent-tasks/index.d.ts.map +1 -1
  38. package/dist/modules/agent-tasks/index.js +1 -0
  39. package/dist/modules/agent-tasks/index.js.map +1 -1
  40. package/dist/modules/agent-tasks/resolve.d.ts.map +1 -1
  41. package/dist/modules/agent-tasks/resolve.js +5 -0
  42. package/dist/modules/agent-tasks/resolve.js.map +1 -1
  43. package/dist/modules/agent-tasks/routes.d.ts +2 -0
  44. package/dist/modules/agent-tasks/routes.d.ts.map +1 -1
  45. package/dist/modules/attachedRecords.d.ts +22 -8
  46. package/dist/modules/attachedRecords.d.ts.map +1 -1
  47. package/dist/modules/attachedRecords.js +42 -5
  48. package/dist/modules/attachedRecords.js.map +1 -1
  49. package/dist/modules/auth/index.d.ts.map +1 -1
  50. package/dist/modules/auth/index.js +1 -0
  51. package/dist/modules/auth/index.js.map +1 -1
  52. package/dist/modules/auth/repository.d.ts +14 -0
  53. package/dist/modules/auth/repository.d.ts.map +1 -1
  54. package/dist/modules/auth/repository.js +20 -2
  55. package/dist/modules/auth/repository.js.map +1 -1
  56. package/dist/modules/auth/routes.d.ts.map +1 -1
  57. package/dist/modules/auth/routes.js +30 -0
  58. package/dist/modules/auth/routes.js.map +1 -1
  59. package/dist/modules/auth/service.d.ts +28 -0
  60. package/dist/modules/auth/service.d.ts.map +1 -1
  61. package/dist/modules/auth/service.js +72 -4
  62. package/dist/modules/auth/service.js.map +1 -1
  63. package/dist/modules/companies/repository.d.ts.map +1 -1
  64. package/dist/modules/companies/repository.js +1 -1
  65. package/dist/modules/companies/repository.js.map +1 -1
  66. package/dist/modules/companies/routes.d.ts +2 -4
  67. package/dist/modules/companies/routes.d.ts.map +1 -1
  68. package/dist/modules/companies/routes.js +6 -11
  69. package/dist/modules/companies/routes.js.map +1 -1
  70. package/dist/modules/companies/schema.d.ts +13 -28
  71. package/dist/modules/companies/schema.d.ts.map +1 -1
  72. package/dist/modules/companies/schema.js +2 -2
  73. package/dist/modules/companies/schema.js.map +1 -1
  74. package/dist/modules/companies/service.d.ts +2 -3
  75. package/dist/modules/companies/service.d.ts.map +1 -1
  76. package/dist/modules/companies/service.js +4 -7
  77. package/dist/modules/companies/service.js.map +1 -1
  78. package/dist/modules/core.d.ts.map +1 -1
  79. package/dist/modules/core.js +2 -0
  80. package/dist/modules/core.js.map +1 -1
  81. package/dist/modules/custom-fields/repository.d.ts.map +1 -1
  82. package/dist/modules/custom-fields/repository.js +2 -0
  83. package/dist/modules/custom-fields/repository.js.map +1 -1
  84. package/dist/modules/custom-fields/routes.d.ts +1 -0
  85. package/dist/modules/custom-fields/routes.d.ts.map +1 -1
  86. package/dist/modules/dashboard/index.d.ts.map +1 -1
  87. package/dist/modules/dashboard/index.js +1 -0
  88. package/dist/modules/dashboard/index.js.map +1 -1
  89. package/dist/modules/dashboard/repository.d.ts +13 -0
  90. package/dist/modules/dashboard/repository.d.ts.map +1 -1
  91. package/dist/modules/dashboard/repository.js +36 -0
  92. package/dist/modules/dashboard/repository.js.map +1 -1
  93. package/dist/modules/dashboard/routes.d.ts.map +1 -1
  94. package/dist/modules/dashboard/routes.js +13 -0
  95. package/dist/modules/dashboard/routes.js.map +1 -1
  96. package/dist/modules/dashboard/service.d.ts +11 -0
  97. package/dist/modules/dashboard/service.d.ts.map +1 -1
  98. package/dist/modules/dashboard/service.js +24 -1
  99. package/dist/modules/dashboard/service.js.map +1 -1
  100. package/dist/modules/decisions/repository.d.ts.map +1 -1
  101. package/dist/modules/decisions/repository.js +2 -0
  102. package/dist/modules/decisions/repository.js.map +1 -1
  103. package/dist/modules/decisions/routes.d.ts +2 -0
  104. package/dist/modules/decisions/routes.d.ts.map +1 -1
  105. package/dist/modules/events/attendances.d.ts +35 -0
  106. package/dist/modules/events/attendances.d.ts.map +1 -0
  107. package/dist/modules/events/attendances.js +157 -0
  108. package/dist/modules/events/attendances.js.map +1 -0
  109. package/dist/modules/events/catalog.d.ts +35 -0
  110. package/dist/modules/events/catalog.d.ts.map +1 -0
  111. package/dist/modules/events/catalog.js +11 -0
  112. package/dist/modules/events/catalog.js.map +1 -0
  113. package/dist/modules/events/index.d.ts +10 -0
  114. package/dist/modules/events/index.d.ts.map +1 -0
  115. package/dist/modules/events/index.js +47 -0
  116. package/dist/modules/events/index.js.map +1 -0
  117. package/dist/modules/events/repository.d.ts +67 -0
  118. package/dist/modules/events/repository.d.ts.map +1 -0
  119. package/dist/modules/events/repository.js +212 -0
  120. package/dist/modules/events/repository.js.map +1 -0
  121. package/dist/modules/events/routes.d.ts +136 -0
  122. package/dist/modules/events/routes.d.ts.map +1 -0
  123. package/dist/modules/events/routes.js +280 -0
  124. package/dist/modules/events/routes.js.map +1 -0
  125. package/dist/modules/events/schema.d.ts +626 -0
  126. package/dist/modules/events/schema.d.ts.map +1 -0
  127. package/dist/modules/events/schema.js +93 -0
  128. package/dist/modules/events/schema.js.map +1 -0
  129. package/dist/modules/events/service.d.ts +68 -0
  130. package/dist/modules/events/service.d.ts.map +1 -0
  131. package/dist/modules/events/service.js +262 -0
  132. package/dist/modules/events/service.js.map +1 -0
  133. package/dist/modules/events/tools.d.ts +8 -0
  134. package/dist/modules/events/tools.d.ts.map +1 -0
  135. package/dist/modules/events/tools.js +80 -0
  136. package/dist/modules/events/tools.js.map +1 -0
  137. package/dist/modules/forms/applySubmissionFields.d.ts.map +1 -1
  138. package/dist/modules/forms/applySubmissionFields.js +7 -5
  139. package/dist/modules/forms/applySubmissionFields.js.map +1 -1
  140. package/dist/modules/forms/fillAddresses.d.ts +4 -0
  141. package/dist/modules/forms/fillAddresses.d.ts.map +1 -0
  142. package/dist/modules/forms/fillAddresses.js +93 -0
  143. package/dist/modules/forms/fillAddresses.js.map +1 -0
  144. package/dist/modules/forms/repository.d.ts +2 -2
  145. package/dist/modules/forms/repository.d.ts.map +1 -1
  146. package/dist/modules/forms/repository.js.map +1 -1
  147. package/dist/modules/forms/routes.d.ts +2 -0
  148. package/dist/modules/forms/routes.d.ts.map +1 -1
  149. package/dist/modules/forms/routes.js +2 -2
  150. package/dist/modules/forms/routes.js.map +1 -1
  151. package/dist/modules/forms/schema.js +2 -2
  152. package/dist/modules/forms/schema.js.map +1 -1
  153. package/dist/modules/forms/service.js +2 -2
  154. package/dist/modules/forms/service.js.map +1 -1
  155. package/dist/modules/forms/submission.d.ts +1 -0
  156. package/dist/modules/forms/submission.d.ts.map +1 -1
  157. package/dist/modules/forms/submission.js +27 -4
  158. package/dist/modules/forms/submission.js.map +1 -1
  159. package/dist/modules/hiring/roles.d.ts.map +1 -1
  160. package/dist/modules/hiring/roles.js +2 -1
  161. package/dist/modules/hiring/roles.js.map +1 -1
  162. package/dist/modules/import-export/addresses.d.ts +22 -0
  163. package/dist/modules/import-export/addresses.d.ts.map +1 -0
  164. package/dist/modules/import-export/addresses.js +83 -0
  165. package/dist/modules/import-export/addresses.js.map +1 -0
  166. package/dist/modules/import-export/drafts.d.ts +3 -4
  167. package/dist/modules/import-export/drafts.d.ts.map +1 -1
  168. package/dist/modules/import-export/drafts.js +4 -5
  169. package/dist/modules/import-export/drafts.js.map +1 -1
  170. package/dist/modules/import-export/exportRows.d.ts.map +1 -1
  171. package/dist/modules/import-export/exportRows.js +3 -3
  172. package/dist/modules/import-export/exportRows.js.map +1 -1
  173. package/dist/modules/import-export/plan.d.ts.map +1 -1
  174. package/dist/modules/import-export/plan.js +17 -2
  175. package/dist/modules/import-export/plan.js.map +1 -1
  176. package/dist/modules/import-export/presets.d.ts.map +1 -1
  177. package/dist/modules/import-export/presets.js +52 -7
  178. package/dist/modules/import-export/presets.js.map +1 -1
  179. package/dist/modules/import-export/repository.d.ts +3 -4
  180. package/dist/modules/import-export/repository.d.ts.map +1 -1
  181. package/dist/modules/import-export/repository.js +2 -3
  182. package/dist/modules/import-export/repository.js.map +1 -1
  183. package/dist/modules/import-export/writes.d.ts.map +1 -1
  184. package/dist/modules/import-export/writes.js +8 -4
  185. package/dist/modules/import-export/writes.js.map +1 -1
  186. package/dist/modules/lists/routes.d.ts +4 -0
  187. package/dist/modules/lists/routes.d.ts.map +1 -1
  188. package/dist/modules/notes/routes.d.ts +2 -0
  189. package/dist/modules/notes/routes.d.ts.map +1 -1
  190. package/dist/modules/people/repository.d.ts.map +1 -1
  191. package/dist/modules/people/repository.js +1 -1
  192. package/dist/modules/people/repository.js.map +1 -1
  193. package/dist/modules/people/routes.d.ts +2 -2
  194. package/dist/modules/people/routes.js +6 -6
  195. package/dist/modules/people/routes.js.map +1 -1
  196. package/dist/modules/people/schema.d.ts +17 -14
  197. package/dist/modules/people/schema.d.ts.map +1 -1
  198. package/dist/modules/people/schema.js +6 -4
  199. package/dist/modules/people/schema.js.map +1 -1
  200. package/dist/modules/people/service.d.ts +2 -2
  201. package/dist/modules/people/service.d.ts.map +1 -1
  202. package/dist/modules/people/service.js +5 -4
  203. package/dist/modules/people/service.js.map +1 -1
  204. package/dist/modules/plans/repository.d.ts +2 -2
  205. package/dist/modules/plans/repository.d.ts.map +1 -1
  206. package/dist/modules/plans/routes.d.ts +1 -0
  207. package/dist/modules/plans/routes.d.ts.map +1 -1
  208. package/dist/modules/plans/routes.js +4 -4
  209. package/dist/modules/plans/routes.js.map +1 -1
  210. package/dist/modules/plans/schema.d.ts +2 -2
  211. package/dist/modules/plans/schema.d.ts.map +1 -1
  212. package/dist/modules/plans/schema.js +3 -3
  213. package/dist/modules/plans/schema.js.map +1 -1
  214. package/dist/modules/plans/service.d.ts +2 -2
  215. package/dist/modules/plans/service.d.ts.map +1 -1
  216. package/dist/modules/plans/service.js.map +1 -1
  217. package/dist/modules/plans/tools.js +2 -2
  218. package/dist/modules/plans/tools.js.map +1 -1
  219. package/dist/modules/recordTargets.d.ts.map +1 -1
  220. package/dist/modules/recordTargets.js +11 -0
  221. package/dist/modules/recordTargets.js.map +1 -1
  222. package/dist/modules/references.d.ts.map +1 -1
  223. package/dist/modules/references.js +2 -0
  224. package/dist/modules/references.js.map +1 -1
  225. package/dist/modules/sample-data/fixture.d.ts +26 -4
  226. package/dist/modules/sample-data/fixture.d.ts.map +1 -1
  227. package/dist/modules/sample-data/fixture.js +157 -23
  228. package/dist/modules/sample-data/fixture.js.map +1 -1
  229. package/dist/modules/sample-data/index.d.ts +5 -2
  230. package/dist/modules/sample-data/index.d.ts.map +1 -1
  231. package/dist/modules/sample-data/index.js +7 -2
  232. package/dist/modules/sample-data/index.js.map +1 -1
  233. package/dist/modules/sample-data/routes.d.ts.map +1 -1
  234. package/dist/modules/sample-data/routes.js +2 -0
  235. package/dist/modules/sample-data/routes.js.map +1 -1
  236. package/dist/modules/sample-data/service.d.ts +11 -3
  237. package/dist/modules/sample-data/service.d.ts.map +1 -1
  238. package/dist/modules/sample-data/service.js +452 -363
  239. package/dist/modules/sample-data/service.js.map +1 -1
  240. package/dist/modules/sample-data/tools.d.ts.map +1 -1
  241. package/dist/modules/sample-data/tools.js +4 -3
  242. package/dist/modules/sample-data/tools.js.map +1 -1
  243. package/dist/modules/search/index.d.ts +1 -1
  244. package/dist/modules/search/index.d.ts.map +1 -1
  245. package/dist/modules/search/index.js +2 -1
  246. package/dist/modules/search/index.js.map +1 -1
  247. package/dist/modules/search/repository.d.ts +1 -0
  248. package/dist/modules/search/repository.d.ts.map +1 -1
  249. package/dist/modules/search/repository.js +22 -0
  250. package/dist/modules/search/repository.js.map +1 -1
  251. package/dist/modules/search/service.d.ts +2 -2
  252. package/dist/modules/search/service.d.ts.map +1 -1
  253. package/dist/modules/search/service.js +4 -3
  254. package/dist/modules/search/service.js.map +1 -1
  255. package/dist/modules/smtp-email/index.d.ts +12 -4
  256. package/dist/modules/smtp-email/index.d.ts.map +1 -1
  257. package/dist/modules/smtp-email/index.js +25 -5
  258. package/dist/modules/smtp-email/index.js.map +1 -1
  259. package/dist/modules/workspace/repository.d.ts +2 -0
  260. package/dist/modules/workspace/repository.d.ts.map +1 -1
  261. package/dist/modules/workspace/repository.js +12 -0
  262. package/dist/modules/workspace/repository.js.map +1 -1
  263. package/dist/modules/workspace/routes.d.ts.map +1 -1
  264. package/dist/modules/workspace/routes.js +13 -0
  265. package/dist/modules/workspace/routes.js.map +1 -1
  266. package/dist/modules/workspace/service.d.ts +10 -0
  267. package/dist/modules/workspace/service.d.ts.map +1 -1
  268. package/dist/modules/workspace/service.js +34 -10
  269. package/dist/modules/workspace/service.js.map +1 -1
  270. package/dist/modules/workspace/starters.d.ts +17 -2
  271. package/dist/modules/workspace/starters.d.ts.map +1 -1
  272. package/dist/modules/workspace/starters.js +141 -14
  273. package/dist/modules/workspace/starters.js.map +1 -1
  274. package/dist/modules/workspace/workspaceAccessMiddleware.d.ts.map +1 -1
  275. package/dist/modules/workspace/workspaceAccessMiddleware.js +3 -4
  276. package/dist/modules/workspace/workspaceAccessMiddleware.js.map +1 -1
  277. package/dist/runtime/events.d.ts +1 -1
  278. package/dist/runtime/events.d.ts.map +1 -1
  279. package/dist/runtime/events.js +2 -0
  280. package/dist/runtime/events.js.map +1 -1
  281. package/dist/runtime/module.d.ts +8 -0
  282. package/dist/runtime/module.d.ts.map +1 -1
  283. package/dist/runtime/registry.d.ts.map +1 -1
  284. package/dist/runtime/registry.js +102 -7
  285. package/dist/runtime/registry.js.map +1 -1
  286. package/dist/schema/index.d.ts +1 -0
  287. package/dist/schema/index.d.ts.map +1 -1
  288. package/dist/schema/index.js +1 -0
  289. package/dist/schema/index.js.map +1 -1
  290. package/dist/testing/app.d.ts +8 -0
  291. package/dist/testing/app.d.ts.map +1 -1
  292. package/dist/testing/app.js +6 -1
  293. package/dist/testing/app.js.map +1 -1
  294. package/dist/testing/client.d.ts.map +1 -1
  295. package/dist/testing/client.js +9 -1
  296. package/dist/testing/client.js.map +1 -1
  297. package/migrations/0044_events.sql +79 -0
  298. package/migrations/0045_addresses.sql +66 -0
  299. package/migrations/0046_drop_company_website.sql +66 -0
  300. package/migrations/meta/_journal.json +21 -0
  301. package/package.json +3 -3
  302. package/src/app.ts +25 -1
  303. package/src/boot.ts +1 -0
  304. package/src/lib/apiKeyScopes.ts +6 -0
  305. package/src/lib/columns.ts +12 -3
  306. package/src/lib/config.ts +21 -0
  307. package/src/lib/egress.ts +8 -4
  308. package/src/lib/errors.ts +16 -9
  309. package/src/lib/ids.ts +11 -0
  310. package/src/lib/kelpieConfigFile.ts +69 -1
  311. package/src/modules/agent-tasks/catalog.ts +33 -1
  312. package/src/modules/agent-tasks/index.ts +1 -0
  313. package/src/modules/agent-tasks/resolve.ts +11 -0
  314. package/src/modules/attachedRecords.ts +85 -13
  315. package/src/modules/auth/index.ts +1 -0
  316. package/src/modules/auth/repository.ts +34 -3
  317. package/src/modules/auth/routes.ts +44 -1
  318. package/src/modules/auth/service.ts +118 -4
  319. package/src/modules/companies/repository.ts +1 -0
  320. package/src/modules/companies/routes.ts +6 -11
  321. package/src/modules/companies/schema.ts +3 -3
  322. package/src/modules/companies/service.ts +6 -10
  323. package/src/modules/core.ts +2 -0
  324. package/src/modules/custom-fields/repository.ts +2 -0
  325. package/src/modules/dashboard/index.ts +1 -0
  326. package/src/modules/dashboard/repository.ts +64 -0
  327. package/src/modules/dashboard/routes.ts +15 -0
  328. package/src/modules/dashboard/service.ts +48 -0
  329. package/src/modules/decisions/repository.ts +2 -0
  330. package/src/modules/events/attendances.ts +245 -0
  331. package/src/modules/events/catalog.ts +36 -0
  332. package/src/modules/events/index.ts +54 -0
  333. package/src/modules/events/repository.ts +396 -0
  334. package/src/modules/events/routes.ts +405 -0
  335. package/src/modules/events/schema.ts +148 -0
  336. package/src/modules/events/service.ts +443 -0
  337. package/src/modules/events/tools.ts +118 -0
  338. package/src/modules/forms/applySubmissionFields.ts +7 -5
  339. package/src/modules/forms/fillAddresses.ts +185 -0
  340. package/src/modules/forms/repository.ts +3 -3
  341. package/src/modules/forms/routes.ts +2 -2
  342. package/src/modules/forms/schema.ts +2 -2
  343. package/src/modules/forms/service.ts +2 -2
  344. package/src/modules/forms/submission.ts +34 -4
  345. package/src/modules/hiring/roles.ts +2 -1
  346. package/src/modules/import-export/addresses.ts +150 -0
  347. package/src/modules/import-export/drafts.ts +7 -9
  348. package/src/modules/import-export/exportRows.ts +3 -3
  349. package/src/modules/import-export/plan.ts +30 -2
  350. package/src/modules/import-export/presets.ts +55 -7
  351. package/src/modules/import-export/repository.ts +5 -7
  352. package/src/modules/import-export/writes.ts +8 -4
  353. package/src/modules/people/repository.ts +1 -0
  354. package/src/modules/people/routes.ts +6 -6
  355. package/src/modules/people/schema.ts +7 -5
  356. package/src/modules/people/service.ts +7 -5
  357. package/src/modules/plans/repository.ts +2 -2
  358. package/src/modules/plans/routes.ts +6 -6
  359. package/src/modules/plans/schema.ts +4 -4
  360. package/src/modules/plans/service.ts +3 -3
  361. package/src/modules/plans/tools.ts +2 -2
  362. package/src/modules/recordTargets.ts +14 -0
  363. package/src/modules/references.ts +2 -0
  364. package/src/modules/sample-data/fixture.ts +208 -27
  365. package/src/modules/sample-data/index.ts +7 -2
  366. package/src/modules/sample-data/routes.ts +2 -0
  367. package/src/modules/sample-data/service.ts +166 -49
  368. package/src/modules/sample-data/tools.ts +4 -3
  369. package/src/modules/search/index.ts +2 -1
  370. package/src/modules/search/repository.ts +32 -1
  371. package/src/modules/search/service.ts +6 -4
  372. package/src/modules/smtp-email/index.ts +32 -10
  373. package/src/modules/workspace/repository.ts +18 -0
  374. package/src/modules/workspace/routes.ts +16 -0
  375. package/src/modules/workspace/service.ts +59 -14
  376. package/src/modules/workspace/starters.ts +275 -14
  377. package/src/modules/workspace/workspaceAccessMiddleware.ts +3 -4
  378. package/src/runtime/events.ts +2 -0
  379. package/src/runtime/module.ts +8 -0
  380. package/src/runtime/registry.ts +122 -8
  381. package/src/schema/index.ts +1 -0
  382. package/src/testing/app.ts +14 -1
  383. package/src/testing/client.ts +11 -1
@@ -0,0 +1,66 @@
1
+ -- Domain is the company homepage. `website` was a second URL that almost
2
+ -- always repeated `https://` plus the same host. Fill a blank domain from
3
+ -- that URL, then drop the column. Visit on the heading opens https://{domain}.
4
+ UPDATE "companies" AS c
5
+ SET "domain" = v.host
6
+ FROM (
7
+ SELECT DISTINCT ON (workspace_id, host)
8
+ id,
9
+ workspace_id,
10
+ host
11
+ FROM (
12
+ SELECT
13
+ id,
14
+ workspace_id,
15
+ NULLIF(
16
+ regexp_replace(
17
+ regexp_replace(
18
+ regexp_replace(lower(trim(website)), '^[a-z][a-z0-9+.-]*://', ''),
19
+ '/.*$',
20
+ ''
21
+ ),
22
+ '\.$',
23
+ ''
24
+ ),
25
+ ''
26
+ ) AS host
27
+ FROM "companies"
28
+ WHERE "domain" IS NULL AND "website" IS NOT NULL
29
+ ) AS sourced
30
+ WHERE host IS NOT NULL
31
+ ORDER BY workspace_id, host, id
32
+ ) AS v
33
+ WHERE c.id = v.id
34
+ AND NOT EXISTS (
35
+ SELECT 1
36
+ FROM "companies" AS other
37
+ WHERE other.workspace_id = c.workspace_id
38
+ AND other.domain = v.host
39
+ AND other.id <> c.id
40
+ );
41
+ --> statement-breakpoint
42
+ ALTER TABLE "companies" DROP COLUMN "website";
43
+ --> statement-breakpoint
44
+ UPDATE "form_fields"
45
+ SET "map_to" = 'company.domain'
46
+ WHERE "map_to" = 'company.website';
47
+ --> statement-breakpoint
48
+ UPDATE "import_jobs"
49
+ SET "column_map" = ("column_map" - 'website') || jsonb_build_object('domain', "column_map"->>'website')
50
+ WHERE jsonb_typeof("column_map") = 'object'
51
+ AND "column_map" ? 'website'
52
+ AND "column_map"->>'website' IS NOT NULL
53
+ AND NOT (
54
+ "column_map" ? 'domain'
55
+ AND "column_map"->>'domain' IS NOT NULL
56
+ );
57
+ --> statement-breakpoint
58
+ UPDATE "import_jobs"
59
+ SET "column_map" = "column_map" - 'website'
60
+ WHERE jsonb_typeof("column_map") = 'object'
61
+ AND "column_map" ? 'website';
62
+ --> statement-breakpoint
63
+ UPDATE "import_jobs"
64
+ SET "match_key" = 'domain'
65
+ WHERE "object" = 'companies'
66
+ AND "match_key" = 'website';
@@ -309,6 +309,27 @@
309
309
  "when": 1788700000000,
310
310
  "tag": "0043_external_sign_in",
311
311
  "breakpoints": true
312
+ },
313
+ {
314
+ "idx": 44,
315
+ "version": "7",
316
+ "when": 1788800000000,
317
+ "tag": "0044_events",
318
+ "breakpoints": true
319
+ },
320
+ {
321
+ "idx": 45,
322
+ "version": "7",
323
+ "when": 1788900000000,
324
+ "tag": "0045_addresses",
325
+ "breakpoints": true
326
+ },
327
+ {
328
+ "idx": 46,
329
+ "version": "7",
330
+ "when": 1789000000000,
331
+ "tag": "0046_drop_company_website",
332
+ "breakpoints": true
312
333
  }
313
334
  ]
314
335
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kelpie/server",
3
- "version": "0.12.0",
3
+ "version": "0.13.0",
4
4
  "description": "The Kelpie service as a library: module runtime, core CRM modules, REST API, MCP surface, and the shared migration pipeline.",
5
5
  "keywords": [
6
6
  "kelpie",
@@ -52,11 +52,11 @@
52
52
  },
53
53
  "dependencies": {
54
54
  "@hono/node-server": "^2.0.12",
55
- "@kelpie/schemas": "^0.12.0",
55
+ "@kelpie/schemas": "^0.13.0",
56
56
  "@node-rs/argon2": "^2.0.2",
57
57
  "drizzle-orm": "^0.45.2",
58
58
  "emittery": "^2.0.0",
59
- "hono": "^4.12.32",
59
+ "hono": "^4.13.7",
60
60
  "mailgen": "^2.0.36",
61
61
  "nodemailer": "^9.0.5",
62
62
  "postgres": "^3.4.9",
package/src/app.ts CHANGED
@@ -2,6 +2,8 @@ import { Hono } from 'hono'
2
2
  import type { Context } from 'hono'
3
3
  import { cors } from 'hono/cors'
4
4
 
5
+ import type { PublicRegion } from '@kelpie/schemas'
6
+
5
7
  import type { RuntimeMode } from './lib/config.ts'
6
8
  import type { DatabaseProbe } from './lib/database.ts'
7
9
  import { AppError, internalErrorBody, toErrorBody } from './lib/errors.ts'
@@ -65,6 +67,18 @@ export interface AppDependencies {
65
67
  * name itself".
66
68
  */
67
69
  readonly siteName?: string | undefined
70
+ /**
71
+ * Reported through `GET /v1/public/config` so the sign-up page can show a
72
+ * "signups are closed" message instead of the form. Defaults to `true`
73
+ * (open), matching the config layer's own default.
74
+ */
75
+ readonly signupsEnabled?: boolean
76
+ /**
77
+ * Reported through `GET /v1/public/config` so signed-out pages can draw a
78
+ * region switcher. Defaults to `[]` (no switcher), matching a self-hosted
79
+ * install that never declared regions.
80
+ */
81
+ readonly regions?: readonly PublicRegion[]
68
82
  }
69
83
 
70
84
  /** Per-request values the middleware chain sets and handlers read. */
@@ -185,9 +199,19 @@ export function createApp(dependencies: AppDependencies): Hono<AppBindings> {
185
199
  // exists to be visible; nothing here is sensitive.
186
200
  const runtimeMode: RuntimeMode = dependencies.runtimeMode ?? 'production'
187
201
  const siteName = dependencies.siteName ?? null
202
+ const signupsEnabled = dependencies.signupsEnabled ?? true
203
+ const regions = dependencies.regions ?? []
188
204
 
189
205
  app.get(`${PUBLIC_ROUTE_PREFIX}/config`, (context) =>
190
- context.json({ runtime_mode: runtimeMode, site_name: siteName }, 200),
206
+ context.json(
207
+ {
208
+ runtime_mode: runtimeMode,
209
+ site_name: siteName,
210
+ signups_enabled: signupsEnabled,
211
+ regions,
212
+ },
213
+ 200,
214
+ ),
191
215
  )
192
216
 
193
217
  for (const { router } of dependencies.contributions.routers) {
package/src/boot.ts CHANGED
@@ -77,6 +77,7 @@ export async function bootAssembly(
77
77
  now: () => new Date(),
78
78
  appBaseUrl: config.appBaseUrl,
79
79
  secretEncryption: config.secretEncryption,
80
+ signupsEnabled: config.signups === 'open',
80
81
  },
81
82
  // `provider` picks a named sender from the runtime's registry. `'log'` is
82
83
  // built in; `'smtp'` is registered by the built-in `smtp-email` core
@@ -58,6 +58,10 @@ const ROUTE_SCOPE_RULES: readonly RouteScopeRule[] = [
58
58
  { methods: ['POST', 'PATCH', 'DELETE'], pattern: /^\/v1\/roles(?:\/[^/]+)?$/u, scope: 'roles:write' },
59
59
  { methods: ['GET'], pattern: /^\/v1\/candidates(?:\/[^/]+)?$/u, scope: 'candidates:read' },
60
60
  { methods: ['POST', 'PATCH', 'DELETE'], pattern: /^\/v1\/candidates(?:\/[^/]+)?$/u, scope: 'candidates:write' },
61
+ { methods: ['GET'], pattern: /^\/v1\/(?:events(?:\/[^/]+(?:\/attendances)?)?|event-associations)$/u, scope: 'events:read' },
62
+ { methods: ['POST', 'PATCH', 'DELETE'], pattern: /^\/v1\/(?:events(?:\/[^/]+(?:\/attendances)?)?|event-associations)$/u, scope: 'events:write' },
63
+ { methods: ['GET'], pattern: /^\/v1\/attendances(?:\/[^/]+)?$/u, scope: 'attendances:read' },
64
+ { methods: ['POST', 'PATCH', 'DELETE'], pattern: /^\/v1\/attendances(?:\/[^/]+)?$/u, scope: 'attendances:write' },
61
65
  { methods: ['GET'], pattern: /^\/v1\/decisions(?:\/[^/]+)?$/u, scope: 'decisions:read' },
62
66
  { methods: ['POST', 'PATCH', 'DELETE'], pattern: /^\/v1\/decisions(?:\/[^/]+)?$/u, scope: 'decisions:write' },
63
67
  { methods: ['GET'], pattern: /^\/v1\/plan_items(?:\/[^/]+)?$/u, scope: 'plan_items:read' },
@@ -100,6 +104,7 @@ const ROUTE_SCOPE_RULES: readonly RouteScopeRule[] = [
100
104
  { methods: ['GET'], pattern: /^\/v1\/workspaces\/[^/]+\/modules(?:\/[^/]+)?$/u, scope: 'modules:read' },
101
105
  { methods: ['PATCH'], pattern: /^\/v1\/workspaces\/[^/]+\/modules(?:\/[^/]+)?$/u, scope: 'modules:write' },
102
106
  { methods: ['POST'], pattern: /^\/v1\/workspaces\/[^/]+\/sample-data$/u, scope: 'sample_data:write' },
107
+ { methods: ['POST'], pattern: /^\/v1\/workspaces\/[^/]+\/handbook\/seed$/u, scope: 'handbook:write' },
103
108
  { methods: ['POST'], pattern: /^\/v1\/workspaces\/[^/]+\/relink-email-domains$/u, scope: 'workspace:write' },
104
109
  { methods: ['GET'], pattern: /^\/v1\/mcp\/tools$/u, scope: 'search:read' },
105
110
  ]
@@ -158,6 +163,7 @@ const MCP_RESOURCE_ALIASES: Readonly<Record<string, string>> = {
158
163
  consent_purposes: 'consent_purposes',
159
164
  form_submissions: 'forms',
160
165
  list_memberships: 'lists',
166
+ event_associations: 'events',
161
167
  agent_tasks: 'agent_tasks',
162
168
  agent_runs: 'agent_runs',
163
169
  import_jobs: 'import_export',
@@ -60,6 +60,12 @@ export interface SearchVectorPart {
60
60
  * a generated column built on it.
61
61
  */
62
62
  readonly array?: boolean
63
+ /**
64
+ * True for an `addresses` jsonb column. Flattened by
65
+ * `kelpie_addresses_search_text`, a wrapper that migration `0045` adds so the
66
+ * generated vector reads city and postal code without the kind keys.
67
+ */
68
+ readonly addresses?: boolean
63
69
  }
64
70
 
65
71
  /**
@@ -78,9 +84,12 @@ export interface SearchVectorPart {
78
84
  const SPLIT_ON_PUNCTUATION = sql.raw("'[^[:alnum:]]+', ' ', 'g'")
79
85
 
80
86
  function searchVectorPart(part: SearchVectorPart): SQL {
81
- const raw = part.array === true
82
- ? sql`coalesce(kelpie_text_array_to_string(${part.column}), '')`
83
- : sql`coalesce(${part.column}::text, '')`
87
+ const raw =
88
+ part.addresses === true
89
+ ? sql`coalesce(kelpie_addresses_search_text(${part.column}), '')`
90
+ : part.array === true
91
+ ? sql`coalesce(kelpie_text_array_to_string(${part.column}), '')`
92
+ : sql`coalesce(${part.column}::text, '')`
84
93
  const source = sql`regexp_replace(${raw}, ${SPLIT_ON_PUNCTUATION})`
85
94
 
86
95
  return sql`setweight(to_tsvector(${sql.raw(`'${SEARCH_CONFIGURATION}'`)}, ${source}), ${sql.raw(`'${part.weight}'`)})`
package/src/lib/config.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import type { PublicRegion } from '@kelpie/schemas'
1
2
  import { z } from 'zod'
2
3
 
3
4
  import { emailConfigSchema } from './email.ts'
@@ -24,6 +25,9 @@ export type RuntimeMode = 'development' | 'test' | 'production'
24
25
 
25
26
  export type LogLevel = 'debug' | 'info' | 'warn' | 'error'
26
27
 
28
+ /** Whether `POST /v1/auth/signup` and external-sign-in provisioning accept a brand-new account. */
29
+ export type SignupsMode = 'open' | 'closed'
30
+
27
31
  /**
28
32
  * Everything the logger needs to boot: the minimum severity to emit, and the
29
33
  * ordered list of destinations each line is written to. Stdout is one such
@@ -96,6 +100,20 @@ export interface KelpieConfig {
96
100
  * it, so both webhooks and agent-tasks receive it through `services`.
97
101
  */
98
102
  readonly secretEncryption: SecretEncryptionConfig | undefined
103
+ /**
104
+ * Whether the instance accepts a brand-new account, through password signup
105
+ * or external-sign-in provisioning. Defaults to `'open'`, so a self-hoster
106
+ * who never sets `SIGNUPS` sees no change in behaviour. Joining an existing
107
+ * workspace via invite, and workspace creation by an already-verified
108
+ * account, are unaffected either way.
109
+ */
110
+ readonly signups: SignupsMode
111
+ /**
112
+ * Origins a multi-region assembly advertises on signed-out pages. Empty
113
+ * (the default) hides the switcher. Two or more draws it. Exposed through
114
+ * `GET /v1/public/config`.
115
+ */
116
+ readonly regions: readonly PublicRegion[]
99
117
  }
100
118
 
101
119
  /** Thrown at boot when the environment cannot produce a valid configuration. */
@@ -129,6 +147,7 @@ const environmentSchema = z.object({
129
147
  KELPIE_SITE_NAME: z.string().min(1).optional(),
130
148
  WEB_BUNDLE_DIR: z.string().min(1).optional(),
131
149
  TRUSTED_PROXY_HOP_COUNT: z.coerce.number().int().nonnegative().default(0),
150
+ SIGNUPS: z.enum(['open', 'closed']).default('open'),
132
151
  ...rateLimitConfigSchema.shape,
133
152
  })
134
153
 
@@ -171,5 +190,7 @@ export function loadConfig(environment: Environment): KelpieConfig {
171
190
  env: environment,
172
191
  appBaseUrl: undefined,
173
192
  secretEncryption: undefined,
193
+ signups: environmentResult.data.SIGNUPS,
194
+ regions: [],
174
195
  }
175
196
  }
package/src/lib/egress.ts CHANGED
@@ -74,17 +74,21 @@ function isPrivateIpv4(ip: string): boolean {
74
74
 
75
75
  /** True for an IPv6 address in a loopback, unspecified, link-local, or unique-local range. */
76
76
  function isPrivateIpv6(ip: string): boolean {
77
- const lower = ip.toLowerCase()
77
+ // Normalize both DNS results and literals. URL parsing compresses IPv6 and
78
+ // rewrites a dotted IPv4 suffix as two hexadecimal groups.
79
+ const lower = new URL(`http://[${ip}]/`).hostname.slice(1, -1)
78
80
 
79
81
  if (lower === '::1' || lower === '::') {
80
82
  return true
81
83
  }
82
84
 
83
- const mapped = /^::ffff:(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$/u.exec(lower)
85
+ const mapped = /^::ffff:([\da-f]{1,4}):([\da-f]{1,4})$/u.exec(lower)
84
86
 
85
- if (mapped?.[1] !== undefined) {
87
+ if (mapped?.[1] !== undefined && mapped[2] !== undefined) {
86
88
  // An IPv4 address written in IPv6 form resolves by its IPv4 rules.
87
- return isPrivateIpv4(mapped[1])
89
+ const high = Number.parseInt(mapped[1], 16)
90
+ const low = Number.parseInt(mapped[2], 16)
91
+ return isPrivateIpv4([high >>> 8, high & 255, low >>> 8, low & 255].join('.'))
88
92
  }
89
93
 
90
94
  const firstHextet = Number.parseInt(lower.split(':')[0] || '0', 16)
package/src/lib/errors.ts CHANGED
@@ -89,25 +89,32 @@ export class AppError extends Error {
89
89
  * A reason rather than a message, so a provider module can branch on it (to
90
90
  * redirect somewhere useful, say) without matching on English text.
91
91
  */
92
- export type ExternalSignInRefusal = 'email_unverified' | 'unknown_identity'
92
+ export type ExternalSignInRefusal = 'email_unverified' | 'unknown_identity' | 'signups_closed'
93
+
94
+ const EXTERNAL_SIGN_IN_CODE: Record<ExternalSignInRefusal, ErrorCode> = {
95
+ email_unverified: 'unauthorized',
96
+ unknown_identity: 'forbidden',
97
+ signups_closed: 'forbidden',
98
+ }
99
+
100
+ const EXTERNAL_SIGN_IN_MESSAGE: Record<ExternalSignInRefusal, string> = {
101
+ email_unverified: 'The identity provider did not verify this email address',
102
+ unknown_identity: 'No account exists for this identity',
103
+ signups_closed: 'Signups are closed on this instance',
104
+ }
93
105
 
94
106
  /**
95
107
  * An external sign-in core would not complete.
96
108
  *
97
109
  * An `AppError`, so an uncaught one still renders as the `api.md` body. No new
98
- * `ErrorCode`: refusing an unverified identity is an ordinary `401`, and
99
- * refusing to provision is an ordinary `403`.
110
+ * `ErrorCode`: refusing an unverified identity is an ordinary `401`, and both
111
+ * refusing to provision and signups being closed are an ordinary `403`.
100
112
  */
101
113
  export class ExternalSignInError extends AppError {
102
114
  readonly reason: ExternalSignInRefusal
103
115
 
104
116
  constructor(reason: ExternalSignInRefusal) {
105
- super(
106
- reason === 'email_unverified' ? 'unauthorized' : 'forbidden',
107
- reason === 'email_unverified'
108
- ? 'The identity provider did not verify this email address'
109
- : 'No account exists for this identity',
110
- )
117
+ super(EXTERNAL_SIGN_IN_CODE[reason], EXTERNAL_SIGN_IN_MESSAGE[reason])
111
118
  this.name = 'ExternalSignInError'
112
119
  this.reason = reason
113
120
  }
package/src/lib/ids.ts CHANGED
@@ -71,6 +71,17 @@ export const idPrefixes = {
71
71
  * key. Not persisted by core.
72
72
  */
73
73
  event: 'ev',
74
+ /**
75
+ * A dated CRM gathering. The public prefix is `event_`; the factory key is
76
+ * `crmEvent` because `event` already stamps domain-event envelopes as `ev_`.
77
+ */
78
+ crmEvent: 'event',
79
+ attendance: 'atnd',
80
+ /**
81
+ * Never returns over the wire: associations nest on the Event and on
82
+ * `GET /v1/event-associations`. Same reason as `personLink`.
83
+ */
84
+ eventAssociation: 'elink',
74
85
  } as const
75
86
 
76
87
  export type ObjectKind = keyof typeof idPrefixes
@@ -1,6 +1,17 @@
1
+ import type { PublicRegion } from '@kelpie/schemas'
2
+ import { publicRegionSchema } from '@kelpie/schemas'
3
+
1
4
  import type { KelpieModule } from '../runtime/module.ts'
2
- import { ConfigurationError, type Environment, type KelpieConfig, type LogLevel, type RuntimeMode } from './config.ts'
5
+ import {
6
+ ConfigurationError,
7
+ type Environment,
8
+ type KelpieConfig,
9
+ type LogLevel,
10
+ type RuntimeMode,
11
+ type SignupsMode,
12
+ } from './config.ts'
3
13
  import type { EmailConfig } from './email.ts'
14
+ import { describeValidationIssue } from './errors.ts'
4
15
  import { type ConfigValue, resolveMarkers } from './fromEnv.ts'
5
16
  import type { LoggingDestination } from './logger.ts'
6
17
  import type { RateLimitConfig } from './rateLimit.ts'
@@ -35,6 +46,19 @@ export interface KelpieConfigInput {
35
46
  readonly siteName?: ConfigValue<string | undefined>
36
47
  /** Optional; defaults to 0 (no proxy in front). */
37
48
  readonly trustedProxyHopCount?: ConfigValue<number>
49
+ /**
50
+ * Whether the instance accepts a brand-new account. Optional; defaults to
51
+ * `'open'`, matching `loadConfig`'s default, so an assembly that never sets
52
+ * this sees no change in behaviour.
53
+ */
54
+ readonly signups?: ConfigValue<SignupsMode>
55
+ /**
56
+ * Origins a multi-region assembly advertises on signed-out pages. Optional;
57
+ * omitted or empty hides the switcher, matching a self-hosted install.
58
+ * Two or more draws it. Each `id` and `origin` must be unique, and each
59
+ * `origin` must be an absolute `http:` or `https:` origin.
60
+ */
61
+ readonly regions?: ConfigValue<readonly PublicRegion[]>
38
62
  /**
39
63
  * The deployment's base URL. Every emailed link (password reset, email
40
64
  * verification, invite) is built from it. Required by the workspace and auth
@@ -164,6 +188,9 @@ export function resolveKelpieConfig(input: KelpieConfigInput, environment: Envir
164
188
  const rateLimit = buildRateLimitConfig(resolved.rateLimit)
165
189
  const env = mergeEnv(environment, resolved.env)
166
190
  const secretEncryption = buildSecretEncryptionConfig(resolved.secretEncryption)
191
+ const regions = resolved.regions ?? []
192
+
193
+ problems.push(...describeRegionProblems(regions))
167
194
 
168
195
  if (problems.length > 0) {
169
196
  throw new ConfigurationError(problems)
@@ -186,6 +213,8 @@ export function resolveKelpieConfig(input: KelpieConfigInput, environment: Envir
186
213
  env,
187
214
  appBaseUrl: resolved.appBaseUrl,
188
215
  secretEncryption,
216
+ signups: resolved.signups ?? 'open',
217
+ regions,
189
218
  }
190
219
  }
191
220
 
@@ -217,6 +246,8 @@ interface ResolvedInput {
217
246
  readonly moduleConfigPath?: string | undefined
218
247
  readonly siteName?: string | undefined
219
248
  readonly trustedProxyHopCount?: number
249
+ readonly signups?: SignupsMode
250
+ readonly regions?: readonly PublicRegion[]
220
251
  readonly appBaseUrl?: string
221
252
  readonly secretEncryption?: ResolvedSecretEncryption
222
253
  readonly email: ResolvedEmail
@@ -242,6 +273,43 @@ interface ResolvedRateLimit {
242
273
  readonly api?: { readonly limit?: number; readonly windowSeconds?: number }
243
274
  }
244
275
 
276
+ function describeRegionProblems(regions: readonly unknown[]): string[] {
277
+ const problems: string[] = []
278
+ const ids = new Set<string>()
279
+ const origins = new Set<string>()
280
+
281
+ for (const [index, region] of regions.entries()) {
282
+ const parsed = publicRegionSchema.safeParse(region)
283
+
284
+ if (!parsed.success) {
285
+ for (const issue of parsed.error.issues) {
286
+ problems.push(
287
+ describeValidationIssue({
288
+ path: ['regions', index, ...issue.path],
289
+ message: issue.message,
290
+ }),
291
+ )
292
+ }
293
+
294
+ continue
295
+ }
296
+
297
+ if (ids.has(parsed.data.id)) {
298
+ problems.push(`regions.${String(index)}.id: duplicate id '${parsed.data.id}'`)
299
+ }
300
+
301
+ ids.add(parsed.data.id)
302
+
303
+ if (origins.has(parsed.data.origin)) {
304
+ problems.push(`regions.${String(index)}.origin: duplicate origin '${parsed.data.origin}'`)
305
+ }
306
+
307
+ origins.add(parsed.data.origin)
308
+ }
309
+
310
+ return problems
311
+ }
312
+
245
313
  function buildEmailConfig(resolved: ResolvedEmail | undefined): EmailConfig {
246
314
  // Missing `email.provider` or `email.from` is already recorded by the
247
315
  // walker; return a placeholder so `resolveKelpieConfig` reaches its throw
@@ -128,7 +128,7 @@ Append a Note, update summary only when clearly supported, propose Plan items on
128
128
  targetTypes: ['company'],
129
129
  placement: 'primary',
130
130
  handbookSlugs: ['ideal-customer-profile', 'agent-faq'],
131
- instructions: `Research this Company. Update description, stage, sizeBand, techStack, website/linkedin when evidenced, tags, and summary.
131
+ instructions: `Research this Company. Update description, stage, sizeBand, techStack, domain when evidenced, tags, and summary.
132
132
  Append a Note listing sources. Prefer notes over guesses for uncertain facts.`,
133
133
  }),
134
134
  task({
@@ -623,6 +623,28 @@ Ask before setting stage to passed.`,
623
623
  instructions: `The human will paste notes after this prompt. Append a Note; propose Plan items; extract Decisions.`,
624
624
  }),
625
625
 
626
+ // Event
627
+ task({
628
+ id: 'event.brief',
629
+ label: 'Event brief',
630
+ description: 'Agenda, who is registered, linked records, open Plans.',
631
+ targetTypes: ['event'],
632
+ placement: 'primary',
633
+ handbookSlugs: ['agent-faq'],
634
+ instructions: `Prepare a brief for this Event: agenda from details, who is registered, linked records, and open Plan items.
635
+ Do not invent attendees. Prefer pinned notes and Decisions.`,
636
+ }),
637
+ task({
638
+ id: 'event.follow_up',
639
+ label: 'Follow up',
640
+ description: 'Draft follow-up from attended vs no-show Attendances.',
641
+ targetTypes: ['event'],
642
+ placement: 'primary',
643
+ handbookSlugs: ['voice-and-tone', 'agent-faq'],
644
+ instructions: `Draft follow-up from this Event's Attendances. Separate attended from no-show.
645
+ Save drafts as Notes. Do not send email.`,
646
+ }),
647
+
626
648
  // Candidate
627
649
  task({
628
650
  id: 'candidate.score',
@@ -753,6 +775,16 @@ Suggest Draft outreach or Plan items. Do not spam everyone.`,
753
775
  handbookSlugs: ['how-we-sell', 'agent-faq'],
754
776
  instructions: `Propose a week of Plan items across open Deals, Opportunities, Raises, and Partnerships.
755
777
  Create Plan items only when clearly useful; otherwise list proposals for confirmation.`,
778
+ }),
779
+ task({
780
+ id: 'workspace.upcoming_events',
781
+ label: 'Upcoming events',
782
+ description: 'Scheduled Events in the next week and their attendee counts.',
783
+ targetTypes: ['workspace'],
784
+ placement: 'primary',
785
+ handbookSlugs: ['agent-faq'],
786
+ instructions: `List scheduled Events in the next week with attendee counts.
787
+ Use GET /v1/dashboard upcoming_events. Suggest follow-up or brief tasks where useful.`,
756
788
  }),
757
789
  task({
758
790
  id: 'workspace.empty_field_sweep',
@@ -38,6 +38,7 @@ export function createAgentTasksModule(
38
38
  'partnerships',
39
39
  'raises',
40
40
  'hiring',
41
+ 'events',
41
42
  'plans',
42
43
  'decisions',
43
44
  'notes',
@@ -9,6 +9,7 @@ import { companies } from '../companies/schema.ts'
9
9
  import { deals } from '../deals/schema.ts'
10
10
  import { decisions } from '../decisions/schema.ts'
11
11
  import { enquiries } from '../enquiries/schema.ts'
12
+ import { attendances } from '../events/schema.ts'
12
13
  import { handbookPages } from '../handbook/schema.ts'
13
14
  import { candidates, roles } from '../hiring/schema.ts'
14
15
  import { notes } from '../notes/schema.ts'
@@ -443,6 +444,16 @@ async function collectRelated(
443
444
  ),
444
445
  }
445
446
 
447
+ case 'event':
448
+ return {
449
+ person_ids: await relatedFrom(
450
+ db,
451
+ attendances,
452
+ attendances.personId,
453
+ and(eq(attendances.workspaceId, workspaceId), eq(attendances.eventId, targetId)) as SQL,
454
+ ),
455
+ }
456
+
446
457
  case 'handbook':
447
458
  case 'workspace':
448
459
  return {}