@elevasis/core 0.7.1 → 0.8.2

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 (476) hide show
  1. package/dist/test-utils/index.d.ts +3122 -0
  2. package/dist/test-utils/index.js +386 -0
  3. package/package.json +6 -1
  4. package/src/README.md +39 -36
  5. package/src/__tests__/publish.test.ts +18 -13
  6. package/src/__tests__/{template-foundations-compatibility.test.ts → template-core-compatibility.test.ts} +99 -99
  7. package/src/_gen/__tests__/__snapshots__/contracts.md.snap +1135 -1131
  8. package/src/_gen/__tests__/scaffold-contracts.test.ts +47 -36
  9. package/src/_gen/scaffold-contracts.ts +45 -45
  10. package/src/auth/multi-tenancy/credentials/README.md +38 -38
  11. package/src/auth/multi-tenancy/credentials/index.ts +6 -6
  12. package/src/auth/multi-tenancy/credentials/server/encryption.ts +39 -39
  13. package/src/auth/multi-tenancy/credentials/server/service.ts +60 -60
  14. package/src/auth/multi-tenancy/index.ts +17 -17
  15. package/src/auth/multi-tenancy/invitations/api-schemas.ts +107 -107
  16. package/src/auth/multi-tenancy/invitations/index.ts +37 -37
  17. package/src/auth/multi-tenancy/invitations/invitation.ts +86 -86
  18. package/src/auth/multi-tenancy/invitations/server/index.ts +25 -25
  19. package/src/auth/multi-tenancy/invitations/server/transforms.ts +24 -24
  20. package/src/auth/multi-tenancy/invitations/server/workos.ts +24 -24
  21. package/src/auth/multi-tenancy/invitations/supabase.ts +50 -50
  22. package/src/auth/multi-tenancy/memberships/api-schemas.ts +126 -126
  23. package/src/auth/multi-tenancy/memberships/index.ts +21 -21
  24. package/src/auth/multi-tenancy/memberships/membership.ts +138 -138
  25. package/src/auth/multi-tenancy/memberships/server/index.ts +15 -15
  26. package/src/auth/multi-tenancy/memberships/server/transforms.ts +32 -32
  27. package/src/auth/multi-tenancy/memberships/server/workos.ts +21 -21
  28. package/src/auth/multi-tenancy/memberships/supabase.ts +46 -46
  29. package/src/auth/multi-tenancy/organizations/api-schemas.ts +128 -128
  30. package/src/auth/multi-tenancy/organizations/index.ts +23 -23
  31. package/src/auth/multi-tenancy/organizations/organization.ts +24 -24
  32. package/src/auth/multi-tenancy/organizations/server/index.ts +10 -10
  33. package/src/auth/multi-tenancy/organizations/server/transforms.ts +35 -35
  34. package/src/auth/multi-tenancy/organizations/server/workos.ts +20 -20
  35. package/src/auth/multi-tenancy/types.ts +83 -83
  36. package/src/auth/multi-tenancy/users/api-schemas.ts +194 -194
  37. package/src/auth/multi-tenancy/users/index.ts +27 -27
  38. package/src/auth/multi-tenancy/users/server/index.ts +19 -19
  39. package/src/auth/multi-tenancy/users/server/transforms.ts +21 -21
  40. package/src/auth/multi-tenancy/users/server/workos.ts +16 -16
  41. package/src/auth/multi-tenancy/users/user.ts +65 -65
  42. package/src/business/README.md +52 -52
  43. package/src/business/__tests__/entities-published.test.ts +33 -33
  44. package/src/business/acquisition/api-schemas.ts +759 -759
  45. package/src/business/acquisition/index.ts +109 -109
  46. package/src/business/acquisition/types.ts +402 -402
  47. package/src/business/base-entities.test.ts +481 -481
  48. package/src/business/base-entities.ts +241 -241
  49. package/src/business/entities-published.ts +24 -24
  50. package/src/business/index.ts +15 -15
  51. package/src/business/pdf/browser/pdfmake-browser.ts +229 -229
  52. package/src/business/pdf/index.ts +10 -10
  53. package/src/business/pdf/server/index.ts +21 -21
  54. package/src/business/pdf/server/themes/default.ts +8 -8
  55. package/src/business/pdf/server/themes/index.ts +9 -9
  56. package/src/business/pdf/server/themes/types.ts +8 -8
  57. package/src/business/pdf/types.ts +272 -272
  58. package/src/business/projects/index.ts +2 -2
  59. package/src/business/projects/sse-events.ts +21 -21
  60. package/src/business/projects/types.ts +89 -89
  61. package/src/business/sales/api-schemas.ts +75 -75
  62. package/src/business/seo/__tests__/linking.test.ts +549 -549
  63. package/src/business/seo/__tests__/types.test.ts +404 -404
  64. package/src/business/seo/index.ts +2 -2
  65. package/src/business/seo/linking.ts +281 -281
  66. package/src/business/seo/types.ts +199 -199
  67. package/src/commands/queue/index.ts +3 -3
  68. package/src/commands/queue/schemas.test.ts +593 -593
  69. package/src/commands/queue/schemas.ts +125 -125
  70. package/src/commands/queue/sse-events.ts +61 -61
  71. package/src/commands/queue/types/action.ts +52 -52
  72. package/src/commands/queue/types/checkpoint.ts +44 -44
  73. package/src/commands/queue/types/index.ts +7 -7
  74. package/src/commands/queue/types/task.ts +116 -116
  75. package/src/commands/queue/types.ts +14 -14
  76. package/src/content/distribution-metadata.ts +61 -61
  77. package/src/content/index.ts +10 -10
  78. package/src/deployments/index.ts +22 -22
  79. package/src/execution/core/__tests__/archived-logs.test.ts +72 -72
  80. package/src/execution/core/index.ts +11 -11
  81. package/src/execution/core/runner-types.ts +80 -80
  82. package/src/execution/core/server/environment.ts +31 -31
  83. package/src/execution/core/sse-executions.ts +119 -119
  84. package/src/execution/core/types.ts +29 -29
  85. package/src/execution/engine/__tests__/fixtures/test-agents.ts +4 -4
  86. package/src/execution/engine/__tests__/timeout.test.ts +565 -565
  87. package/src/execution/engine/agent/__tests__/errors.test.ts +508 -508
  88. package/src/execution/engine/agent/actions/__tests__/processor.test.ts +531 -531
  89. package/src/execution/engine/agent/actions/executor.ts +205 -205
  90. package/src/execution/engine/agent/actions/navigate-knowledge-executor.ts +230 -230
  91. package/src/execution/engine/agent/actions/processor.ts +116 -116
  92. package/src/execution/engine/agent/actions/types.ts +70 -70
  93. package/src/execution/engine/agent/core/agent.ts +810 -810
  94. package/src/execution/engine/agent/core/types.ts +155 -155
  95. package/src/execution/engine/agent/errors.ts +251 -251
  96. package/src/execution/engine/agent/index.ts +78 -78
  97. package/src/execution/engine/agent/knowledge-map/types.ts +106 -106
  98. package/src/execution/engine/agent/knowledge-map/utils.ts +101 -101
  99. package/src/execution/engine/agent/memory/__tests__/manager.test.ts +754 -754
  100. package/src/execution/engine/agent/memory/domains.ts +99 -99
  101. package/src/execution/engine/agent/memory/manager.ts +365 -365
  102. package/src/execution/engine/agent/memory/processor.ts +66 -66
  103. package/src/execution/engine/agent/memory/types.ts +90 -90
  104. package/src/execution/engine/agent/memory/utils.ts +134 -134
  105. package/src/execution/engine/agent/observability/logging.ts +467 -467
  106. package/src/execution/engine/agent/observability/types.ts +64 -64
  107. package/src/execution/engine/agent/reasoning/adapters/agent-adapter-helpers.ts +349 -349
  108. package/src/execution/engine/agent/reasoning/processor.ts +92 -92
  109. package/src/execution/engine/agent/reasoning/prompt-sections/base-actions.ts +134 -134
  110. package/src/execution/engine/agent/reasoning/prompt-sections/completion.ts +49 -49
  111. package/src/execution/engine/agent/reasoning/prompt-sections/knowledge-map.ts +93 -93
  112. package/src/execution/engine/agent/reasoning/prompt-sections/memory.ts +65 -65
  113. package/src/execution/engine/agent/reasoning/prompt-sections/tools.ts +44 -44
  114. package/src/execution/engine/agent/reasoning/request-builder.ts +169 -169
  115. package/src/execution/engine/agent/reasoning/types.ts +18 -18
  116. package/src/execution/engine/base/errors.ts +118 -118
  117. package/src/execution/engine/base/index.ts +2 -2
  118. package/src/execution/engine/base/logging.ts +31 -31
  119. package/src/execution/engine/base/serialization.ts +324 -324
  120. package/src/execution/engine/base/types.ts +126 -126
  121. package/src/execution/engine/base/utils.ts +41 -41
  122. package/src/execution/engine/index.ts +434 -434
  123. package/src/execution/engine/interface/index.ts +1 -1
  124. package/src/execution/engine/interface/types.ts +62 -62
  125. package/src/execution/engine/llm/__tests__/model-info.test.ts +50 -50
  126. package/src/execution/engine/llm/__tests__/model-validation.test.ts +321 -321
  127. package/src/execution/engine/llm/__tests__/response-schema-validator.test.ts +115 -115
  128. package/src/execution/engine/llm/adapters/__tests__/adapter-factory.test.ts +375 -375
  129. package/src/execution/engine/llm/adapters/__tests__/anthropic-adapter.test.ts +463 -463
  130. package/src/execution/engine/llm/adapters/__tests__/anthropic.integration.test.ts +177 -177
  131. package/src/execution/engine/llm/adapters/__tests__/google-adapter.test.ts +722 -722
  132. package/src/execution/engine/llm/adapters/__tests__/google.integration.test.ts +376 -376
  133. package/src/execution/engine/llm/adapters/__tests__/openai-adapter.test.ts +551 -551
  134. package/src/execution/engine/llm/adapters/__tests__/openrouter-adapter.test.ts +563 -563
  135. package/src/execution/engine/llm/adapters/__tests__/openrouter.integration.test.ts +105 -105
  136. package/src/execution/engine/llm/adapters/__tests__/universal-adapter.test.ts +537 -537
  137. package/src/execution/engine/llm/adapters/circuit-breaker.ts +147 -147
  138. package/src/execution/engine/llm/adapters/index.ts +17 -17
  139. package/src/execution/engine/llm/adapters/mock-adapter.ts +116 -116
  140. package/src/execution/engine/llm/adapters/server/adapter-factory.ts +130 -130
  141. package/src/execution/engine/llm/adapters/server/anthropic.ts +137 -137
  142. package/src/execution/engine/llm/adapters/server/google.ts +283 -283
  143. package/src/execution/engine/llm/adapters/server/index.ts +12 -12
  144. package/src/execution/engine/llm/adapters/server/openai.ts +206 -206
  145. package/src/execution/engine/llm/adapters/server/openrouter.ts +235 -235
  146. package/src/execution/engine/llm/adapters/universal-adapter.ts +230 -230
  147. package/src/execution/engine/llm/errors.ts +186 -186
  148. package/src/execution/engine/llm/model-info.ts +332 -332
  149. package/src/execution/engine/llm/response-schema-validator.ts +113 -113
  150. package/src/execution/engine/llm/types.ts +86 -86
  151. package/src/execution/engine/test-utils/index.ts +6 -6
  152. package/src/execution/engine/test-utils/mocks.ts +56 -56
  153. package/src/execution/engine/tools/integration/base-integration-adapter.ts +50 -50
  154. package/src/execution/engine/tools/integration/index.ts +53 -53
  155. package/src/execution/engine/tools/integration/server/adapters/anymailfinder/anymailfinder-adapter.ts +73 -73
  156. package/src/execution/engine/tools/integration/server/adapters/anymailfinder/anymailfinder-tools.ts +209 -209
  157. package/src/execution/engine/tools/integration/server/adapters/anymailfinder/fetch/find-company-email/index.ts +82 -82
  158. package/src/execution/engine/tools/integration/server/adapters/anymailfinder/fetch/find-decision-maker-email/index.ts +122 -122
  159. package/src/execution/engine/tools/integration/server/adapters/anymailfinder/fetch/find-person-email/index.ts +89 -89
  160. package/src/execution/engine/tools/integration/server/adapters/anymailfinder/fetch/verify-email/index.ts +84 -84
  161. package/src/execution/engine/tools/integration/server/adapters/anymailfinder/index.ts +16 -16
  162. package/src/execution/engine/tools/integration/server/adapters/apify/__tests__/apify-run-actor.integration.test.ts +293 -293
  163. package/src/execution/engine/tools/integration/server/adapters/apify/apify-adapter.ts +100 -100
  164. package/src/execution/engine/tools/integration/server/adapters/apify/apify-tools.ts +217 -217
  165. package/src/execution/engine/tools/integration/server/adapters/apify/fetch/get-dataset-items/index.ts +92 -92
  166. package/src/execution/engine/tools/integration/server/adapters/apify/fetch/run-actor/index.ts +218 -218
  167. package/src/execution/engine/tools/integration/server/adapters/apify/fetch/start-actor/index.ts +87 -87
  168. package/src/execution/engine/tools/integration/server/adapters/apify/index.ts +11 -11
  169. package/src/execution/engine/tools/integration/server/adapters/attio/__tests__/attio-crud.integration.test.ts +361 -361
  170. package/src/execution/engine/tools/integration/server/adapters/attio/attio-adapter.ts +162 -162
  171. package/src/execution/engine/tools/integration/server/adapters/attio/attio-tools.ts +594 -594
  172. package/src/execution/engine/tools/integration/server/adapters/attio/fetch/create-attribute/index.ts +214 -214
  173. package/src/execution/engine/tools/integration/server/adapters/attio/fetch/create-note/index.ts +152 -152
  174. package/src/execution/engine/tools/integration/server/adapters/attio/fetch/create-record/index.ts +141 -141
  175. package/src/execution/engine/tools/integration/server/adapters/attio/fetch/delete-note/index.ts +86 -86
  176. package/src/execution/engine/tools/integration/server/adapters/attio/fetch/delete-record/index.ts +105 -105
  177. package/src/execution/engine/tools/integration/server/adapters/attio/fetch/get-record/index.ts +118 -118
  178. package/src/execution/engine/tools/integration/server/adapters/attio/fetch/list-attributes/index.ts +165 -165
  179. package/src/execution/engine/tools/integration/server/adapters/attio/fetch/list-notes/index.ts +96 -96
  180. package/src/execution/engine/tools/integration/server/adapters/attio/fetch/list-objects/index.ts +104 -104
  181. package/src/execution/engine/tools/integration/server/adapters/attio/fetch/list-records/index.ts +156 -156
  182. package/src/execution/engine/tools/integration/server/adapters/attio/fetch/update-attribute/index.ts +220 -220
  183. package/src/execution/engine/tools/integration/server/adapters/attio/fetch/update-record/index.ts +140 -140
  184. package/src/execution/engine/tools/integration/server/adapters/attio/fetch/utils/types.ts +146 -146
  185. package/src/execution/engine/tools/integration/server/adapters/attio/index.ts +31 -31
  186. package/src/execution/engine/tools/integration/server/adapters/gmail/gmail-adapter.ts +210 -210
  187. package/src/execution/engine/tools/integration/server/adapters/gmail/gmail-tools.ts +104 -104
  188. package/src/execution/engine/tools/integration/server/adapters/google-sheets/__tests__/google-sheets.integration.test.ts +261 -261
  189. package/src/execution/engine/tools/integration/server/adapters/google-sheets/google-sheets-adapter.ts +1189 -1189
  190. package/src/execution/engine/tools/integration/server/adapters/google-sheets/google-sheets-tools.ts +641 -641
  191. package/src/execution/engine/tools/integration/server/adapters/google-sheets/index.ts +18 -18
  192. package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/activate-campaign/index.ts +86 -86
  193. package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/add-to-campaign/__tests__/index.test.ts +289 -289
  194. package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/add-to-campaign/index.ts +154 -154
  195. package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/bulk-add-leads/__tests__/index.test.ts +325 -325
  196. package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/bulk-add-leads/index.ts +153 -153
  197. package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/bulk-delete-leads/index.ts +84 -84
  198. package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/create-campaign/index.ts +125 -125
  199. package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/create-inbox-test/index.ts +107 -107
  200. package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/delete-campaign/index.ts +85 -85
  201. package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/get-account-health/index.ts +91 -91
  202. package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/get-campaign/index.ts +92 -92
  203. package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/get-campaign-analytics/__tests__/index.test.ts +195 -195
  204. package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/get-campaign-analytics/index.ts +113 -113
  205. package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/get-daily-campaign-analytics/index.ts +104 -104
  206. package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/get-emails/index.ts +155 -155
  207. package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/get-step-analytics/__tests__/index.test.ts +196 -196
  208. package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/get-step-analytics/index.ts +102 -102
  209. package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/list-campaigns/__tests__/index.test.ts +189 -189
  210. package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/list-campaigns/index.ts +87 -87
  211. package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/list-leads/index.ts +112 -112
  212. package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/patch-lead/index.ts +76 -76
  213. package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/pause-campaign/index.ts +86 -86
  214. package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/remove-from-subsequence/index.ts +98 -98
  215. package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/send-reply/index.ts +126 -126
  216. package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/update-campaign/__tests__/index.test.ts +193 -193
  217. package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/update-campaign/index.ts +99 -99
  218. package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/update-interest-status/__tests__/index.test.ts +621 -621
  219. package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/update-interest-status/index.ts +125 -125
  220. package/src/execution/engine/tools/integration/server/adapters/instantly/index.ts +29 -29
  221. package/src/execution/engine/tools/integration/server/adapters/instantly/instantly-adapter.ts +178 -178
  222. package/src/execution/engine/tools/integration/server/adapters/instantly/instantly-tools.ts +1473 -1473
  223. package/src/execution/engine/tools/integration/server/adapters/millionverifier/fetch/check-credits/index.ts +59 -59
  224. package/src/execution/engine/tools/integration/server/adapters/millionverifier/fetch/verify-email/index.ts +102 -102
  225. package/src/execution/engine/tools/integration/server/adapters/millionverifier/index.ts +17 -17
  226. package/src/execution/engine/tools/integration/server/adapters/millionverifier/millionverifier-adapter.ts +80 -80
  227. package/src/execution/engine/tools/integration/server/adapters/millionverifier/millionverifier-tools.ts +102 -102
  228. package/src/execution/engine/tools/integration/server/adapters/resend/fetch/get-email/index.ts +102 -102
  229. package/src/execution/engine/tools/integration/server/adapters/resend/fetch/send-email/index.ts +134 -134
  230. package/src/execution/engine/tools/integration/server/adapters/resend/fetch/utils/types.ts +75 -75
  231. package/src/execution/engine/tools/integration/server/adapters/resend/index.ts +27 -27
  232. package/src/execution/engine/tools/integration/server/adapters/resend/resend-adapter.ts +108 -108
  233. package/src/execution/engine/tools/integration/server/adapters/resend/resend-tools.ts +132 -132
  234. package/src/execution/engine/tools/integration/server/adapters/signature-api/fetch/create-envelope/index.ts +274 -274
  235. package/src/execution/engine/tools/integration/server/adapters/signature-api/fetch/download-document/index.ts +230 -230
  236. package/src/execution/engine/tools/integration/server/adapters/signature-api/fetch/get-envelope/index.ts +133 -133
  237. package/src/execution/engine/tools/integration/server/adapters/signature-api/fetch/void-envelope/index.ts +90 -90
  238. package/src/execution/engine/tools/integration/server/adapters/stripe/fetch/utils/types.ts +210 -210
  239. package/src/execution/engine/tools/integration/server/adapters/stripe/stripe-adapter.ts +517 -517
  240. package/src/execution/engine/tools/integration/server/adapters/stripe/stripe-tools.ts +309 -309
  241. package/src/execution/engine/tools/integration/server/adapters/tomba/fetch/domain-search/index.ts +133 -133
  242. package/src/execution/engine/tools/integration/server/adapters/tomba/fetch/email-finder/index.ts +122 -122
  243. package/src/execution/engine/tools/integration/server/adapters/tomba/fetch/email-verifier/index.ts +111 -111
  244. package/src/execution/engine/tools/integration/server/adapters/tomba/index.ts +11 -11
  245. package/src/execution/engine/tools/integration/server/adapters/tomba/tomba-adapter.ts +78 -78
  246. package/src/execution/engine/tools/integration/server/adapters/tomba/tomba-tools.ts +222 -222
  247. package/src/execution/engine/tools/integration/server/index.ts +61 -61
  248. package/src/execution/engine/tools/integration/service.ts +161 -161
  249. package/src/execution/engine/tools/integration/tool.ts +253 -253
  250. package/src/execution/engine/tools/integration/types/anymailfinder.ts +74 -74
  251. package/src/execution/engine/tools/integration/types/apify.ts +92 -92
  252. package/src/execution/engine/tools/integration/types/index.ts +19 -19
  253. package/src/execution/engine/tools/integration/types/instantly.ts +557 -557
  254. package/src/execution/engine/tools/integration/types/millionverifier.ts +56 -56
  255. package/src/execution/engine/tools/integration/types/stripe.ts +162 -162
  256. package/src/execution/engine/tools/integration/types/tomba.ts +94 -94
  257. package/src/execution/engine/tools/lead-service-types.ts +884 -884
  258. package/src/execution/engine/tools/llm/index.ts +11 -11
  259. package/src/execution/engine/tools/llm/server/index.ts +8 -8
  260. package/src/execution/engine/tools/llm/server/llm-call-tool.ts +118 -118
  261. package/src/execution/engine/tools/platform/__tests__/pdf.test.ts +441 -441
  262. package/src/execution/engine/tools/platform/acquisition/company-tools.ts +248 -248
  263. package/src/execution/engine/tools/platform/acquisition/contact-tools.ts +319 -319
  264. package/src/execution/engine/tools/platform/acquisition/index.ts +43 -43
  265. package/src/execution/engine/tools/platform/acquisition/list-tools.ts +148 -148
  266. package/src/execution/engine/tools/platform/acquisition/types.ts +260 -260
  267. package/src/execution/engine/tools/platform/email/index.ts +122 -122
  268. package/src/execution/engine/tools/platform/email/types.ts +96 -96
  269. package/src/execution/engine/tools/platform/index.ts +157 -157
  270. package/src/execution/engine/tools/platform/notification.ts +81 -81
  271. package/src/execution/engine/tools/platform/pdf/index.ts +110 -110
  272. package/src/execution/engine/tools/platform/pdf/types.ts +77 -77
  273. package/src/execution/engine/tools/platform/scheduler.ts +87 -87
  274. package/src/execution/engine/tools/platform/storage/index.ts +370 -370
  275. package/src/execution/engine/tools/platform/types.ts +148 -148
  276. package/src/execution/engine/tools/registry.ts +700 -700
  277. package/src/execution/engine/tools/tool-maps.ts +786 -786
  278. package/src/execution/engine/tools/types.ts +233 -233
  279. package/src/execution/engine/workflow/__tests__/errors.test.ts +139 -139
  280. package/src/execution/engine/workflow/errors.ts +63 -63
  281. package/src/execution/engine/workflow/helpers/index.ts +11 -11
  282. package/src/execution/engine/workflow/helpers/server/index.ts +8 -8
  283. package/src/execution/engine/workflow/helpers/server/llm-call.ts +93 -93
  284. package/src/execution/engine/workflow/index.ts +19 -19
  285. package/src/execution/engine/workflow/log-truncate.ts +26 -26
  286. package/src/execution/engine/workflow/logging.ts +191 -191
  287. package/src/execution/engine/workflow/types.ts +182 -182
  288. package/src/execution/engine/workflow/utils.ts +280 -280
  289. package/src/execution/engine/workflow/workflow.ts +168 -168
  290. package/src/execution/index.ts +3 -3
  291. package/src/execution/scheduler/__tests__/api-schemas.test.ts +733 -733
  292. package/src/execution/scheduler/__tests__/utils.test.ts +1009 -1009
  293. package/src/execution/scheduler/api-schemas.ts +296 -296
  294. package/src/execution/scheduler/index.ts +50 -50
  295. package/src/execution/scheduler/schemas.ts +264 -264
  296. package/src/execution/scheduler/types.ts +111 -111
  297. package/src/execution/scheduler/utils.ts +364 -364
  298. package/src/forms/index.ts +7 -7
  299. package/src/forms/schemas.ts +69 -69
  300. package/src/forms/types.ts +70 -70
  301. package/src/index.ts +71 -60
  302. package/src/integrations/credentials/__tests__/schemas.test.ts +82 -82
  303. package/src/integrations/credentials/__tests__/utils.test.ts +144 -144
  304. package/src/integrations/credentials/api-schemas.ts +143 -143
  305. package/src/integrations/credentials/index.ts +32 -32
  306. package/src/integrations/credentials/schemas.ts +164 -164
  307. package/src/integrations/credentials/utils.ts +59 -59
  308. package/src/integrations/oauth/__tests__/provider-registry.test.ts +59 -59
  309. package/src/integrations/oauth/api-schemas.ts +92 -92
  310. package/src/integrations/oauth/index.ts +19 -19
  311. package/src/integrations/oauth/provider-registry.ts +61 -61
  312. package/src/integrations/oauth/server/__tests__/refresh-concurrent.test.ts +183 -183
  313. package/src/integrations/oauth/server/__tests__/refresh.test.ts +577 -577
  314. package/src/integrations/oauth/server/credentials.ts +39 -39
  315. package/src/integrations/oauth/server/refresh.ts +214 -214
  316. package/src/integrations/oauth/types.ts +34 -34
  317. package/src/integrations/webhook-endpoints/__tests__/api-schemas.test.ts +318 -318
  318. package/src/integrations/webhook-endpoints/api-schemas.ts +102 -102
  319. package/src/integrations/webhook-endpoints/index.ts +28 -28
  320. package/src/integrations/webhook-endpoints/types.ts +51 -51
  321. package/src/operations/activities/api-schemas.ts +79 -79
  322. package/src/operations/activities/index.ts +9 -9
  323. package/src/operations/activities/sse-events.ts +30 -30
  324. package/src/operations/activities/types.ts +63 -63
  325. package/src/operations/debug-logs/client.ts +60 -60
  326. package/src/operations/debug-logs/debug-logger.ts +83 -83
  327. package/src/operations/debug-logs/index.ts +8 -8
  328. package/src/operations/debug-logs/server.ts +19 -19
  329. package/src/operations/debug-logs/types.ts +33 -33
  330. package/src/operations/index.ts +50 -50
  331. package/src/operations/notifications/api-schemas.ts +91 -91
  332. package/src/operations/notifications/index.ts +3 -3
  333. package/src/operations/notifications/sse-events.ts +21 -21
  334. package/src/operations/notifications/types.ts +47 -47
  335. package/src/operations/observability/__tests__/openrouter-cost-flow.test.ts +297 -297
  336. package/src/operations/observability/__tests__/utils.test.ts +54 -54
  337. package/src/operations/observability/ai-usage-collector.ts +64 -64
  338. package/src/operations/observability/index.ts +13 -13
  339. package/src/operations/observability/metrics-collector.ts +49 -49
  340. package/src/operations/observability/schemas.ts +39 -39
  341. package/src/operations/observability/types.ts +463 -463
  342. package/src/operations/observability/utils.ts +77 -77
  343. package/src/operations/sessions/__tests__/manager.test.ts +821 -821
  344. package/src/operations/sessions/index.ts +26 -26
  345. package/src/operations/sessions/server/manager.ts +90 -90
  346. package/src/operations/sessions/server/session.ts +180 -180
  347. package/src/operations/sessions/types.ts +98 -98
  348. package/src/operations/triggers/index.ts +12 -12
  349. package/src/operations/triggers/webhook/definitions/instantly-account-error.ts +44 -44
  350. package/src/operations/triggers/webhook/definitions/instantly-auto-reply-received.ts +51 -51
  351. package/src/operations/triggers/webhook/definitions/instantly-campaign-completed.ts +45 -45
  352. package/src/operations/triggers/webhook/definitions/instantly-email-bounced.ts +49 -49
  353. package/src/operations/triggers/webhook/definitions/instantly-lead-unsubscribed.ts +45 -45
  354. package/src/operations/triggers/webhook/definitions/instantly-reply-received.ts +54 -54
  355. package/src/operations/triggers/webhook/index.ts +35 -35
  356. package/src/operations/triggers/webhook/types.ts +74 -74
  357. package/src/organization-model/README.md +97 -97
  358. package/src/organization-model/__tests__/defaults.test.ts +175 -175
  359. package/src/organization-model/__tests__/domains/customers.test.ts +295 -295
  360. package/src/organization-model/__tests__/domains/goals.test.ts +479 -479
  361. package/src/organization-model/__tests__/domains/identity.test.ts +279 -279
  362. package/src/organization-model/__tests__/domains/navigation.test.ts +212 -212
  363. package/src/organization-model/__tests__/domains/offerings.test.ts +419 -419
  364. package/src/organization-model/__tests__/domains/operations.test.ts +203 -203
  365. package/src/organization-model/__tests__/domains/resource-mappings.test.ts +362 -362
  366. package/src/organization-model/__tests__/domains/roles.test.ts +347 -347
  367. package/src/organization-model/__tests__/domains/statuses.test.ts +243 -243
  368. package/src/organization-model/__tests__/foundation.test.ts +105 -105
  369. package/src/organization-model/__tests__/graph.test.ts +894 -894
  370. package/src/organization-model/__tests__/resolve.test.ts +690 -690
  371. package/src/organization-model/__tests__/schema.test.ts +407 -407
  372. package/src/organization-model/contracts.ts +14 -14
  373. package/src/organization-model/defaults.ts +148 -148
  374. package/src/organization-model/domains/branding.ts +22 -22
  375. package/src/organization-model/domains/customers.ts +75 -75
  376. package/src/organization-model/domains/features.ts +22 -22
  377. package/src/organization-model/domains/goals.ts +80 -80
  378. package/src/organization-model/domains/identity.ts +94 -94
  379. package/src/organization-model/domains/navigation.ts +391 -391
  380. package/src/organization-model/domains/offerings.ts +66 -66
  381. package/src/organization-model/domains/operations.ts +85 -85
  382. package/src/organization-model/domains/projects.ts +48 -48
  383. package/src/organization-model/domains/prospecting.ts +33 -33
  384. package/src/organization-model/domains/roles.ts +55 -55
  385. package/src/organization-model/domains/sales.ts +94 -94
  386. package/src/organization-model/domains/shared.ts +62 -62
  387. package/src/organization-model/domains/statuses.ts +130 -130
  388. package/src/organization-model/foundation.ts +97 -97
  389. package/src/organization-model/graph/build.ts +399 -399
  390. package/src/organization-model/graph/index.ts +4 -4
  391. package/src/organization-model/graph/schema.ts +48 -48
  392. package/src/organization-model/graph/types.ts +40 -40
  393. package/src/organization-model/index.ts +13 -13
  394. package/src/organization-model/organization-graph.mdx +272 -272
  395. package/src/organization-model/organization-model.mdx +320 -320
  396. package/src/organization-model/published.ts +85 -85
  397. package/src/organization-model/resolve.ts +66 -66
  398. package/src/organization-model/schema.ts +287 -287
  399. package/src/organization-model/types.ts +46 -46
  400. package/src/platform/api/index.ts +1 -1
  401. package/src/platform/api/types.ts +35 -35
  402. package/src/platform/constants/http.ts +37 -37
  403. package/src/platform/constants/index.ts +5 -5
  404. package/src/platform/constants/limits.ts +32 -32
  405. package/src/platform/constants/resilience.ts +51 -51
  406. package/src/platform/constants/timeouts.ts +20 -20
  407. package/src/platform/constants/versions.ts +3 -3
  408. package/src/platform/registry/__tests__/resource-registry-static.test.ts +347 -347
  409. package/src/platform/registry/__tests__/resource-registry.integration.test.ts +1028 -1028
  410. package/src/platform/registry/__tests__/resource-registry.list-executable.test.ts +393 -393
  411. package/src/platform/registry/__tests__/resource-registry.test.ts +2005 -2005
  412. package/src/platform/registry/__tests__/serialization.test.ts +1127 -1127
  413. package/src/platform/registry/command-view.ts +180 -180
  414. package/src/platform/registry/domains.ts +165 -165
  415. package/src/platform/registry/index.ts +93 -93
  416. package/src/platform/registry/reserved.ts +24 -24
  417. package/src/platform/registry/resource-metadata.ts +59 -59
  418. package/src/platform/registry/resource-registry.command-queue-groups.test.ts +129 -129
  419. package/src/platform/registry/resource-registry.ts +876 -876
  420. package/src/platform/registry/serialization.ts +273 -273
  421. package/src/platform/registry/serialized-types.ts +231 -231
  422. package/src/platform/registry/stats-types.ts +66 -66
  423. package/src/platform/registry/types.ts +404 -404
  424. package/src/platform/registry/validation.ts +513 -513
  425. package/src/platform/resilience/__tests__/rate-limiter.test.ts +471 -471
  426. package/src/platform/resilience/circuit-breaker.ts +164 -164
  427. package/src/platform/resilience/errors.ts +68 -68
  428. package/src/platform/resilience/http-error-mapper.ts +129 -129
  429. package/src/platform/resilience/index.ts +93 -93
  430. package/src/platform/resilience/rate-limiter-types.ts +46 -46
  431. package/src/platform/resilience/rate-limiter.ts +140 -140
  432. package/src/platform/resilience/retry.ts +89 -89
  433. package/src/platform/resilience/timeout.ts +63 -63
  434. package/src/platform/sse/events.ts +37 -37
  435. package/src/platform/sse/index.ts +7 -7
  436. package/src/platform/utils/__tests__/validation.test.ts +1083 -1083
  437. package/src/platform/utils/currency.ts +96 -96
  438. package/src/platform/utils/debounce.ts +52 -52
  439. package/src/platform/utils/error.ts +41 -41
  440. package/src/platform/utils/hmac.test.ts +97 -97
  441. package/src/platform/utils/index.ts +32 -32
  442. package/src/platform/utils/server/betterstack-logger.ts +210 -210
  443. package/src/platform/utils/server/hmac.ts +44 -44
  444. package/src/platform/utils/server/unsubscribe.ts +111 -111
  445. package/src/platform/utils/token-counter.ts +96 -96
  446. package/src/platform/utils/validation.ts +425 -425
  447. package/src/projects/api-schemas.ts +268 -268
  448. package/src/published.ts +1 -1
  449. package/src/reference/_generated/contracts.md +607 -607
  450. package/src/reference/glossary.md +105 -105
  451. package/src/requests/__tests__/api-schemas.test.ts +277 -277
  452. package/src/requests/api-schemas.ts +83 -83
  453. package/src/requests/index.ts +1 -1
  454. package/src/scaffold-registry/__tests__/index.test.ts +17 -0
  455. package/src/scaffold-registry/__tests__/schema.test.ts +329 -230
  456. package/src/scaffold-registry/index.ts +205 -189
  457. package/src/scaffold-registry/schema.ts +196 -128
  458. package/src/server.ts +272 -272
  459. package/src/supabase/database.types.ts +2719 -2719
  460. package/src/supabase/helpers.ts +20 -20
  461. package/src/supabase/index.ts +52 -52
  462. package/src/supabase/server/client.ts +58 -58
  463. package/src/test-utils/README.md +30 -138
  464. package/src/test-utils/browser-mocks.ts +54 -54
  465. package/src/test-utils/fixtures/api-keys.ts +52 -52
  466. package/src/test-utils/fixtures/index.ts +4 -4
  467. package/src/test-utils/fixtures/memberships.ts +80 -80
  468. package/src/test-utils/fixtures/organizations.ts +69 -69
  469. package/src/test-utils/fixtures/users.ts +79 -79
  470. package/src/test-utils/index.ts +7 -8
  471. package/src/test-utils/mocks/index.ts +2 -2
  472. package/src/test-utils/mocks/supabase.ts +142 -142
  473. package/src/test-utils/mocks/workos.ts +108 -108
  474. package/src/test-utils/published.ts +4 -0
  475. package/src/test-utils/rls/RLSTestContext.ts +554 -554
  476. 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
+ }