@elevasis/core 0.7.1 → 0.8.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 (473) hide show
  1. package/package.json +1 -1
  2. package/src/README.md +41 -41
  3. package/src/__tests__/publish.test.ts +18 -18
  4. package/src/__tests__/{template-foundations-compatibility.test.ts → template-core-compatibility.test.ts} +99 -99
  5. package/src/_gen/__tests__/__snapshots__/contracts.md.snap +1135 -1131
  6. package/src/_gen/__tests__/scaffold-contracts.test.ts +53 -53
  7. package/src/_gen/scaffold-contracts.ts +45 -45
  8. package/src/auth/multi-tenancy/credentials/README.md +38 -38
  9. package/src/auth/multi-tenancy/credentials/index.ts +6 -6
  10. package/src/auth/multi-tenancy/credentials/server/encryption.ts +39 -39
  11. package/src/auth/multi-tenancy/credentials/server/service.ts +60 -60
  12. package/src/auth/multi-tenancy/index.ts +17 -17
  13. package/src/auth/multi-tenancy/invitations/api-schemas.ts +107 -107
  14. package/src/auth/multi-tenancy/invitations/index.ts +37 -37
  15. package/src/auth/multi-tenancy/invitations/invitation.ts +86 -86
  16. package/src/auth/multi-tenancy/invitations/server/index.ts +25 -25
  17. package/src/auth/multi-tenancy/invitations/server/transforms.ts +24 -24
  18. package/src/auth/multi-tenancy/invitations/server/workos.ts +24 -24
  19. package/src/auth/multi-tenancy/invitations/supabase.ts +50 -50
  20. package/src/auth/multi-tenancy/memberships/api-schemas.ts +126 -126
  21. package/src/auth/multi-tenancy/memberships/index.ts +21 -21
  22. package/src/auth/multi-tenancy/memberships/membership.ts +138 -138
  23. package/src/auth/multi-tenancy/memberships/server/index.ts +15 -15
  24. package/src/auth/multi-tenancy/memberships/server/transforms.ts +32 -32
  25. package/src/auth/multi-tenancy/memberships/server/workos.ts +21 -21
  26. package/src/auth/multi-tenancy/memberships/supabase.ts +46 -46
  27. package/src/auth/multi-tenancy/organizations/api-schemas.ts +128 -128
  28. package/src/auth/multi-tenancy/organizations/index.ts +23 -23
  29. package/src/auth/multi-tenancy/organizations/organization.ts +24 -24
  30. package/src/auth/multi-tenancy/organizations/server/index.ts +10 -10
  31. package/src/auth/multi-tenancy/organizations/server/transforms.ts +35 -35
  32. package/src/auth/multi-tenancy/organizations/server/workos.ts +20 -20
  33. package/src/auth/multi-tenancy/types.ts +83 -83
  34. package/src/auth/multi-tenancy/users/api-schemas.ts +194 -194
  35. package/src/auth/multi-tenancy/users/index.ts +27 -27
  36. package/src/auth/multi-tenancy/users/server/index.ts +19 -19
  37. package/src/auth/multi-tenancy/users/server/transforms.ts +21 -21
  38. package/src/auth/multi-tenancy/users/server/workos.ts +16 -16
  39. package/src/auth/multi-tenancy/users/user.ts +65 -65
  40. package/src/business/README.md +52 -52
  41. package/src/business/__tests__/entities-published.test.ts +33 -33
  42. package/src/business/acquisition/api-schemas.ts +759 -759
  43. package/src/business/acquisition/index.ts +109 -109
  44. package/src/business/acquisition/types.ts +402 -402
  45. package/src/business/base-entities.test.ts +481 -481
  46. package/src/business/base-entities.ts +241 -241
  47. package/src/business/entities-published.ts +24 -24
  48. package/src/business/index.ts +15 -15
  49. package/src/business/pdf/browser/pdfmake-browser.ts +229 -229
  50. package/src/business/pdf/index.ts +10 -10
  51. package/src/business/pdf/server/index.ts +21 -21
  52. package/src/business/pdf/server/themes/default.ts +8 -8
  53. package/src/business/pdf/server/themes/index.ts +9 -9
  54. package/src/business/pdf/server/themes/types.ts +8 -8
  55. package/src/business/pdf/types.ts +272 -272
  56. package/src/business/projects/index.ts +2 -2
  57. package/src/business/projects/sse-events.ts +21 -21
  58. package/src/business/projects/types.ts +89 -89
  59. package/src/business/sales/api-schemas.ts +75 -75
  60. package/src/business/seo/__tests__/linking.test.ts +549 -549
  61. package/src/business/seo/__tests__/types.test.ts +404 -404
  62. package/src/business/seo/index.ts +2 -2
  63. package/src/business/seo/linking.ts +281 -281
  64. package/src/business/seo/types.ts +199 -199
  65. package/src/commands/queue/index.ts +3 -3
  66. package/src/commands/queue/schemas.test.ts +593 -593
  67. package/src/commands/queue/schemas.ts +125 -125
  68. package/src/commands/queue/sse-events.ts +61 -61
  69. package/src/commands/queue/types/action.ts +52 -52
  70. package/src/commands/queue/types/checkpoint.ts +44 -44
  71. package/src/commands/queue/types/index.ts +7 -7
  72. package/src/commands/queue/types/task.ts +116 -116
  73. package/src/commands/queue/types.ts +14 -14
  74. package/src/content/distribution-metadata.ts +61 -61
  75. package/src/content/index.ts +10 -10
  76. package/src/deployments/index.ts +22 -22
  77. package/src/execution/core/__tests__/archived-logs.test.ts +72 -72
  78. package/src/execution/core/index.ts +11 -11
  79. package/src/execution/core/runner-types.ts +80 -80
  80. package/src/execution/core/server/environment.ts +31 -31
  81. package/src/execution/core/sse-executions.ts +119 -119
  82. package/src/execution/core/types.ts +29 -29
  83. package/src/execution/engine/__tests__/fixtures/test-agents.ts +4 -4
  84. package/src/execution/engine/__tests__/timeout.test.ts +565 -565
  85. package/src/execution/engine/agent/__tests__/errors.test.ts +508 -508
  86. package/src/execution/engine/agent/actions/__tests__/processor.test.ts +531 -531
  87. package/src/execution/engine/agent/actions/executor.ts +205 -205
  88. package/src/execution/engine/agent/actions/navigate-knowledge-executor.ts +230 -230
  89. package/src/execution/engine/agent/actions/processor.ts +116 -116
  90. package/src/execution/engine/agent/actions/types.ts +70 -70
  91. package/src/execution/engine/agent/core/agent.ts +810 -810
  92. package/src/execution/engine/agent/core/types.ts +155 -155
  93. package/src/execution/engine/agent/errors.ts +251 -251
  94. package/src/execution/engine/agent/index.ts +78 -78
  95. package/src/execution/engine/agent/knowledge-map/types.ts +106 -106
  96. package/src/execution/engine/agent/knowledge-map/utils.ts +101 -101
  97. package/src/execution/engine/agent/memory/__tests__/manager.test.ts +754 -754
  98. package/src/execution/engine/agent/memory/domains.ts +99 -99
  99. package/src/execution/engine/agent/memory/manager.ts +365 -365
  100. package/src/execution/engine/agent/memory/processor.ts +66 -66
  101. package/src/execution/engine/agent/memory/types.ts +90 -90
  102. package/src/execution/engine/agent/memory/utils.ts +134 -134
  103. package/src/execution/engine/agent/observability/logging.ts +467 -467
  104. package/src/execution/engine/agent/observability/types.ts +64 -64
  105. package/src/execution/engine/agent/reasoning/adapters/agent-adapter-helpers.ts +349 -349
  106. package/src/execution/engine/agent/reasoning/processor.ts +92 -92
  107. package/src/execution/engine/agent/reasoning/prompt-sections/base-actions.ts +134 -134
  108. package/src/execution/engine/agent/reasoning/prompt-sections/completion.ts +49 -49
  109. package/src/execution/engine/agent/reasoning/prompt-sections/knowledge-map.ts +93 -93
  110. package/src/execution/engine/agent/reasoning/prompt-sections/memory.ts +65 -65
  111. package/src/execution/engine/agent/reasoning/prompt-sections/tools.ts +44 -44
  112. package/src/execution/engine/agent/reasoning/request-builder.ts +169 -169
  113. package/src/execution/engine/agent/reasoning/types.ts +18 -18
  114. package/src/execution/engine/base/errors.ts +118 -118
  115. package/src/execution/engine/base/index.ts +2 -2
  116. package/src/execution/engine/base/logging.ts +31 -31
  117. package/src/execution/engine/base/serialization.ts +324 -324
  118. package/src/execution/engine/base/types.ts +126 -126
  119. package/src/execution/engine/base/utils.ts +41 -41
  120. package/src/execution/engine/index.ts +434 -434
  121. package/src/execution/engine/interface/index.ts +1 -1
  122. package/src/execution/engine/interface/types.ts +62 -62
  123. package/src/execution/engine/llm/__tests__/model-info.test.ts +50 -50
  124. package/src/execution/engine/llm/__tests__/model-validation.test.ts +321 -321
  125. package/src/execution/engine/llm/__tests__/response-schema-validator.test.ts +115 -115
  126. package/src/execution/engine/llm/adapters/__tests__/adapter-factory.test.ts +375 -375
  127. package/src/execution/engine/llm/adapters/__tests__/anthropic-adapter.test.ts +463 -463
  128. package/src/execution/engine/llm/adapters/__tests__/anthropic.integration.test.ts +177 -177
  129. package/src/execution/engine/llm/adapters/__tests__/google-adapter.test.ts +722 -722
  130. package/src/execution/engine/llm/adapters/__tests__/google.integration.test.ts +376 -376
  131. package/src/execution/engine/llm/adapters/__tests__/openai-adapter.test.ts +551 -551
  132. package/src/execution/engine/llm/adapters/__tests__/openrouter-adapter.test.ts +563 -563
  133. package/src/execution/engine/llm/adapters/__tests__/openrouter.integration.test.ts +105 -105
  134. package/src/execution/engine/llm/adapters/__tests__/universal-adapter.test.ts +537 -537
  135. package/src/execution/engine/llm/adapters/circuit-breaker.ts +147 -147
  136. package/src/execution/engine/llm/adapters/index.ts +17 -17
  137. package/src/execution/engine/llm/adapters/mock-adapter.ts +116 -116
  138. package/src/execution/engine/llm/adapters/server/adapter-factory.ts +130 -130
  139. package/src/execution/engine/llm/adapters/server/anthropic.ts +137 -137
  140. package/src/execution/engine/llm/adapters/server/google.ts +283 -283
  141. package/src/execution/engine/llm/adapters/server/index.ts +12 -12
  142. package/src/execution/engine/llm/adapters/server/openai.ts +206 -206
  143. package/src/execution/engine/llm/adapters/server/openrouter.ts +235 -235
  144. package/src/execution/engine/llm/adapters/universal-adapter.ts +230 -230
  145. package/src/execution/engine/llm/errors.ts +186 -186
  146. package/src/execution/engine/llm/model-info.ts +332 -332
  147. package/src/execution/engine/llm/response-schema-validator.ts +113 -113
  148. package/src/execution/engine/llm/types.ts +86 -86
  149. package/src/execution/engine/test-utils/index.ts +6 -6
  150. package/src/execution/engine/test-utils/mocks.ts +56 -56
  151. package/src/execution/engine/tools/integration/base-integration-adapter.ts +50 -50
  152. package/src/execution/engine/tools/integration/index.ts +53 -53
  153. package/src/execution/engine/tools/integration/server/adapters/anymailfinder/anymailfinder-adapter.ts +73 -73
  154. package/src/execution/engine/tools/integration/server/adapters/anymailfinder/anymailfinder-tools.ts +209 -209
  155. package/src/execution/engine/tools/integration/server/adapters/anymailfinder/fetch/find-company-email/index.ts +82 -82
  156. package/src/execution/engine/tools/integration/server/adapters/anymailfinder/fetch/find-decision-maker-email/index.ts +122 -122
  157. package/src/execution/engine/tools/integration/server/adapters/anymailfinder/fetch/find-person-email/index.ts +89 -89
  158. package/src/execution/engine/tools/integration/server/adapters/anymailfinder/fetch/verify-email/index.ts +84 -84
  159. package/src/execution/engine/tools/integration/server/adapters/anymailfinder/index.ts +16 -16
  160. package/src/execution/engine/tools/integration/server/adapters/apify/__tests__/apify-run-actor.integration.test.ts +293 -293
  161. package/src/execution/engine/tools/integration/server/adapters/apify/apify-adapter.ts +100 -100
  162. package/src/execution/engine/tools/integration/server/adapters/apify/apify-tools.ts +217 -217
  163. package/src/execution/engine/tools/integration/server/adapters/apify/fetch/get-dataset-items/index.ts +92 -92
  164. package/src/execution/engine/tools/integration/server/adapters/apify/fetch/run-actor/index.ts +218 -218
  165. package/src/execution/engine/tools/integration/server/adapters/apify/fetch/start-actor/index.ts +87 -87
  166. package/src/execution/engine/tools/integration/server/adapters/apify/index.ts +11 -11
  167. package/src/execution/engine/tools/integration/server/adapters/attio/__tests__/attio-crud.integration.test.ts +361 -361
  168. package/src/execution/engine/tools/integration/server/adapters/attio/attio-adapter.ts +162 -162
  169. package/src/execution/engine/tools/integration/server/adapters/attio/attio-tools.ts +594 -594
  170. package/src/execution/engine/tools/integration/server/adapters/attio/fetch/create-attribute/index.ts +214 -214
  171. package/src/execution/engine/tools/integration/server/adapters/attio/fetch/create-note/index.ts +152 -152
  172. package/src/execution/engine/tools/integration/server/adapters/attio/fetch/create-record/index.ts +141 -141
  173. package/src/execution/engine/tools/integration/server/adapters/attio/fetch/delete-note/index.ts +86 -86
  174. package/src/execution/engine/tools/integration/server/adapters/attio/fetch/delete-record/index.ts +105 -105
  175. package/src/execution/engine/tools/integration/server/adapters/attio/fetch/get-record/index.ts +118 -118
  176. package/src/execution/engine/tools/integration/server/adapters/attio/fetch/list-attributes/index.ts +165 -165
  177. package/src/execution/engine/tools/integration/server/adapters/attio/fetch/list-notes/index.ts +96 -96
  178. package/src/execution/engine/tools/integration/server/adapters/attio/fetch/list-objects/index.ts +104 -104
  179. package/src/execution/engine/tools/integration/server/adapters/attio/fetch/list-records/index.ts +156 -156
  180. package/src/execution/engine/tools/integration/server/adapters/attio/fetch/update-attribute/index.ts +220 -220
  181. package/src/execution/engine/tools/integration/server/adapters/attio/fetch/update-record/index.ts +140 -140
  182. package/src/execution/engine/tools/integration/server/adapters/attio/fetch/utils/types.ts +146 -146
  183. package/src/execution/engine/tools/integration/server/adapters/attio/index.ts +31 -31
  184. package/src/execution/engine/tools/integration/server/adapters/gmail/gmail-adapter.ts +210 -210
  185. package/src/execution/engine/tools/integration/server/adapters/gmail/gmail-tools.ts +104 -104
  186. package/src/execution/engine/tools/integration/server/adapters/google-sheets/__tests__/google-sheets.integration.test.ts +261 -261
  187. package/src/execution/engine/tools/integration/server/adapters/google-sheets/google-sheets-adapter.ts +1189 -1189
  188. package/src/execution/engine/tools/integration/server/adapters/google-sheets/google-sheets-tools.ts +641 -641
  189. package/src/execution/engine/tools/integration/server/adapters/google-sheets/index.ts +18 -18
  190. package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/activate-campaign/index.ts +86 -86
  191. package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/add-to-campaign/__tests__/index.test.ts +289 -289
  192. package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/add-to-campaign/index.ts +154 -154
  193. package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/bulk-add-leads/__tests__/index.test.ts +325 -325
  194. package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/bulk-add-leads/index.ts +153 -153
  195. package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/bulk-delete-leads/index.ts +84 -84
  196. package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/create-campaign/index.ts +125 -125
  197. package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/create-inbox-test/index.ts +107 -107
  198. package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/delete-campaign/index.ts +85 -85
  199. package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/get-account-health/index.ts +91 -91
  200. package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/get-campaign/index.ts +92 -92
  201. package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/get-campaign-analytics/__tests__/index.test.ts +195 -195
  202. package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/get-campaign-analytics/index.ts +113 -113
  203. package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/get-daily-campaign-analytics/index.ts +104 -104
  204. package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/get-emails/index.ts +155 -155
  205. package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/get-step-analytics/__tests__/index.test.ts +196 -196
  206. package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/get-step-analytics/index.ts +102 -102
  207. package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/list-campaigns/__tests__/index.test.ts +189 -189
  208. package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/list-campaigns/index.ts +87 -87
  209. package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/list-leads/index.ts +112 -112
  210. package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/patch-lead/index.ts +76 -76
  211. package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/pause-campaign/index.ts +86 -86
  212. package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/remove-from-subsequence/index.ts +98 -98
  213. package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/send-reply/index.ts +126 -126
  214. package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/update-campaign/__tests__/index.test.ts +193 -193
  215. package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/update-campaign/index.ts +99 -99
  216. package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/update-interest-status/__tests__/index.test.ts +621 -621
  217. package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/update-interest-status/index.ts +125 -125
  218. package/src/execution/engine/tools/integration/server/adapters/instantly/index.ts +29 -29
  219. package/src/execution/engine/tools/integration/server/adapters/instantly/instantly-adapter.ts +178 -178
  220. package/src/execution/engine/tools/integration/server/adapters/instantly/instantly-tools.ts +1473 -1473
  221. package/src/execution/engine/tools/integration/server/adapters/millionverifier/fetch/check-credits/index.ts +59 -59
  222. package/src/execution/engine/tools/integration/server/adapters/millionverifier/fetch/verify-email/index.ts +102 -102
  223. package/src/execution/engine/tools/integration/server/adapters/millionverifier/index.ts +17 -17
  224. package/src/execution/engine/tools/integration/server/adapters/millionverifier/millionverifier-adapter.ts +80 -80
  225. package/src/execution/engine/tools/integration/server/adapters/millionverifier/millionverifier-tools.ts +102 -102
  226. package/src/execution/engine/tools/integration/server/adapters/resend/fetch/get-email/index.ts +102 -102
  227. package/src/execution/engine/tools/integration/server/adapters/resend/fetch/send-email/index.ts +134 -134
  228. package/src/execution/engine/tools/integration/server/adapters/resend/fetch/utils/types.ts +75 -75
  229. package/src/execution/engine/tools/integration/server/adapters/resend/index.ts +27 -27
  230. package/src/execution/engine/tools/integration/server/adapters/resend/resend-adapter.ts +108 -108
  231. package/src/execution/engine/tools/integration/server/adapters/resend/resend-tools.ts +132 -132
  232. package/src/execution/engine/tools/integration/server/adapters/signature-api/fetch/create-envelope/index.ts +274 -274
  233. package/src/execution/engine/tools/integration/server/adapters/signature-api/fetch/download-document/index.ts +230 -230
  234. package/src/execution/engine/tools/integration/server/adapters/signature-api/fetch/get-envelope/index.ts +133 -133
  235. package/src/execution/engine/tools/integration/server/adapters/signature-api/fetch/void-envelope/index.ts +90 -90
  236. package/src/execution/engine/tools/integration/server/adapters/stripe/fetch/utils/types.ts +210 -210
  237. package/src/execution/engine/tools/integration/server/adapters/stripe/stripe-adapter.ts +517 -517
  238. package/src/execution/engine/tools/integration/server/adapters/stripe/stripe-tools.ts +309 -309
  239. package/src/execution/engine/tools/integration/server/adapters/tomba/fetch/domain-search/index.ts +133 -133
  240. package/src/execution/engine/tools/integration/server/adapters/tomba/fetch/email-finder/index.ts +122 -122
  241. package/src/execution/engine/tools/integration/server/adapters/tomba/fetch/email-verifier/index.ts +111 -111
  242. package/src/execution/engine/tools/integration/server/adapters/tomba/index.ts +11 -11
  243. package/src/execution/engine/tools/integration/server/adapters/tomba/tomba-adapter.ts +78 -78
  244. package/src/execution/engine/tools/integration/server/adapters/tomba/tomba-tools.ts +222 -222
  245. package/src/execution/engine/tools/integration/server/index.ts +61 -61
  246. package/src/execution/engine/tools/integration/service.ts +161 -161
  247. package/src/execution/engine/tools/integration/tool.ts +253 -253
  248. package/src/execution/engine/tools/integration/types/anymailfinder.ts +74 -74
  249. package/src/execution/engine/tools/integration/types/apify.ts +92 -92
  250. package/src/execution/engine/tools/integration/types/index.ts +19 -19
  251. package/src/execution/engine/tools/integration/types/instantly.ts +557 -557
  252. package/src/execution/engine/tools/integration/types/millionverifier.ts +56 -56
  253. package/src/execution/engine/tools/integration/types/stripe.ts +162 -162
  254. package/src/execution/engine/tools/integration/types/tomba.ts +94 -94
  255. package/src/execution/engine/tools/lead-service-types.ts +884 -884
  256. package/src/execution/engine/tools/llm/index.ts +11 -11
  257. package/src/execution/engine/tools/llm/server/index.ts +8 -8
  258. package/src/execution/engine/tools/llm/server/llm-call-tool.ts +118 -118
  259. package/src/execution/engine/tools/platform/__tests__/pdf.test.ts +441 -441
  260. package/src/execution/engine/tools/platform/acquisition/company-tools.ts +248 -248
  261. package/src/execution/engine/tools/platform/acquisition/contact-tools.ts +319 -319
  262. package/src/execution/engine/tools/platform/acquisition/index.ts +43 -43
  263. package/src/execution/engine/tools/platform/acquisition/list-tools.ts +148 -148
  264. package/src/execution/engine/tools/platform/acquisition/types.ts +260 -260
  265. package/src/execution/engine/tools/platform/email/index.ts +122 -122
  266. package/src/execution/engine/tools/platform/email/types.ts +96 -96
  267. package/src/execution/engine/tools/platform/index.ts +157 -157
  268. package/src/execution/engine/tools/platform/notification.ts +81 -81
  269. package/src/execution/engine/tools/platform/pdf/index.ts +110 -110
  270. package/src/execution/engine/tools/platform/pdf/types.ts +77 -77
  271. package/src/execution/engine/tools/platform/scheduler.ts +87 -87
  272. package/src/execution/engine/tools/platform/storage/index.ts +370 -370
  273. package/src/execution/engine/tools/platform/types.ts +148 -148
  274. package/src/execution/engine/tools/registry.ts +700 -700
  275. package/src/execution/engine/tools/tool-maps.ts +786 -786
  276. package/src/execution/engine/tools/types.ts +233 -233
  277. package/src/execution/engine/workflow/__tests__/errors.test.ts +139 -139
  278. package/src/execution/engine/workflow/errors.ts +63 -63
  279. package/src/execution/engine/workflow/helpers/index.ts +11 -11
  280. package/src/execution/engine/workflow/helpers/server/index.ts +8 -8
  281. package/src/execution/engine/workflow/helpers/server/llm-call.ts +93 -93
  282. package/src/execution/engine/workflow/index.ts +19 -19
  283. package/src/execution/engine/workflow/log-truncate.ts +26 -26
  284. package/src/execution/engine/workflow/logging.ts +191 -191
  285. package/src/execution/engine/workflow/types.ts +182 -182
  286. package/src/execution/engine/workflow/utils.ts +280 -280
  287. package/src/execution/engine/workflow/workflow.ts +168 -168
  288. package/src/execution/index.ts +3 -3
  289. package/src/execution/scheduler/__tests__/api-schemas.test.ts +733 -733
  290. package/src/execution/scheduler/__tests__/utils.test.ts +1009 -1009
  291. package/src/execution/scheduler/api-schemas.ts +296 -296
  292. package/src/execution/scheduler/index.ts +50 -50
  293. package/src/execution/scheduler/schemas.ts +264 -264
  294. package/src/execution/scheduler/types.ts +111 -111
  295. package/src/execution/scheduler/utils.ts +364 -364
  296. package/src/forms/index.ts +7 -7
  297. package/src/forms/schemas.ts +69 -69
  298. package/src/forms/types.ts +70 -70
  299. package/src/index.ts +71 -60
  300. package/src/integrations/credentials/__tests__/schemas.test.ts +82 -82
  301. package/src/integrations/credentials/__tests__/utils.test.ts +144 -144
  302. package/src/integrations/credentials/api-schemas.ts +143 -143
  303. package/src/integrations/credentials/index.ts +32 -32
  304. package/src/integrations/credentials/schemas.ts +164 -164
  305. package/src/integrations/credentials/utils.ts +59 -59
  306. package/src/integrations/oauth/__tests__/provider-registry.test.ts +59 -59
  307. package/src/integrations/oauth/api-schemas.ts +92 -92
  308. package/src/integrations/oauth/index.ts +19 -19
  309. package/src/integrations/oauth/provider-registry.ts +61 -61
  310. package/src/integrations/oauth/server/__tests__/refresh-concurrent.test.ts +183 -183
  311. package/src/integrations/oauth/server/__tests__/refresh.test.ts +577 -577
  312. package/src/integrations/oauth/server/credentials.ts +39 -39
  313. package/src/integrations/oauth/server/refresh.ts +214 -214
  314. package/src/integrations/oauth/types.ts +34 -34
  315. package/src/integrations/webhook-endpoints/__tests__/api-schemas.test.ts +318 -318
  316. package/src/integrations/webhook-endpoints/api-schemas.ts +102 -102
  317. package/src/integrations/webhook-endpoints/index.ts +28 -28
  318. package/src/integrations/webhook-endpoints/types.ts +51 -51
  319. package/src/operations/activities/api-schemas.ts +79 -79
  320. package/src/operations/activities/index.ts +9 -9
  321. package/src/operations/activities/sse-events.ts +30 -30
  322. package/src/operations/activities/types.ts +63 -63
  323. package/src/operations/debug-logs/client.ts +60 -60
  324. package/src/operations/debug-logs/debug-logger.ts +83 -83
  325. package/src/operations/debug-logs/index.ts +8 -8
  326. package/src/operations/debug-logs/server.ts +19 -19
  327. package/src/operations/debug-logs/types.ts +33 -33
  328. package/src/operations/index.ts +50 -50
  329. package/src/operations/notifications/api-schemas.ts +91 -91
  330. package/src/operations/notifications/index.ts +3 -3
  331. package/src/operations/notifications/sse-events.ts +21 -21
  332. package/src/operations/notifications/types.ts +47 -47
  333. package/src/operations/observability/__tests__/openrouter-cost-flow.test.ts +297 -297
  334. package/src/operations/observability/__tests__/utils.test.ts +54 -54
  335. package/src/operations/observability/ai-usage-collector.ts +64 -64
  336. package/src/operations/observability/index.ts +13 -13
  337. package/src/operations/observability/metrics-collector.ts +49 -49
  338. package/src/operations/observability/schemas.ts +39 -39
  339. package/src/operations/observability/types.ts +463 -463
  340. package/src/operations/observability/utils.ts +77 -77
  341. package/src/operations/sessions/__tests__/manager.test.ts +821 -821
  342. package/src/operations/sessions/index.ts +26 -26
  343. package/src/operations/sessions/server/manager.ts +90 -90
  344. package/src/operations/sessions/server/session.ts +180 -180
  345. package/src/operations/sessions/types.ts +98 -98
  346. package/src/operations/triggers/index.ts +12 -12
  347. package/src/operations/triggers/webhook/definitions/instantly-account-error.ts +44 -44
  348. package/src/operations/triggers/webhook/definitions/instantly-auto-reply-received.ts +51 -51
  349. package/src/operations/triggers/webhook/definitions/instantly-campaign-completed.ts +45 -45
  350. package/src/operations/triggers/webhook/definitions/instantly-email-bounced.ts +49 -49
  351. package/src/operations/triggers/webhook/definitions/instantly-lead-unsubscribed.ts +45 -45
  352. package/src/operations/triggers/webhook/definitions/instantly-reply-received.ts +54 -54
  353. package/src/operations/triggers/webhook/index.ts +35 -35
  354. package/src/operations/triggers/webhook/types.ts +74 -74
  355. package/src/organization-model/README.md +97 -97
  356. package/src/organization-model/__tests__/defaults.test.ts +175 -175
  357. package/src/organization-model/__tests__/domains/customers.test.ts +295 -295
  358. package/src/organization-model/__tests__/domains/goals.test.ts +479 -479
  359. package/src/organization-model/__tests__/domains/identity.test.ts +279 -279
  360. package/src/organization-model/__tests__/domains/navigation.test.ts +212 -212
  361. package/src/organization-model/__tests__/domains/offerings.test.ts +419 -419
  362. package/src/organization-model/__tests__/domains/operations.test.ts +203 -203
  363. package/src/organization-model/__tests__/domains/resource-mappings.test.ts +362 -362
  364. package/src/organization-model/__tests__/domains/roles.test.ts +347 -347
  365. package/src/organization-model/__tests__/domains/statuses.test.ts +243 -243
  366. package/src/organization-model/__tests__/foundation.test.ts +105 -105
  367. package/src/organization-model/__tests__/graph.test.ts +894 -894
  368. package/src/organization-model/__tests__/resolve.test.ts +690 -690
  369. package/src/organization-model/__tests__/schema.test.ts +407 -407
  370. package/src/organization-model/contracts.ts +14 -14
  371. package/src/organization-model/defaults.ts +148 -148
  372. package/src/organization-model/domains/branding.ts +22 -22
  373. package/src/organization-model/domains/customers.ts +75 -75
  374. package/src/organization-model/domains/features.ts +22 -22
  375. package/src/organization-model/domains/goals.ts +80 -80
  376. package/src/organization-model/domains/identity.ts +94 -94
  377. package/src/organization-model/domains/navigation.ts +391 -391
  378. package/src/organization-model/domains/offerings.ts +66 -66
  379. package/src/organization-model/domains/operations.ts +85 -85
  380. package/src/organization-model/domains/projects.ts +48 -48
  381. package/src/organization-model/domains/prospecting.ts +33 -33
  382. package/src/organization-model/domains/roles.ts +55 -55
  383. package/src/organization-model/domains/sales.ts +94 -94
  384. package/src/organization-model/domains/shared.ts +62 -62
  385. package/src/organization-model/domains/statuses.ts +130 -130
  386. package/src/organization-model/foundation.ts +97 -97
  387. package/src/organization-model/graph/build.ts +399 -399
  388. package/src/organization-model/graph/index.ts +4 -4
  389. package/src/organization-model/graph/schema.ts +48 -48
  390. package/src/organization-model/graph/types.ts +40 -40
  391. package/src/organization-model/index.ts +13 -13
  392. package/src/organization-model/organization-graph.mdx +272 -272
  393. package/src/organization-model/organization-model.mdx +320 -320
  394. package/src/organization-model/published.ts +85 -85
  395. package/src/organization-model/resolve.ts +66 -66
  396. package/src/organization-model/schema.ts +287 -287
  397. package/src/organization-model/types.ts +46 -46
  398. package/src/platform/api/index.ts +1 -1
  399. package/src/platform/api/types.ts +35 -35
  400. package/src/platform/constants/http.ts +37 -37
  401. package/src/platform/constants/index.ts +5 -5
  402. package/src/platform/constants/limits.ts +32 -32
  403. package/src/platform/constants/resilience.ts +51 -51
  404. package/src/platform/constants/timeouts.ts +20 -20
  405. package/src/platform/constants/versions.ts +3 -3
  406. package/src/platform/registry/__tests__/resource-registry-static.test.ts +347 -347
  407. package/src/platform/registry/__tests__/resource-registry.integration.test.ts +1028 -1028
  408. package/src/platform/registry/__tests__/resource-registry.list-executable.test.ts +393 -393
  409. package/src/platform/registry/__tests__/resource-registry.test.ts +2005 -2005
  410. package/src/platform/registry/__tests__/serialization.test.ts +1127 -1127
  411. package/src/platform/registry/command-view.ts +180 -180
  412. package/src/platform/registry/domains.ts +165 -165
  413. package/src/platform/registry/index.ts +93 -93
  414. package/src/platform/registry/reserved.ts +24 -24
  415. package/src/platform/registry/resource-metadata.ts +59 -59
  416. package/src/platform/registry/resource-registry.command-queue-groups.test.ts +129 -129
  417. package/src/platform/registry/resource-registry.ts +876 -876
  418. package/src/platform/registry/serialization.ts +273 -273
  419. package/src/platform/registry/serialized-types.ts +231 -231
  420. package/src/platform/registry/stats-types.ts +66 -66
  421. package/src/platform/registry/types.ts +404 -404
  422. package/src/platform/registry/validation.ts +513 -513
  423. package/src/platform/resilience/__tests__/rate-limiter.test.ts +471 -471
  424. package/src/platform/resilience/circuit-breaker.ts +164 -164
  425. package/src/platform/resilience/errors.ts +68 -68
  426. package/src/platform/resilience/http-error-mapper.ts +129 -129
  427. package/src/platform/resilience/index.ts +93 -93
  428. package/src/platform/resilience/rate-limiter-types.ts +46 -46
  429. package/src/platform/resilience/rate-limiter.ts +140 -140
  430. package/src/platform/resilience/retry.ts +89 -89
  431. package/src/platform/resilience/timeout.ts +63 -63
  432. package/src/platform/sse/events.ts +37 -37
  433. package/src/platform/sse/index.ts +7 -7
  434. package/src/platform/utils/__tests__/validation.test.ts +1083 -1083
  435. package/src/platform/utils/currency.ts +96 -96
  436. package/src/platform/utils/debounce.ts +52 -52
  437. package/src/platform/utils/error.ts +41 -41
  438. package/src/platform/utils/hmac.test.ts +97 -97
  439. package/src/platform/utils/index.ts +32 -32
  440. package/src/platform/utils/server/betterstack-logger.ts +210 -210
  441. package/src/platform/utils/server/hmac.ts +44 -44
  442. package/src/platform/utils/server/unsubscribe.ts +111 -111
  443. package/src/platform/utils/token-counter.ts +96 -96
  444. package/src/platform/utils/validation.ts +425 -425
  445. package/src/projects/api-schemas.ts +268 -268
  446. package/src/published.ts +1 -1
  447. package/src/reference/_generated/contracts.md +607 -607
  448. package/src/reference/glossary.md +105 -105
  449. package/src/requests/__tests__/api-schemas.test.ts +277 -277
  450. package/src/requests/api-schemas.ts +83 -83
  451. package/src/requests/index.ts +1 -1
  452. package/src/scaffold-registry/__tests__/index.test.ts +17 -0
  453. package/src/scaffold-registry/__tests__/schema.test.ts +329 -230
  454. package/src/scaffold-registry/index.ts +205 -189
  455. package/src/scaffold-registry/schema.ts +196 -128
  456. package/src/server.ts +272 -272
  457. package/src/supabase/database.types.ts +2719 -2719
  458. package/src/supabase/helpers.ts +20 -20
  459. package/src/supabase/index.ts +52 -52
  460. package/src/supabase/server/client.ts +58 -58
  461. package/src/test-utils/README.md +38 -38
  462. package/src/test-utils/browser-mocks.ts +54 -54
  463. package/src/test-utils/fixtures/api-keys.ts +52 -52
  464. package/src/test-utils/fixtures/index.ts +4 -4
  465. package/src/test-utils/fixtures/memberships.ts +80 -80
  466. package/src/test-utils/fixtures/organizations.ts +69 -69
  467. package/src/test-utils/fixtures/users.ts +79 -79
  468. package/src/test-utils/index.ts +11 -11
  469. package/src/test-utils/mocks/index.ts +2 -2
  470. package/src/test-utils/mocks/supabase.ts +142 -142
  471. package/src/test-utils/mocks/workos.ts +108 -108
  472. package/src/test-utils/rls/RLSTestContext.ts +556 -556
  473. package/src/test-utils/rls/index.ts +1 -1
@@ -1,876 +1,876 @@
1
- /**
2
- * ResourceRegistry - Resource discovery and lookup
3
- * Handles resource definitions from OrganizationRegistry
4
- *
5
- * Features:
6
- * - Resource discovery by organization
7
- * - Startup validation (duplicate IDs, model configs, relationships, interface-schema alignment)
8
- * - Pre-serialization cache for instant API responses
9
- * - Command View data generation
10
- */
11
-
12
- import type { WorkflowDefinition } from '../../execution/engine/workflow/types'
13
- import type { AgentDefinition } from '../../execution/engine/agent/core/types'
14
- import type {
15
- ResourceStatus,
16
- ResourceDefinition,
17
- ResourceList,
18
- TriggerDefinition,
19
- IntegrationDefinition,
20
- ResourceRelationships,
21
- ExternalResourceDefinition,
22
- HumanCheckpointDefinition
23
- } from './types'
24
- import type {
25
- SerializedOrganizationData,
26
- SerializedAgentDefinition,
27
- SerializedWorkflowDefinition,
28
- SerializedExecutionInterface,
29
- CommandViewData
30
- } from './serialized-types'
31
- import { validateDeploymentSpec, validateRelationships } from './validation'
32
- import { serializeAllOrganizations, serializeOrganization } from './serialization'
33
- import { isReservedResourceId } from './reserved'
34
-
35
- /** Filter out archived resources from an organization's resource collection */
36
- function filterArchived(org: DeploymentSpec): DeploymentSpec {
37
- return {
38
- ...org,
39
- workflows: org.workflows?.filter((w) => !w.config.archived),
40
- agents: org.agents?.filter((a) => !a.config.archived),
41
- triggers: org.triggers?.filter((t) => !t.archived),
42
- integrations: org.integrations?.filter((i) => !i.archived),
43
- externalResources: org.externalResources?.filter((e) => !e.archived),
44
- humanCheckpoints: org.humanCheckpoints?.filter((h) => !h.archived)
45
- }
46
- }
47
-
48
- /**
49
- * Configuration for a remotely-deployed organization
50
- *
51
- * Stored alongside runtime-registered organizations to support
52
- * worker thread execution branching and credential management.
53
- */
54
- export interface RemoteOrgConfig {
55
- /** Supabase Storage path: "{orgId}/{deploymentId}/bundle.js" */
56
- storagePath: string
57
- /** Deployment record ID */
58
- deploymentId: string
59
- /** OS temp path to bundle -- set after first download, used by worker threads */
60
- cachedTempPath?: string
61
- /** Platform tool name -> credential name mapping */
62
- toolCredentials?: Record<string, string>
63
- /** SDK version used to deploy this bundle */
64
- sdkVersion?: string
65
- /** Deployment version (semver) of the deployed bundle */
66
- deploymentVersion?: string
67
- }
68
-
69
- /**
70
- * Organization-specific resource collection
71
- *
72
- * Complete manifest of all automation resources for an organization.
73
- * Used by ResourceRegistry for discovery and Command View for visualization.
74
- */
75
- export interface DeploymentSpec {
76
- /** Deployment version (semver) */
77
- version: string
78
- /** Workflow definitions */
79
- workflows?: WorkflowDefinition[]
80
- /** Agent definitions */
81
- agents?: AgentDefinition[]
82
-
83
- // Resource Manifest fields (optional for backwards compatibility)
84
- /** Trigger definitions - entry points that initiate executions */
85
- triggers?: TriggerDefinition[]
86
- /** Integration definitions - external service connections */
87
- integrations?: IntegrationDefinition[]
88
- /** Explicit relationship declarations between resources */
89
- relationships?: ResourceRelationships
90
- /** External automation resources (n8n, Make, Zapier, etc.) */
91
- externalResources?: ExternalResourceDefinition[]
92
- /** Human checkpoint definitions - human decision points in automation */
93
- humanCheckpoints?: HumanCheckpointDefinition[]
94
- }
95
-
96
- /**
97
- * Organization Registry type
98
- */
99
- export type OrganizationRegistry = Record<string, DeploymentSpec>
100
-
101
- export class ResourceRegistry {
102
- /**
103
- * Pre-serialized organization data cache
104
- * Computed once at construction for static orgs, updated incrementally for runtime orgs
105
- */
106
- private serializedCache: Map<string, SerializedOrganizationData>
107
-
108
- /**
109
- * Per-resource remote configuration (external deployments)
110
- * Key: "orgName/resourceId", Value: RemoteOrgConfig for that resource.
111
- * Tracks which individual resources were added at runtime via deploy pipeline.
112
- * Static and remote resources coexist in the same org.
113
- */
114
- private remoteResources = new Map<string, RemoteOrgConfig>()
115
-
116
- constructor(private registry: OrganizationRegistry) {
117
- this.validateRegistry()
118
- this.validateRelationships()
119
- this.serializedCache = serializeAllOrganizations(registry)
120
- }
121
-
122
- /**
123
- * Validates registry on construction
124
- * - Checks for duplicate resourceIds within organizations
125
- * - Validates model configurations against constraints
126
- * - Validates ExecutionInterface matches inputSchema
127
- * @throws Error if validation fails
128
- */
129
- private validateRegistry(): void {
130
- for (const [orgName, resources] of Object.entries(this.registry)) {
131
- validateDeploymentSpec(orgName, resources)
132
- }
133
- }
134
-
135
- /**
136
- * Validates relationship declarations reference valid resources
137
- * Runs at API server startup - fails fast in development
138
- * @throws Error if validation fails
139
- */
140
- private validateRelationships(): void {
141
- for (const [orgName, resources] of Object.entries(this.registry)) {
142
- validateRelationships(orgName, resources)
143
- }
144
- }
145
-
146
- /**
147
- * Get the remote resource IDs currently registered for an organization.
148
- * Used to validate redeployments against the post-swap state before any
149
- * live registry mutation occurs.
150
- */
151
- private getRemoteResourceIds(orgName: string): Set<string> {
152
- const prefix = `${orgName}/`
153
- const remoteIds = new Set<string>()
154
- for (const key of this.remoteResources.keys()) {
155
- if (key.startsWith(prefix)) {
156
- remoteIds.add(key.slice(prefix.length))
157
- }
158
- }
159
- return remoteIds
160
- }
161
-
162
- /**
163
- * Build the "static + surviving" baseline for registration validation.
164
- * On redeploy, this strips the currently remote-owned resources and
165
- * deployment-owned metadata so validation reflects the state after swap.
166
- */
167
- private buildRegistrationBase(orgName: string): DeploymentSpec | undefined {
168
- const existingOrg = this.registry[orgName]
169
- if (!existingOrg) return undefined
170
-
171
- const remoteIds = this.getRemoteResourceIds(orgName)
172
- if (remoteIds.size === 0) return existingOrg
173
-
174
- const relationships = existingOrg.relationships
175
- ? Object.fromEntries(Object.entries(existingOrg.relationships).filter(([resourceId]) => !remoteIds.has(resourceId)))
176
- : undefined
177
-
178
- return {
179
- ...existingOrg,
180
- version: existingOrg.version ?? '0.0.0',
181
- workflows: (existingOrg.workflows ?? []).filter((w) => !remoteIds.has(w.config.resourceId)),
182
- agents: (existingOrg.agents ?? []).filter((a) => !remoteIds.has(a.config.resourceId)),
183
- triggers: undefined,
184
- integrations: undefined,
185
- humanCheckpoints: undefined,
186
- externalResources: undefined,
187
- relationships: relationships && Object.keys(relationships).length > 0 ? relationships : undefined
188
- }
189
- }
190
-
191
- /**
192
- * Validate the registry state that would exist after registration succeeds.
193
- * This runs before any live mutation so invalid redeploys preserve the
194
- * currently active remote resources.
195
- */
196
- private validateRegistrationCandidate(orgName: string, incoming: DeploymentSpec): void {
197
- const base = this.buildRegistrationBase(orgName)
198
-
199
- const candidate: DeploymentSpec = base
200
- ? {
201
- ...base,
202
- version: incoming.version ?? base.version ?? '0.0.0',
203
- workflows: [...(base.workflows ?? []), ...(incoming.workflows ?? [])],
204
- agents: [...(base.agents ?? []), ...(incoming.agents ?? [])],
205
- triggers: incoming.triggers,
206
- integrations: incoming.integrations,
207
- humanCheckpoints: incoming.humanCheckpoints,
208
- externalResources: incoming.externalResources,
209
- relationships: incoming.relationships
210
- ? {
211
- ...(base.relationships ?? {}),
212
- ...incoming.relationships
213
- }
214
- : base.relationships
215
- }
216
- : {
217
- ...incoming,
218
- version: incoming.version ?? '0.0.0'
219
- }
220
-
221
- validateDeploymentSpec(orgName, candidate)
222
- validateRelationships(orgName, candidate)
223
- }
224
-
225
- /**
226
- * Get a resource definition by ID
227
- * Returns full definition (WorkflowDefinition or AgentDefinition)
228
- * Check definition.config.type to determine if it's a workflow or agent
229
- */
230
- getResourceDefinition(organizationName: string, resourceId: string): WorkflowDefinition | AgentDefinition | null {
231
- const orgResources = this.registry[organizationName]
232
- if (!orgResources) return null
233
-
234
- // Check workflows first
235
- const workflow = orgResources.workflows?.find((w) => w.config.resourceId === resourceId)
236
- if (workflow) return workflow
237
-
238
- // Check agents
239
- const agent = orgResources.agents?.find((a) => a.config.resourceId === resourceId)
240
- if (agent) return agent
241
-
242
- return null
243
- }
244
-
245
- /**
246
- * List all resources for an organization
247
- * Returns ResourceDefinition metadata (not full definitions)
248
- *
249
- * All resources are returned regardless of server environment.
250
- * Pass an explicit `environment` filter to get only 'dev' or 'prod' resources.
251
- */
252
- listResourcesForOrganization(organizationName: string, environment?: ResourceStatus): ResourceList {
253
- const orgResources = this.registry[organizationName]
254
- if (!orgResources) {
255
- return {
256
- workflows: [],
257
- agents: [],
258
- total: 0,
259
- organizationName,
260
- environment
261
- }
262
- }
263
-
264
- // Map workflows to ResourceDefinition metadata and filter by environment
265
- const workflows: ResourceDefinition[] = (orgResources.workflows || [])
266
- .map((def) => ({
267
- resourceId: def.config.resourceId,
268
- name: def.config.name,
269
- description: def.config.description,
270
- version: def.config.version,
271
- type: def.config.type,
272
- status: def.config.status,
273
- domains: def.config.domains,
274
- origin: this.remoteResources.has(`${organizationName}/${def.config.resourceId}`)
275
- ? ('remote' as const)
276
- : ('local' as const)
277
- }))
278
- .filter((resource) => !environment || resource.status === environment)
279
-
280
- // Map agents to ResourceDefinition metadata and filter by environment
281
- const agents: ResourceDefinition[] = (orgResources.agents || [])
282
- .map((def) => ({
283
- resourceId: def.config.resourceId,
284
- name: def.config.name,
285
- description: def.config.description,
286
- version: def.config.version,
287
- type: def.config.type,
288
- status: def.config.status,
289
- domains: def.config.domains,
290
- sessionCapable: def.config.sessionCapable ?? false,
291
- origin: this.remoteResources.has(`${organizationName}/${def.config.resourceId}`)
292
- ? ('remote' as const)
293
- : ('local' as const)
294
- }))
295
- .filter((resource) => !environment || resource.status === environment)
296
-
297
- return {
298
- workflows,
299
- agents,
300
- total: workflows.length + agents.length,
301
- organizationName,
302
- environment
303
- }
304
- }
305
-
306
- /**
307
- * List all resources from all organizations
308
- * NOTE: For debugging only - returns raw registry data
309
- */
310
- listAllResources(): OrganizationRegistry {
311
- return this.registry
312
- }
313
-
314
- // ============================================================================
315
- // Runtime Organization Registration (External Deployments)
316
- // ============================================================================
317
-
318
- /**
319
- * Register external resources at runtime
320
- *
321
- * Called during deploy pipeline when an external developer deploys their bundle.
322
- * Merges the incoming stub definitions into the org's registry and stores
323
- * per-resource remote config for worker thread execution branching.
324
- *
325
- * Static and remote resources coexist in the same org. If the org already
326
- * has static resources, the incoming remote resources are merged alongside them.
327
- * If redeploying (some resources already registered as remote for this org),
328
- * the previous remote resources are unregistered first.
329
- *
330
- * @param orgName - Organization name (used as registry key)
331
- * @param org - Stub resource definitions (workflows/agents with placeholder handlers)
332
- * @param remote - Remote configuration (bundle path, deployment ID, env vars)
333
- * @throws Error if incoming resourceId conflicts with a static resource
334
- * @throws Error if incoming deployment contains duplicate resourceIds
335
- */
336
- registerOrganization(orgName: string, org: DeploymentSpec, remote: RemoteOrgConfig): void {
337
- // Filter out archived resources before any processing
338
- org = filterArchived(org)
339
-
340
- // Collect all incoming resource IDs for conflict checking
341
- const incomingWorkflowIds = (org.workflows ?? []).map((w) => w.config.resourceId)
342
- const incomingAgentIds = (org.agents ?? []).map((a) => a.config.resourceId)
343
- const incomingIds = [...incomingWorkflowIds, ...incomingAgentIds]
344
-
345
- // Check for intra-deployment duplicates
346
- const seen = new Set<string>()
347
- for (const id of incomingIds) {
348
- if (seen.has(id)) {
349
- throw new Error(`Duplicate resource ID '${id}' in deployment. Each resource must have a unique ID.`)
350
- }
351
- seen.add(id)
352
- }
353
-
354
- // Check for reserved resource IDs (cannot be claimed by external deployments)
355
- for (const id of incomingIds) {
356
- if (isReservedResourceId(id)) {
357
- throw new Error(
358
- `Resource ID '${id}' is reserved for platform use. External deployments cannot use reserved resource IDs.`
359
- )
360
- }
361
- }
362
-
363
- // Check for conflicts against the static/surviving org state.
364
- // On redeploy, current remote resources are excluded so a deployment can
365
- // legitimately replace its own resource IDs without colliding with itself.
366
- const validationBase = this.buildRegistrationBase(orgName)
367
- if (validationBase) {
368
- const staticWorkflowIds = new Set((validationBase.workflows ?? []).map((w) => w.config.resourceId))
369
- const staticAgentIds = new Set((validationBase.agents ?? []).map((a) => a.config.resourceId))
370
-
371
- for (const id of incomingIds) {
372
- if (staticWorkflowIds.has(id) || staticAgentIds.has(id)) {
373
- throw new Error(
374
- `Resource '${id}' already exists in '${orgName}' as an internal resource. External deployments cannot override internal resources.`
375
- )
376
- }
377
- }
378
- }
379
-
380
- // Validate the merged deployment shape before mutating the live registry.
381
- // This keeps the current deployment intact if a redeploy introduces
382
- // invalid relationships or other registry-level validation failures.
383
- this.validateRegistrationCandidate(orgName, org)
384
-
385
- // If redeploying (some resources already registered as remote for this org), clean up only
386
- // after validation succeeds so a failed redeploy preserves the current remote resources.
387
- if (this.isRemote(orgName)) {
388
- this.unregisterOrganization(orgName)
389
- }
390
-
391
- // Merge incoming resources into the org (or create new org entry)
392
- const existingOrg = this.registry[orgName]
393
- if (existingOrg) {
394
- existingOrg.workflows = [...(existingOrg.workflows ?? []), ...(org.workflows ?? [])]
395
- existingOrg.agents = [...(existingOrg.agents ?? []), ...(org.agents ?? [])]
396
- // Deployment-owned metadata: replace entirely (unregister cleared these)
397
- existingOrg.triggers = org.triggers
398
- existingOrg.integrations = org.integrations
399
- existingOrg.humanCheckpoints = org.humanCheckpoints
400
- existingOrg.externalResources = org.externalResources
401
- if (org.relationships) {
402
- existingOrg.relationships = {
403
- ...(existingOrg.relationships ?? {}),
404
- ...org.relationships
405
- }
406
- }
407
- } else {
408
- this.registry[orgName] = org
409
- }
410
-
411
- // Populate per-resource remote config entries
412
- for (const id of incomingIds) {
413
- this.remoteResources.set(`${orgName}/${id}`, remote)
414
- }
415
-
416
- // Rebuild serialized cache for the full merged org
417
- this.serializedCache.set(orgName, serializeOrganization(this.registry[orgName]))
418
- }
419
-
420
- /**
421
- * Patch serialized cache with pre-serialized schemas from an external manifest.
422
- *
423
- * External deployments use stub definitions with z.any() schemas (never called).
424
- * The manifest carries the real schemas as pre-serialized JSON Schema from the worker.
425
- * This method patches those into the serialized cache so describe/CLI display them.
426
- *
427
- * @param orgName - Organization name
428
- * @param manifestSchemas - Map of resourceId -> { contract, steps } with JSON Schema
429
- */
430
- patchManifestSchemas(
431
- orgName: string,
432
- manifestSchemas: Array<{
433
- resourceId: string
434
- type: 'workflow' | 'agent'
435
- contract?: { inputSchema?: object; outputSchema?: object }
436
- steps?: Array<{ id: string; inputSchema?: object; outputSchema?: object }>
437
- }>
438
- ): void {
439
- const cache = this.serializedCache.get(orgName)
440
- if (!cache) return
441
-
442
- for (const entry of manifestSchemas) {
443
- if (entry.type === 'workflow') {
444
- const def = cache.definitions.workflows.get(entry.resourceId)
445
- if (!def) continue
446
-
447
- // Patch contract schemas
448
- if (entry.contract?.inputSchema) def.contract.inputSchema = entry.contract.inputSchema
449
- if (entry.contract?.outputSchema) def.contract.outputSchema = entry.contract.outputSchema
450
-
451
- // Patch step schemas
452
- if (entry.steps && def.steps) {
453
- for (const stepPatch of entry.steps) {
454
- const step = def.steps.find((s) => s.id === stepPatch.id)
455
- if (!step) continue
456
- if (stepPatch.inputSchema) step.inputSchema = stepPatch.inputSchema
457
- if (stepPatch.outputSchema) step.outputSchema = stepPatch.outputSchema
458
- }
459
- }
460
- } else if (entry.type === 'agent') {
461
- const def = cache.definitions.agents.get(entry.resourceId)
462
- if (!def) continue
463
- if (entry.contract?.inputSchema) def.contract.inputSchema = entry.contract.inputSchema
464
- if (entry.contract?.outputSchema) def.contract.outputSchema = entry.contract.outputSchema
465
- }
466
- }
467
- }
468
-
469
- /**
470
- * Register built-in platform resources (static, local execution)
471
- *
472
- * Unlike registerOrganization(), these resources:
473
- * - Do NOT have remote config (execute in-process, not in worker threads)
474
- * - Are NOT removed by unregisterOrganization() (persist across redeployments)
475
- * - Use reserved resource IDs that external deployments cannot claim
476
- *
477
- * @param orgName - Organization name
478
- * @param org - Resource definitions with real handlers (not stubs)
479
- */
480
- registerStaticResources(orgName: string, org: DeploymentSpec): void {
481
- // Filter out archived resources before any processing
482
- org = filterArchived(org)
483
-
484
- const incomingWorkflowIds = (org.workflows ?? []).map((w) => w.config.resourceId)
485
- const incomingAgentIds = (org.agents ?? []).map((a) => a.config.resourceId)
486
- const incomingIds = [...incomingWorkflowIds, ...incomingAgentIds]
487
-
488
- // Check for duplicates within incoming resources
489
- const seen = new Set<string>()
490
- for (const id of incomingIds) {
491
- if (seen.has(id)) {
492
- throw new Error(`Duplicate resource ID '${id}' in static resources.`)
493
- }
494
- seen.add(id)
495
- }
496
-
497
- // Check for conflicts with existing resources in this org
498
- const existingOrg = this.registry[orgName]
499
- if (existingOrg) {
500
- const existingWorkflowIds = new Set((existingOrg.workflows ?? []).map((w) => w.config.resourceId))
501
- const existingAgentIds = new Set((existingOrg.agents ?? []).map((a) => a.config.resourceId))
502
-
503
- for (const id of incomingIds) {
504
- if (existingWorkflowIds.has(id) || existingAgentIds.has(id)) {
505
- throw new Error(`Static resource '${id}' conflicts with existing resource in '${orgName}'.`)
506
- }
507
- }
508
- }
509
-
510
- // Merge into registry (no remote config = local execution path)
511
- if (existingOrg) {
512
- existingOrg.workflows = [...(existingOrg.workflows ?? []), ...(org.workflows ?? [])]
513
- existingOrg.agents = [...(existingOrg.agents ?? []), ...(org.agents ?? [])]
514
- } else {
515
- this.registry[orgName] = org
516
- }
517
-
518
- // Rebuild serialized cache
519
- this.serializedCache.set(orgName, serializeOrganization(this.registry[orgName]))
520
- }
521
-
522
- /**
523
- * Unregister runtime-registered resources for an organization
524
- *
525
- * Removes only resources that were registered at runtime (via registerOrganization).
526
- * Static resources loaded at startup are preserved. If the org still has static
527
- * resources after removal, the serialization cache is rebuilt. If no resources
528
- * remain, the org is fully removed from the registry.
529
- * No-op if the org has no remote resources.
530
- *
531
- * @param orgName - Organization name to unregister remote resources from
532
- */
533
- unregisterOrganization(orgName: string): void {
534
- // Find all remote resource keys for this org
535
- const prefix = `${orgName}/`
536
- const remoteIds = new Set<string>()
537
- for (const key of this.remoteResources.keys()) {
538
- if (key.startsWith(prefix)) {
539
- remoteIds.add(key.slice(prefix.length))
540
- this.remoteResources.delete(key)
541
- }
542
- }
543
-
544
- // No-op if org had no remote resources
545
- if (remoteIds.size === 0) return
546
-
547
- const orgResources = this.registry[orgName]
548
- if (!orgResources) return
549
-
550
- // Remove remote resources from the org's arrays
551
- orgResources.workflows = (orgResources.workflows ?? []).filter((w) => !remoteIds.has(w.config.resourceId))
552
- orgResources.agents = (orgResources.agents ?? []).filter((a) => !remoteIds.has(a.config.resourceId))
553
-
554
- // Remove deployment-owned metadata (triggers, integrations, checkpoints, external resources)
555
- // These are always fully owned by the deployment — replaced on each deploy
556
- orgResources.triggers = undefined
557
- orgResources.integrations = undefined
558
- orgResources.humanCheckpoints = undefined
559
- orgResources.externalResources = undefined
560
-
561
- // Remove relationship entries for remote resource IDs
562
- if (orgResources.relationships) {
563
- for (const id of remoteIds) {
564
- delete orgResources.relationships[id]
565
- }
566
- if (Object.keys(orgResources.relationships).length === 0) {
567
- delete orgResources.relationships
568
- }
569
- }
570
-
571
- // If the org still has static resources, rebuild cache; otherwise fully remove
572
- // (triggers, integrations, checkpoints, externalResources were cleared above)
573
- const remaining = (orgResources.workflows?.length ?? 0) + (orgResources.agents?.length ?? 0)
574
-
575
- if (remaining > 0) {
576
- this.serializedCache.set(orgName, serializeOrganization(orgResources))
577
- } else {
578
- delete this.registry[orgName]
579
- this.serializedCache.delete(orgName)
580
- }
581
- }
582
-
583
- /**
584
- * Get remote configuration for a specific resource
585
- *
586
- * Returns the RemoteOrgConfig if the resource was registered at runtime,
587
- * or null if it's a static resource or doesn't exist.
588
- * Used by the execution coordinator to branch between local and worker execution.
589
- *
590
- * @param orgName - Organization name
591
- * @param resourceId - Resource ID
592
- * @returns Remote config or null
593
- */
594
- getRemoteConfig(orgName: string, resourceId: string): RemoteOrgConfig | null {
595
- return this.remoteResources.get(`${orgName}/${resourceId}`) ?? null
596
- }
597
-
598
- /**
599
- * Check if an organization has any remote (externally deployed) resources
600
- *
601
- * @param orgName - Organization name
602
- * @returns true if the org has at least one runtime-registered resource
603
- */
604
- isRemote(orgName: string): boolean {
605
- const prefix = `${orgName}/`
606
- for (const key of this.remoteResources.keys()) {
607
- if (key.startsWith(prefix)) return true
608
- }
609
- return false
610
- }
611
-
612
- /**
613
- * Get the remote config for any resource in an organization.
614
- * Used when the specific resource ID is unknown (e.g., to clean up a
615
- * temp file before unregistering an org -- all resources share one config).
616
- *
617
- * @param orgName - Organization name
618
- * @returns Remote config or null if org has no remote resources
619
- */
620
- getAnyRemoteConfig(orgName: string): RemoteOrgConfig | null {
621
- const prefix = `${orgName}/`
622
- for (const [key, config] of this.remoteResources) {
623
- if (key.startsWith(prefix)) return config
624
- }
625
- return null
626
- }
627
-
628
- /**
629
- * Get statistics about remotely-deployed resources
630
- * Used by the health endpoint for platform-wide deployment visibility.
631
- */
632
- getRemoteStats(): { activeOrgs: number; totalResources: number } {
633
- const orgs = new Set<string>()
634
- for (const key of this.remoteResources.keys()) {
635
- const orgName = key.split('/')[0]
636
- orgs.add(orgName)
637
- }
638
- return {
639
- activeOrgs: orgs.size,
640
- totalResources: this.remoteResources.size
641
- }
642
- }
643
-
644
- // ============================================================================
645
- // Resource Manifest Accessors
646
- // ============================================================================
647
-
648
- /**
649
- * Get triggers for an organization
650
- * @param organizationName - Organization name
651
- * @returns Array of trigger definitions (empty if none defined)
652
- */
653
- getTriggers(organizationName: string): TriggerDefinition[] {
654
- return this.registry[organizationName]?.triggers ?? []
655
- }
656
-
657
- /**
658
- * Get integrations for an organization
659
- * @param organizationName - Organization name
660
- * @returns Array of integration definitions (empty if none defined)
661
- */
662
- getIntegrations(organizationName: string): IntegrationDefinition[] {
663
- return this.registry[organizationName]?.integrations ?? []
664
- }
665
-
666
- /**
667
- * Get resource relationships for an organization
668
- * @param organizationName - Organization name
669
- * @returns Resource relationships map (undefined if none defined)
670
- */
671
- getRelationships(organizationName: string): ResourceRelationships | undefined {
672
- return this.registry[organizationName]?.relationships
673
- }
674
-
675
- /**
676
- * Get a specific trigger by ID
677
- * @param organizationName - Organization name
678
- * @param triggerId - Trigger ID
679
- * @returns Trigger definition or null if not found
680
- */
681
- getTrigger(organizationName: string, triggerId: string): TriggerDefinition | null {
682
- const triggers = this.getTriggers(organizationName)
683
- return triggers.find((t) => t.resourceId === triggerId) ?? null
684
- }
685
-
686
- /**
687
- * Get a specific integration by ID
688
- * @param organizationName - Organization name
689
- * @param integrationId - Integration ID
690
- * @returns Integration definition or null if not found
691
- */
692
- getIntegration(organizationName: string, integrationId: string): IntegrationDefinition | null {
693
- const integrations = this.getIntegrations(organizationName)
694
- return integrations.find((i) => i.resourceId === integrationId) ?? null
695
- }
696
-
697
- /**
698
- * Get external resources for an organization
699
- * @param organizationName - Organization name
700
- * @returns Array of external resource definitions (empty if none defined)
701
- */
702
- getExternalResources(organizationName: string): ExternalResourceDefinition[] {
703
- return this.registry[organizationName]?.externalResources ?? []
704
- }
705
-
706
- /**
707
- * Get a specific external resource by ID
708
- * @param organizationName - Organization name
709
- * @param externalId - External resource ID
710
- * @returns External resource definition or null if not found
711
- */
712
- getExternalResource(organizationName: string, externalId: string): ExternalResourceDefinition | null {
713
- const externalResources = this.getExternalResources(organizationName)
714
- return externalResources.find((e) => e.resourceId === externalId) ?? null
715
- }
716
-
717
- /**
718
- * Get human checkpoints for an organization
719
- * @param organizationName - Organization name
720
- * @returns Array of human checkpoint definitions (empty if none defined)
721
- */
722
- getHumanCheckpoints(organizationName: string): HumanCheckpointDefinition[] {
723
- return this.registry[organizationName]?.humanCheckpoints ?? []
724
- }
725
-
726
- /**
727
- * Get a specific human checkpoint by ID
728
- * @param organizationName - Organization name
729
- * @param humanCheckpointId - Human checkpoint ID
730
- * @returns Human checkpoint definition or null if not found
731
- */
732
- getHumanCheckpoint(organizationName: string, humanCheckpointId: string): HumanCheckpointDefinition | null {
733
- const humanCheckpoints = this.getHumanCheckpoints(organizationName)
734
- return humanCheckpoints.find((hc) => hc.resourceId === humanCheckpointId) ?? null
735
- }
736
-
737
- // ============================================================================
738
- // Serialized Data Access (Pre-computed at Startup)
739
- // ============================================================================
740
-
741
- /**
742
- * Get serialized resource definition (instant lookup)
743
- * Use for API responses - returns pre-computed JSON-safe structure
744
- *
745
- * @param organizationName - Organization name
746
- * @param resourceId - Resource ID
747
- * @returns Serialized definition or null if not found
748
- */
749
- getSerializedDefinition(
750
- organizationName: string,
751
- resourceId: string
752
- ): SerializedAgentDefinition | SerializedWorkflowDefinition | null {
753
- const cache = this.serializedCache.get(organizationName)
754
- if (!cache) return null
755
-
756
- return cache.definitions.agents.get(resourceId) ?? cache.definitions.workflows.get(resourceId) ?? null
757
- }
758
-
759
- /**
760
- * Get resource list for organization (instant lookup)
761
- * Use for /resources endpoint - returns pre-computed ResourceDefinition array
762
- *
763
- * @param organizationName - Organization name
764
- * @returns Resource list with workflows, agents, and total count
765
- */
766
- getResourceList(organizationName: string): {
767
- workflows: ResourceDefinition[]
768
- agents: ResourceDefinition[]
769
- total: number
770
- } {
771
- const cache = this.serializedCache.get(organizationName)
772
- if (!cache) {
773
- return { workflows: [], agents: [], total: 0 }
774
- }
775
- return cache.resources
776
- }
777
-
778
- /**
779
- * Get Command View data for organization (instant lookup)
780
- * Use for /command-view endpoint - returns complete graph data
781
- *
782
- * @param organizationName - Organization name
783
- * @returns Command View data with nodes and edges
784
- */
785
- getCommandViewData(organizationName: string): CommandViewData {
786
- const cache = this.serializedCache.get(organizationName)
787
- if (!cache) {
788
- return {
789
- workflows: [],
790
- agents: [],
791
- triggers: [],
792
- integrations: [],
793
- externalResources: [],
794
- humanCheckpoints: [],
795
- edges: []
796
- }
797
- }
798
- return cache.commandView
799
- }
800
-
801
- /**
802
- * List resources that have UI interfaces configured
803
- * Used by Execution Runner Catalog UI
804
- *
805
- * @param organizationName - Organization name
806
- * @param environment - Optional environment filter ('dev' or 'prod')
807
- * @returns Array of resources with interfaces
808
- */
809
- listExecutable(
810
- organizationName: string,
811
- environment?: 'dev' | 'prod'
812
- ): Array<{
813
- resourceId: string
814
- resourceName: string
815
- resourceType: 'workflow' | 'agent'
816
- description?: string
817
- status: 'dev' | 'prod'
818
- version: string
819
- interface: SerializedExecutionInterface
820
- }> {
821
- const cache = this.serializedCache.get(organizationName)
822
- if (!cache) {
823
- return []
824
- }
825
-
826
- const results: Array<{
827
- resourceId: string
828
- resourceName: string
829
- resourceType: 'workflow' | 'agent'
830
- description?: string
831
- status: 'dev' | 'prod'
832
- version: string
833
- interface: SerializedExecutionInterface
834
- }> = []
835
-
836
- // Check workflows with interfaces
837
- cache.definitions.workflows.forEach((serializedWorkflow, resourceId) => {
838
- // Skip if no interface defined
839
- if (!serializedWorkflow.interface) return
840
-
841
- // Apply environment filter if specified
842
- if (environment && serializedWorkflow.config.status !== environment) return
843
-
844
- results.push({
845
- resourceId,
846
- resourceName: serializedWorkflow.config.name,
847
- resourceType: 'workflow',
848
- description: serializedWorkflow.config.description,
849
- status: serializedWorkflow.config.status as 'dev' | 'prod',
850
- version: serializedWorkflow.config.version,
851
- interface: serializedWorkflow.interface
852
- })
853
- })
854
-
855
- // Check agents with interfaces
856
- cache.definitions.agents.forEach((serializedAgent, resourceId) => {
857
- // Skip if no interface defined
858
- if (!serializedAgent.interface) return
859
-
860
- // Apply environment filter if specified
861
- if (environment && serializedAgent.config.status !== environment) return
862
-
863
- results.push({
864
- resourceId,
865
- resourceName: serializedAgent.config.name,
866
- resourceType: 'agent',
867
- description: serializedAgent.config.description,
868
- status: serializedAgent.config.status as 'dev' | 'prod',
869
- version: serializedAgent.config.version,
870
- interface: serializedAgent.interface
871
- })
872
- })
873
-
874
- return results
875
- }
876
- }
1
+ /**
2
+ * ResourceRegistry - Resource discovery and lookup
3
+ * Handles resource definitions from OrganizationRegistry
4
+ *
5
+ * Features:
6
+ * - Resource discovery by organization
7
+ * - Startup validation (duplicate IDs, model configs, relationships, interface-schema alignment)
8
+ * - Pre-serialization cache for instant API responses
9
+ * - Command View data generation
10
+ */
11
+
12
+ import type { WorkflowDefinition } from '../../execution/engine/workflow/types'
13
+ import type { AgentDefinition } from '../../execution/engine/agent/core/types'
14
+ import type {
15
+ ResourceStatus,
16
+ ResourceDefinition,
17
+ ResourceList,
18
+ TriggerDefinition,
19
+ IntegrationDefinition,
20
+ ResourceRelationships,
21
+ ExternalResourceDefinition,
22
+ HumanCheckpointDefinition
23
+ } from './types'
24
+ import type {
25
+ SerializedOrganizationData,
26
+ SerializedAgentDefinition,
27
+ SerializedWorkflowDefinition,
28
+ SerializedExecutionInterface,
29
+ CommandViewData
30
+ } from './serialized-types'
31
+ import { validateDeploymentSpec, validateRelationships } from './validation'
32
+ import { serializeAllOrganizations, serializeOrganization } from './serialization'
33
+ import { isReservedResourceId } from './reserved'
34
+
35
+ /** Filter out archived resources from an organization's resource collection */
36
+ function filterArchived(org: DeploymentSpec): DeploymentSpec {
37
+ return {
38
+ ...org,
39
+ workflows: org.workflows?.filter((w) => !w.config.archived),
40
+ agents: org.agents?.filter((a) => !a.config.archived),
41
+ triggers: org.triggers?.filter((t) => !t.archived),
42
+ integrations: org.integrations?.filter((i) => !i.archived),
43
+ externalResources: org.externalResources?.filter((e) => !e.archived),
44
+ humanCheckpoints: org.humanCheckpoints?.filter((h) => !h.archived)
45
+ }
46
+ }
47
+
48
+ /**
49
+ * Configuration for a remotely-deployed organization
50
+ *
51
+ * Stored alongside runtime-registered organizations to support
52
+ * worker thread execution branching and credential management.
53
+ */
54
+ export interface RemoteOrgConfig {
55
+ /** Supabase Storage path: "{orgId}/{deploymentId}/bundle.js" */
56
+ storagePath: string
57
+ /** Deployment record ID */
58
+ deploymentId: string
59
+ /** OS temp path to bundle -- set after first download, used by worker threads */
60
+ cachedTempPath?: string
61
+ /** Platform tool name -> credential name mapping */
62
+ toolCredentials?: Record<string, string>
63
+ /** SDK version used to deploy this bundle */
64
+ sdkVersion?: string
65
+ /** Deployment version (semver) of the deployed bundle */
66
+ deploymentVersion?: string
67
+ }
68
+
69
+ /**
70
+ * Organization-specific resource collection
71
+ *
72
+ * Complete manifest of all automation resources for an organization.
73
+ * Used by ResourceRegistry for discovery and Command View for visualization.
74
+ */
75
+ export interface DeploymentSpec {
76
+ /** Deployment version (semver) */
77
+ version: string
78
+ /** Workflow definitions */
79
+ workflows?: WorkflowDefinition[]
80
+ /** Agent definitions */
81
+ agents?: AgentDefinition[]
82
+
83
+ // Resource Manifest fields (optional for backwards compatibility)
84
+ /** Trigger definitions - entry points that initiate executions */
85
+ triggers?: TriggerDefinition[]
86
+ /** Integration definitions - external service connections */
87
+ integrations?: IntegrationDefinition[]
88
+ /** Explicit relationship declarations between resources */
89
+ relationships?: ResourceRelationships
90
+ /** External automation resources (n8n, Make, Zapier, etc.) */
91
+ externalResources?: ExternalResourceDefinition[]
92
+ /** Human checkpoint definitions - human decision points in automation */
93
+ humanCheckpoints?: HumanCheckpointDefinition[]
94
+ }
95
+
96
+ /**
97
+ * Organization Registry type
98
+ */
99
+ export type OrganizationRegistry = Record<string, DeploymentSpec>
100
+
101
+ export class ResourceRegistry {
102
+ /**
103
+ * Pre-serialized organization data cache
104
+ * Computed once at construction for static orgs, updated incrementally for runtime orgs
105
+ */
106
+ private serializedCache: Map<string, SerializedOrganizationData>
107
+
108
+ /**
109
+ * Per-resource remote configuration (external deployments)
110
+ * Key: "orgName/resourceId", Value: RemoteOrgConfig for that resource.
111
+ * Tracks which individual resources were added at runtime via deploy pipeline.
112
+ * Static and remote resources coexist in the same org.
113
+ */
114
+ private remoteResources = new Map<string, RemoteOrgConfig>()
115
+
116
+ constructor(private registry: OrganizationRegistry) {
117
+ this.validateRegistry()
118
+ this.validateRelationships()
119
+ this.serializedCache = serializeAllOrganizations(registry)
120
+ }
121
+
122
+ /**
123
+ * Validates registry on construction
124
+ * - Checks for duplicate resourceIds within organizations
125
+ * - Validates model configurations against constraints
126
+ * - Validates ExecutionInterface matches inputSchema
127
+ * @throws Error if validation fails
128
+ */
129
+ private validateRegistry(): void {
130
+ for (const [orgName, resources] of Object.entries(this.registry)) {
131
+ validateDeploymentSpec(orgName, resources)
132
+ }
133
+ }
134
+
135
+ /**
136
+ * Validates relationship declarations reference valid resources
137
+ * Runs at API server startup - fails fast in development
138
+ * @throws Error if validation fails
139
+ */
140
+ private validateRelationships(): void {
141
+ for (const [orgName, resources] of Object.entries(this.registry)) {
142
+ validateRelationships(orgName, resources)
143
+ }
144
+ }
145
+
146
+ /**
147
+ * Get the remote resource IDs currently registered for an organization.
148
+ * Used to validate redeployments against the post-swap state before any
149
+ * live registry mutation occurs.
150
+ */
151
+ private getRemoteResourceIds(orgName: string): Set<string> {
152
+ const prefix = `${orgName}/`
153
+ const remoteIds = new Set<string>()
154
+ for (const key of this.remoteResources.keys()) {
155
+ if (key.startsWith(prefix)) {
156
+ remoteIds.add(key.slice(prefix.length))
157
+ }
158
+ }
159
+ return remoteIds
160
+ }
161
+
162
+ /**
163
+ * Build the "static + surviving" baseline for registration validation.
164
+ * On redeploy, this strips the currently remote-owned resources and
165
+ * deployment-owned metadata so validation reflects the state after swap.
166
+ */
167
+ private buildRegistrationBase(orgName: string): DeploymentSpec | undefined {
168
+ const existingOrg = this.registry[orgName]
169
+ if (!existingOrg) return undefined
170
+
171
+ const remoteIds = this.getRemoteResourceIds(orgName)
172
+ if (remoteIds.size === 0) return existingOrg
173
+
174
+ const relationships = existingOrg.relationships
175
+ ? Object.fromEntries(Object.entries(existingOrg.relationships).filter(([resourceId]) => !remoteIds.has(resourceId)))
176
+ : undefined
177
+
178
+ return {
179
+ ...existingOrg,
180
+ version: existingOrg.version ?? '0.0.0',
181
+ workflows: (existingOrg.workflows ?? []).filter((w) => !remoteIds.has(w.config.resourceId)),
182
+ agents: (existingOrg.agents ?? []).filter((a) => !remoteIds.has(a.config.resourceId)),
183
+ triggers: undefined,
184
+ integrations: undefined,
185
+ humanCheckpoints: undefined,
186
+ externalResources: undefined,
187
+ relationships: relationships && Object.keys(relationships).length > 0 ? relationships : undefined
188
+ }
189
+ }
190
+
191
+ /**
192
+ * Validate the registry state that would exist after registration succeeds.
193
+ * This runs before any live mutation so invalid redeploys preserve the
194
+ * currently active remote resources.
195
+ */
196
+ private validateRegistrationCandidate(orgName: string, incoming: DeploymentSpec): void {
197
+ const base = this.buildRegistrationBase(orgName)
198
+
199
+ const candidate: DeploymentSpec = base
200
+ ? {
201
+ ...base,
202
+ version: incoming.version ?? base.version ?? '0.0.0',
203
+ workflows: [...(base.workflows ?? []), ...(incoming.workflows ?? [])],
204
+ agents: [...(base.agents ?? []), ...(incoming.agents ?? [])],
205
+ triggers: incoming.triggers,
206
+ integrations: incoming.integrations,
207
+ humanCheckpoints: incoming.humanCheckpoints,
208
+ externalResources: incoming.externalResources,
209
+ relationships: incoming.relationships
210
+ ? {
211
+ ...(base.relationships ?? {}),
212
+ ...incoming.relationships
213
+ }
214
+ : base.relationships
215
+ }
216
+ : {
217
+ ...incoming,
218
+ version: incoming.version ?? '0.0.0'
219
+ }
220
+
221
+ validateDeploymentSpec(orgName, candidate)
222
+ validateRelationships(orgName, candidate)
223
+ }
224
+
225
+ /**
226
+ * Get a resource definition by ID
227
+ * Returns full definition (WorkflowDefinition or AgentDefinition)
228
+ * Check definition.config.type to determine if it's a workflow or agent
229
+ */
230
+ getResourceDefinition(organizationName: string, resourceId: string): WorkflowDefinition | AgentDefinition | null {
231
+ const orgResources = this.registry[organizationName]
232
+ if (!orgResources) return null
233
+
234
+ // Check workflows first
235
+ const workflow = orgResources.workflows?.find((w) => w.config.resourceId === resourceId)
236
+ if (workflow) return workflow
237
+
238
+ // Check agents
239
+ const agent = orgResources.agents?.find((a) => a.config.resourceId === resourceId)
240
+ if (agent) return agent
241
+
242
+ return null
243
+ }
244
+
245
+ /**
246
+ * List all resources for an organization
247
+ * Returns ResourceDefinition metadata (not full definitions)
248
+ *
249
+ * All resources are returned regardless of server environment.
250
+ * Pass an explicit `environment` filter to get only 'dev' or 'prod' resources.
251
+ */
252
+ listResourcesForOrganization(organizationName: string, environment?: ResourceStatus): ResourceList {
253
+ const orgResources = this.registry[organizationName]
254
+ if (!orgResources) {
255
+ return {
256
+ workflows: [],
257
+ agents: [],
258
+ total: 0,
259
+ organizationName,
260
+ environment
261
+ }
262
+ }
263
+
264
+ // Map workflows to ResourceDefinition metadata and filter by environment
265
+ const workflows: ResourceDefinition[] = (orgResources.workflows || [])
266
+ .map((def) => ({
267
+ resourceId: def.config.resourceId,
268
+ name: def.config.name,
269
+ description: def.config.description,
270
+ version: def.config.version,
271
+ type: def.config.type,
272
+ status: def.config.status,
273
+ domains: def.config.domains,
274
+ origin: this.remoteResources.has(`${organizationName}/${def.config.resourceId}`)
275
+ ? ('remote' as const)
276
+ : ('local' as const)
277
+ }))
278
+ .filter((resource) => !environment || resource.status === environment)
279
+
280
+ // Map agents to ResourceDefinition metadata and filter by environment
281
+ const agents: ResourceDefinition[] = (orgResources.agents || [])
282
+ .map((def) => ({
283
+ resourceId: def.config.resourceId,
284
+ name: def.config.name,
285
+ description: def.config.description,
286
+ version: def.config.version,
287
+ type: def.config.type,
288
+ status: def.config.status,
289
+ domains: def.config.domains,
290
+ sessionCapable: def.config.sessionCapable ?? false,
291
+ origin: this.remoteResources.has(`${organizationName}/${def.config.resourceId}`)
292
+ ? ('remote' as const)
293
+ : ('local' as const)
294
+ }))
295
+ .filter((resource) => !environment || resource.status === environment)
296
+
297
+ return {
298
+ workflows,
299
+ agents,
300
+ total: workflows.length + agents.length,
301
+ organizationName,
302
+ environment
303
+ }
304
+ }
305
+
306
+ /**
307
+ * List all resources from all organizations
308
+ * NOTE: For debugging only - returns raw registry data
309
+ */
310
+ listAllResources(): OrganizationRegistry {
311
+ return this.registry
312
+ }
313
+
314
+ // ============================================================================
315
+ // Runtime Organization Registration (External Deployments)
316
+ // ============================================================================
317
+
318
+ /**
319
+ * Register external resources at runtime
320
+ *
321
+ * Called during deploy pipeline when an external developer deploys their bundle.
322
+ * Merges the incoming stub definitions into the org's registry and stores
323
+ * per-resource remote config for worker thread execution branching.
324
+ *
325
+ * Static and remote resources coexist in the same org. If the org already
326
+ * has static resources, the incoming remote resources are merged alongside them.
327
+ * If redeploying (some resources already registered as remote for this org),
328
+ * the previous remote resources are unregistered first.
329
+ *
330
+ * @param orgName - Organization name (used as registry key)
331
+ * @param org - Stub resource definitions (workflows/agents with placeholder handlers)
332
+ * @param remote - Remote configuration (bundle path, deployment ID, env vars)
333
+ * @throws Error if incoming resourceId conflicts with a static resource
334
+ * @throws Error if incoming deployment contains duplicate resourceIds
335
+ */
336
+ registerOrganization(orgName: string, org: DeploymentSpec, remote: RemoteOrgConfig): void {
337
+ // Filter out archived resources before any processing
338
+ org = filterArchived(org)
339
+
340
+ // Collect all incoming resource IDs for conflict checking
341
+ const incomingWorkflowIds = (org.workflows ?? []).map((w) => w.config.resourceId)
342
+ const incomingAgentIds = (org.agents ?? []).map((a) => a.config.resourceId)
343
+ const incomingIds = [...incomingWorkflowIds, ...incomingAgentIds]
344
+
345
+ // Check for intra-deployment duplicates
346
+ const seen = new Set<string>()
347
+ for (const id of incomingIds) {
348
+ if (seen.has(id)) {
349
+ throw new Error(`Duplicate resource ID '${id}' in deployment. Each resource must have a unique ID.`)
350
+ }
351
+ seen.add(id)
352
+ }
353
+
354
+ // Check for reserved resource IDs (cannot be claimed by external deployments)
355
+ for (const id of incomingIds) {
356
+ if (isReservedResourceId(id)) {
357
+ throw new Error(
358
+ `Resource ID '${id}' is reserved for platform use. External deployments cannot use reserved resource IDs.`
359
+ )
360
+ }
361
+ }
362
+
363
+ // Check for conflicts against the static/surviving org state.
364
+ // On redeploy, current remote resources are excluded so a deployment can
365
+ // legitimately replace its own resource IDs without colliding with itself.
366
+ const validationBase = this.buildRegistrationBase(orgName)
367
+ if (validationBase) {
368
+ const staticWorkflowIds = new Set((validationBase.workflows ?? []).map((w) => w.config.resourceId))
369
+ const staticAgentIds = new Set((validationBase.agents ?? []).map((a) => a.config.resourceId))
370
+
371
+ for (const id of incomingIds) {
372
+ if (staticWorkflowIds.has(id) || staticAgentIds.has(id)) {
373
+ throw new Error(
374
+ `Resource '${id}' already exists in '${orgName}' as an internal resource. External deployments cannot override internal resources.`
375
+ )
376
+ }
377
+ }
378
+ }
379
+
380
+ // Validate the merged deployment shape before mutating the live registry.
381
+ // This keeps the current deployment intact if a redeploy introduces
382
+ // invalid relationships or other registry-level validation failures.
383
+ this.validateRegistrationCandidate(orgName, org)
384
+
385
+ // If redeploying (some resources already registered as remote for this org), clean up only
386
+ // after validation succeeds so a failed redeploy preserves the current remote resources.
387
+ if (this.isRemote(orgName)) {
388
+ this.unregisterOrganization(orgName)
389
+ }
390
+
391
+ // Merge incoming resources into the org (or create new org entry)
392
+ const existingOrg = this.registry[orgName]
393
+ if (existingOrg) {
394
+ existingOrg.workflows = [...(existingOrg.workflows ?? []), ...(org.workflows ?? [])]
395
+ existingOrg.agents = [...(existingOrg.agents ?? []), ...(org.agents ?? [])]
396
+ // Deployment-owned metadata: replace entirely (unregister cleared these)
397
+ existingOrg.triggers = org.triggers
398
+ existingOrg.integrations = org.integrations
399
+ existingOrg.humanCheckpoints = org.humanCheckpoints
400
+ existingOrg.externalResources = org.externalResources
401
+ if (org.relationships) {
402
+ existingOrg.relationships = {
403
+ ...(existingOrg.relationships ?? {}),
404
+ ...org.relationships
405
+ }
406
+ }
407
+ } else {
408
+ this.registry[orgName] = org
409
+ }
410
+
411
+ // Populate per-resource remote config entries
412
+ for (const id of incomingIds) {
413
+ this.remoteResources.set(`${orgName}/${id}`, remote)
414
+ }
415
+
416
+ // Rebuild serialized cache for the full merged org
417
+ this.serializedCache.set(orgName, serializeOrganization(this.registry[orgName]))
418
+ }
419
+
420
+ /**
421
+ * Patch serialized cache with pre-serialized schemas from an external manifest.
422
+ *
423
+ * External deployments use stub definitions with z.any() schemas (never called).
424
+ * The manifest carries the real schemas as pre-serialized JSON Schema from the worker.
425
+ * This method patches those into the serialized cache so describe/CLI display them.
426
+ *
427
+ * @param orgName - Organization name
428
+ * @param manifestSchemas - Map of resourceId -> { contract, steps } with JSON Schema
429
+ */
430
+ patchManifestSchemas(
431
+ orgName: string,
432
+ manifestSchemas: Array<{
433
+ resourceId: string
434
+ type: 'workflow' | 'agent'
435
+ contract?: { inputSchema?: object; outputSchema?: object }
436
+ steps?: Array<{ id: string; inputSchema?: object; outputSchema?: object }>
437
+ }>
438
+ ): void {
439
+ const cache = this.serializedCache.get(orgName)
440
+ if (!cache) return
441
+
442
+ for (const entry of manifestSchemas) {
443
+ if (entry.type === 'workflow') {
444
+ const def = cache.definitions.workflows.get(entry.resourceId)
445
+ if (!def) continue
446
+
447
+ // Patch contract schemas
448
+ if (entry.contract?.inputSchema) def.contract.inputSchema = entry.contract.inputSchema
449
+ if (entry.contract?.outputSchema) def.contract.outputSchema = entry.contract.outputSchema
450
+
451
+ // Patch step schemas
452
+ if (entry.steps && def.steps) {
453
+ for (const stepPatch of entry.steps) {
454
+ const step = def.steps.find((s) => s.id === stepPatch.id)
455
+ if (!step) continue
456
+ if (stepPatch.inputSchema) step.inputSchema = stepPatch.inputSchema
457
+ if (stepPatch.outputSchema) step.outputSchema = stepPatch.outputSchema
458
+ }
459
+ }
460
+ } else if (entry.type === 'agent') {
461
+ const def = cache.definitions.agents.get(entry.resourceId)
462
+ if (!def) continue
463
+ if (entry.contract?.inputSchema) def.contract.inputSchema = entry.contract.inputSchema
464
+ if (entry.contract?.outputSchema) def.contract.outputSchema = entry.contract.outputSchema
465
+ }
466
+ }
467
+ }
468
+
469
+ /**
470
+ * Register built-in platform resources (static, local execution)
471
+ *
472
+ * Unlike registerOrganization(), these resources:
473
+ * - Do NOT have remote config (execute in-process, not in worker threads)
474
+ * - Are NOT removed by unregisterOrganization() (persist across redeployments)
475
+ * - Use reserved resource IDs that external deployments cannot claim
476
+ *
477
+ * @param orgName - Organization name
478
+ * @param org - Resource definitions with real handlers (not stubs)
479
+ */
480
+ registerStaticResources(orgName: string, org: DeploymentSpec): void {
481
+ // Filter out archived resources before any processing
482
+ org = filterArchived(org)
483
+
484
+ const incomingWorkflowIds = (org.workflows ?? []).map((w) => w.config.resourceId)
485
+ const incomingAgentIds = (org.agents ?? []).map((a) => a.config.resourceId)
486
+ const incomingIds = [...incomingWorkflowIds, ...incomingAgentIds]
487
+
488
+ // Check for duplicates within incoming resources
489
+ const seen = new Set<string>()
490
+ for (const id of incomingIds) {
491
+ if (seen.has(id)) {
492
+ throw new Error(`Duplicate resource ID '${id}' in static resources.`)
493
+ }
494
+ seen.add(id)
495
+ }
496
+
497
+ // Check for conflicts with existing resources in this org
498
+ const existingOrg = this.registry[orgName]
499
+ if (existingOrg) {
500
+ const existingWorkflowIds = new Set((existingOrg.workflows ?? []).map((w) => w.config.resourceId))
501
+ const existingAgentIds = new Set((existingOrg.agents ?? []).map((a) => a.config.resourceId))
502
+
503
+ for (const id of incomingIds) {
504
+ if (existingWorkflowIds.has(id) || existingAgentIds.has(id)) {
505
+ throw new Error(`Static resource '${id}' conflicts with existing resource in '${orgName}'.`)
506
+ }
507
+ }
508
+ }
509
+
510
+ // Merge into registry (no remote config = local execution path)
511
+ if (existingOrg) {
512
+ existingOrg.workflows = [...(existingOrg.workflows ?? []), ...(org.workflows ?? [])]
513
+ existingOrg.agents = [...(existingOrg.agents ?? []), ...(org.agents ?? [])]
514
+ } else {
515
+ this.registry[orgName] = org
516
+ }
517
+
518
+ // Rebuild serialized cache
519
+ this.serializedCache.set(orgName, serializeOrganization(this.registry[orgName]))
520
+ }
521
+
522
+ /**
523
+ * Unregister runtime-registered resources for an organization
524
+ *
525
+ * Removes only resources that were registered at runtime (via registerOrganization).
526
+ * Static resources loaded at startup are preserved. If the org still has static
527
+ * resources after removal, the serialization cache is rebuilt. If no resources
528
+ * remain, the org is fully removed from the registry.
529
+ * No-op if the org has no remote resources.
530
+ *
531
+ * @param orgName - Organization name to unregister remote resources from
532
+ */
533
+ unregisterOrganization(orgName: string): void {
534
+ // Find all remote resource keys for this org
535
+ const prefix = `${orgName}/`
536
+ const remoteIds = new Set<string>()
537
+ for (const key of this.remoteResources.keys()) {
538
+ if (key.startsWith(prefix)) {
539
+ remoteIds.add(key.slice(prefix.length))
540
+ this.remoteResources.delete(key)
541
+ }
542
+ }
543
+
544
+ // No-op if org had no remote resources
545
+ if (remoteIds.size === 0) return
546
+
547
+ const orgResources = this.registry[orgName]
548
+ if (!orgResources) return
549
+
550
+ // Remove remote resources from the org's arrays
551
+ orgResources.workflows = (orgResources.workflows ?? []).filter((w) => !remoteIds.has(w.config.resourceId))
552
+ orgResources.agents = (orgResources.agents ?? []).filter((a) => !remoteIds.has(a.config.resourceId))
553
+
554
+ // Remove deployment-owned metadata (triggers, integrations, checkpoints, external resources)
555
+ // These are always fully owned by the deployment — replaced on each deploy
556
+ orgResources.triggers = undefined
557
+ orgResources.integrations = undefined
558
+ orgResources.humanCheckpoints = undefined
559
+ orgResources.externalResources = undefined
560
+
561
+ // Remove relationship entries for remote resource IDs
562
+ if (orgResources.relationships) {
563
+ for (const id of remoteIds) {
564
+ delete orgResources.relationships[id]
565
+ }
566
+ if (Object.keys(orgResources.relationships).length === 0) {
567
+ delete orgResources.relationships
568
+ }
569
+ }
570
+
571
+ // If the org still has static resources, rebuild cache; otherwise fully remove
572
+ // (triggers, integrations, checkpoints, externalResources were cleared above)
573
+ const remaining = (orgResources.workflows?.length ?? 0) + (orgResources.agents?.length ?? 0)
574
+
575
+ if (remaining > 0) {
576
+ this.serializedCache.set(orgName, serializeOrganization(orgResources))
577
+ } else {
578
+ delete this.registry[orgName]
579
+ this.serializedCache.delete(orgName)
580
+ }
581
+ }
582
+
583
+ /**
584
+ * Get remote configuration for a specific resource
585
+ *
586
+ * Returns the RemoteOrgConfig if the resource was registered at runtime,
587
+ * or null if it's a static resource or doesn't exist.
588
+ * Used by the execution coordinator to branch between local and worker execution.
589
+ *
590
+ * @param orgName - Organization name
591
+ * @param resourceId - Resource ID
592
+ * @returns Remote config or null
593
+ */
594
+ getRemoteConfig(orgName: string, resourceId: string): RemoteOrgConfig | null {
595
+ return this.remoteResources.get(`${orgName}/${resourceId}`) ?? null
596
+ }
597
+
598
+ /**
599
+ * Check if an organization has any remote (externally deployed) resources
600
+ *
601
+ * @param orgName - Organization name
602
+ * @returns true if the org has at least one runtime-registered resource
603
+ */
604
+ isRemote(orgName: string): boolean {
605
+ const prefix = `${orgName}/`
606
+ for (const key of this.remoteResources.keys()) {
607
+ if (key.startsWith(prefix)) return true
608
+ }
609
+ return false
610
+ }
611
+
612
+ /**
613
+ * Get the remote config for any resource in an organization.
614
+ * Used when the specific resource ID is unknown (e.g., to clean up a
615
+ * temp file before unregistering an org -- all resources share one config).
616
+ *
617
+ * @param orgName - Organization name
618
+ * @returns Remote config or null if org has no remote resources
619
+ */
620
+ getAnyRemoteConfig(orgName: string): RemoteOrgConfig | null {
621
+ const prefix = `${orgName}/`
622
+ for (const [key, config] of this.remoteResources) {
623
+ if (key.startsWith(prefix)) return config
624
+ }
625
+ return null
626
+ }
627
+
628
+ /**
629
+ * Get statistics about remotely-deployed resources
630
+ * Used by the health endpoint for platform-wide deployment visibility.
631
+ */
632
+ getRemoteStats(): { activeOrgs: number; totalResources: number } {
633
+ const orgs = new Set<string>()
634
+ for (const key of this.remoteResources.keys()) {
635
+ const orgName = key.split('/')[0]
636
+ orgs.add(orgName)
637
+ }
638
+ return {
639
+ activeOrgs: orgs.size,
640
+ totalResources: this.remoteResources.size
641
+ }
642
+ }
643
+
644
+ // ============================================================================
645
+ // Resource Manifest Accessors
646
+ // ============================================================================
647
+
648
+ /**
649
+ * Get triggers for an organization
650
+ * @param organizationName - Organization name
651
+ * @returns Array of trigger definitions (empty if none defined)
652
+ */
653
+ getTriggers(organizationName: string): TriggerDefinition[] {
654
+ return this.registry[organizationName]?.triggers ?? []
655
+ }
656
+
657
+ /**
658
+ * Get integrations for an organization
659
+ * @param organizationName - Organization name
660
+ * @returns Array of integration definitions (empty if none defined)
661
+ */
662
+ getIntegrations(organizationName: string): IntegrationDefinition[] {
663
+ return this.registry[organizationName]?.integrations ?? []
664
+ }
665
+
666
+ /**
667
+ * Get resource relationships for an organization
668
+ * @param organizationName - Organization name
669
+ * @returns Resource relationships map (undefined if none defined)
670
+ */
671
+ getRelationships(organizationName: string): ResourceRelationships | undefined {
672
+ return this.registry[organizationName]?.relationships
673
+ }
674
+
675
+ /**
676
+ * Get a specific trigger by ID
677
+ * @param organizationName - Organization name
678
+ * @param triggerId - Trigger ID
679
+ * @returns Trigger definition or null if not found
680
+ */
681
+ getTrigger(organizationName: string, triggerId: string): TriggerDefinition | null {
682
+ const triggers = this.getTriggers(organizationName)
683
+ return triggers.find((t) => t.resourceId === triggerId) ?? null
684
+ }
685
+
686
+ /**
687
+ * Get a specific integration by ID
688
+ * @param organizationName - Organization name
689
+ * @param integrationId - Integration ID
690
+ * @returns Integration definition or null if not found
691
+ */
692
+ getIntegration(organizationName: string, integrationId: string): IntegrationDefinition | null {
693
+ const integrations = this.getIntegrations(organizationName)
694
+ return integrations.find((i) => i.resourceId === integrationId) ?? null
695
+ }
696
+
697
+ /**
698
+ * Get external resources for an organization
699
+ * @param organizationName - Organization name
700
+ * @returns Array of external resource definitions (empty if none defined)
701
+ */
702
+ getExternalResources(organizationName: string): ExternalResourceDefinition[] {
703
+ return this.registry[organizationName]?.externalResources ?? []
704
+ }
705
+
706
+ /**
707
+ * Get a specific external resource by ID
708
+ * @param organizationName - Organization name
709
+ * @param externalId - External resource ID
710
+ * @returns External resource definition or null if not found
711
+ */
712
+ getExternalResource(organizationName: string, externalId: string): ExternalResourceDefinition | null {
713
+ const externalResources = this.getExternalResources(organizationName)
714
+ return externalResources.find((e) => e.resourceId === externalId) ?? null
715
+ }
716
+
717
+ /**
718
+ * Get human checkpoints for an organization
719
+ * @param organizationName - Organization name
720
+ * @returns Array of human checkpoint definitions (empty if none defined)
721
+ */
722
+ getHumanCheckpoints(organizationName: string): HumanCheckpointDefinition[] {
723
+ return this.registry[organizationName]?.humanCheckpoints ?? []
724
+ }
725
+
726
+ /**
727
+ * Get a specific human checkpoint by ID
728
+ * @param organizationName - Organization name
729
+ * @param humanCheckpointId - Human checkpoint ID
730
+ * @returns Human checkpoint definition or null if not found
731
+ */
732
+ getHumanCheckpoint(organizationName: string, humanCheckpointId: string): HumanCheckpointDefinition | null {
733
+ const humanCheckpoints = this.getHumanCheckpoints(organizationName)
734
+ return humanCheckpoints.find((hc) => hc.resourceId === humanCheckpointId) ?? null
735
+ }
736
+
737
+ // ============================================================================
738
+ // Serialized Data Access (Pre-computed at Startup)
739
+ // ============================================================================
740
+
741
+ /**
742
+ * Get serialized resource definition (instant lookup)
743
+ * Use for API responses - returns pre-computed JSON-safe structure
744
+ *
745
+ * @param organizationName - Organization name
746
+ * @param resourceId - Resource ID
747
+ * @returns Serialized definition or null if not found
748
+ */
749
+ getSerializedDefinition(
750
+ organizationName: string,
751
+ resourceId: string
752
+ ): SerializedAgentDefinition | SerializedWorkflowDefinition | null {
753
+ const cache = this.serializedCache.get(organizationName)
754
+ if (!cache) return null
755
+
756
+ return cache.definitions.agents.get(resourceId) ?? cache.definitions.workflows.get(resourceId) ?? null
757
+ }
758
+
759
+ /**
760
+ * Get resource list for organization (instant lookup)
761
+ * Use for /resources endpoint - returns pre-computed ResourceDefinition array
762
+ *
763
+ * @param organizationName - Organization name
764
+ * @returns Resource list with workflows, agents, and total count
765
+ */
766
+ getResourceList(organizationName: string): {
767
+ workflows: ResourceDefinition[]
768
+ agents: ResourceDefinition[]
769
+ total: number
770
+ } {
771
+ const cache = this.serializedCache.get(organizationName)
772
+ if (!cache) {
773
+ return { workflows: [], agents: [], total: 0 }
774
+ }
775
+ return cache.resources
776
+ }
777
+
778
+ /**
779
+ * Get Command View data for organization (instant lookup)
780
+ * Use for /command-view endpoint - returns complete graph data
781
+ *
782
+ * @param organizationName - Organization name
783
+ * @returns Command View data with nodes and edges
784
+ */
785
+ getCommandViewData(organizationName: string): CommandViewData {
786
+ const cache = this.serializedCache.get(organizationName)
787
+ if (!cache) {
788
+ return {
789
+ workflows: [],
790
+ agents: [],
791
+ triggers: [],
792
+ integrations: [],
793
+ externalResources: [],
794
+ humanCheckpoints: [],
795
+ edges: []
796
+ }
797
+ }
798
+ return cache.commandView
799
+ }
800
+
801
+ /**
802
+ * List resources that have UI interfaces configured
803
+ * Used by Execution Runner Catalog UI
804
+ *
805
+ * @param organizationName - Organization name
806
+ * @param environment - Optional environment filter ('dev' or 'prod')
807
+ * @returns Array of resources with interfaces
808
+ */
809
+ listExecutable(
810
+ organizationName: string,
811
+ environment?: 'dev' | 'prod'
812
+ ): Array<{
813
+ resourceId: string
814
+ resourceName: string
815
+ resourceType: 'workflow' | 'agent'
816
+ description?: string
817
+ status: 'dev' | 'prod'
818
+ version: string
819
+ interface: SerializedExecutionInterface
820
+ }> {
821
+ const cache = this.serializedCache.get(organizationName)
822
+ if (!cache) {
823
+ return []
824
+ }
825
+
826
+ const results: Array<{
827
+ resourceId: string
828
+ resourceName: string
829
+ resourceType: 'workflow' | 'agent'
830
+ description?: string
831
+ status: 'dev' | 'prod'
832
+ version: string
833
+ interface: SerializedExecutionInterface
834
+ }> = []
835
+
836
+ // Check workflows with interfaces
837
+ cache.definitions.workflows.forEach((serializedWorkflow, resourceId) => {
838
+ // Skip if no interface defined
839
+ if (!serializedWorkflow.interface) return
840
+
841
+ // Apply environment filter if specified
842
+ if (environment && serializedWorkflow.config.status !== environment) return
843
+
844
+ results.push({
845
+ resourceId,
846
+ resourceName: serializedWorkflow.config.name,
847
+ resourceType: 'workflow',
848
+ description: serializedWorkflow.config.description,
849
+ status: serializedWorkflow.config.status as 'dev' | 'prod',
850
+ version: serializedWorkflow.config.version,
851
+ interface: serializedWorkflow.interface
852
+ })
853
+ })
854
+
855
+ // Check agents with interfaces
856
+ cache.definitions.agents.forEach((serializedAgent, resourceId) => {
857
+ // Skip if no interface defined
858
+ if (!serializedAgent.interface) return
859
+
860
+ // Apply environment filter if specified
861
+ if (environment && serializedAgent.config.status !== environment) return
862
+
863
+ results.push({
864
+ resourceId,
865
+ resourceName: serializedAgent.config.name,
866
+ resourceType: 'agent',
867
+ description: serializedAgent.config.description,
868
+ status: serializedAgent.config.status as 'dev' | 'prod',
869
+ version: serializedAgent.config.version,
870
+ interface: serializedAgent.interface
871
+ })
872
+ })
873
+
874
+ return results
875
+ }
876
+ }