@igoruehara/canvas-flow 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (283) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +152 -0
  3. package/bin/canvas-flow.js +1132 -0
  4. package/package.json +68 -0
  5. package/public/assets/index-PCQkqMUe.css +1 -0
  6. package/public/assets/index-qV8twxcq.js +767 -0
  7. package/public/index.html +13 -0
  8. package/server/api-key/api-key-connect-provider.d.ts +104 -0
  9. package/server/api-key/api-key-connect-provider.js +14 -0
  10. package/server/api-key/api-key-connect-provider.js.map +1 -0
  11. package/server/api-key/api-key-constants-model.d.ts +2 -0
  12. package/server/api-key/api-key-constants-model.js +6 -0
  13. package/server/api-key/api-key-constants-model.js.map +1 -0
  14. package/server/api-key/api-key-controller.d.ts +12 -0
  15. package/server/api-key/api-key-controller.js +86 -0
  16. package/server/api-key/api-key-controller.js.map +1 -0
  17. package/server/api-key/api-key-module.d.ts +2 -0
  18. package/server/api-key/api-key-module.js +27 -0
  19. package/server/api-key/api-key-module.js.map +1 -0
  20. package/server/api-key/api-key-schema.d.ts +72 -0
  21. package/server/api-key/api-key-schema.js +98 -0
  22. package/server/api-key/api-key-schema.js.map +1 -0
  23. package/server/api-key/api-key-service.d.ts +45 -0
  24. package/server/api-key/api-key-service.js +151 -0
  25. package/server/api-key/api-key-service.js.map +1 -0
  26. package/server/api-key/dto/create-api-key.dto.d.ts +8 -0
  27. package/server/api-key/dto/create-api-key.dto.js +7 -0
  28. package/server/api-key/dto/create-api-key.dto.js.map +1 -0
  29. package/server/app.module.d.ts +2 -0
  30. package/server/app.module.js +53 -0
  31. package/server/app.module.js.map +1 -0
  32. package/server/auth/auth-connect-provider.d.ts +140 -0
  33. package/server/auth/auth-connect-provider.js +20 -0
  34. package/server/auth/auth-connect-provider.js.map +1 -0
  35. package/server/auth/auth-constants-model.d.ts +4 -0
  36. package/server/auth/auth-constants-model.js +8 -0
  37. package/server/auth/auth-constants-model.js.map +1 -0
  38. package/server/auth/auth-controller.d.ts +25 -0
  39. package/server/auth/auth-controller.js +96 -0
  40. package/server/auth/auth-controller.js.map +1 -0
  41. package/server/auth/auth-module.d.ts +2 -0
  42. package/server/auth/auth-module.js +26 -0
  43. package/server/auth/auth-module.js.map +1 -0
  44. package/server/auth/auth-organization-schema.d.ts +44 -0
  45. package/server/auth/auth-organization-schema.js +62 -0
  46. package/server/auth/auth-organization-schema.js.map +1 -0
  47. package/server/auth/auth-schema.d.ts +56 -0
  48. package/server/auth/auth-schema.js +77 -0
  49. package/server/auth/auth-schema.js.map +1 -0
  50. package/server/auth/auth-service.d.ts +64 -0
  51. package/server/auth/auth-service.js +343 -0
  52. package/server/auth/auth-service.js.map +1 -0
  53. package/server/canvas-flow/canvas-flow-connect-provider.d.ts +278 -0
  54. package/server/canvas-flow/canvas-flow-connect-provider.js +24 -0
  55. package/server/canvas-flow/canvas-flow-connect-provider.js.map +1 -0
  56. package/server/canvas-flow/canvas-flow-constants-model.d.ts +6 -0
  57. package/server/canvas-flow/canvas-flow-constants-model.js +10 -0
  58. package/server/canvas-flow/canvas-flow-constants-model.js.map +1 -0
  59. package/server/canvas-flow/canvas-flow-controller.d.ts +98 -0
  60. package/server/canvas-flow/canvas-flow-controller.js +423 -0
  61. package/server/canvas-flow/canvas-flow-controller.js.map +1 -0
  62. package/server/canvas-flow/canvas-flow-module.d.ts +2 -0
  63. package/server/canvas-flow/canvas-flow-module.js +27 -0
  64. package/server/canvas-flow/canvas-flow-module.js.map +1 -0
  65. package/server/canvas-flow/canvas-flow-schema.d.ts +192 -0
  66. package/server/canvas-flow/canvas-flow-schema.js +239 -0
  67. package/server/canvas-flow/canvas-flow-schema.js.map +1 -0
  68. package/server/canvas-flow/canvas-flow-service.d.ts +250 -0
  69. package/server/canvas-flow/canvas-flow-service.js +1681 -0
  70. package/server/canvas-flow/canvas-flow-service.js.map +1 -0
  71. package/server/canvas-flow/dto/create-canvas-flow.dto.d.ts +11 -0
  72. package/server/canvas-flow/dto/create-canvas-flow.dto.js +61 -0
  73. package/server/canvas-flow/dto/create-canvas-flow.dto.js.map +1 -0
  74. package/server/canvas-flow/dto/update-canvas-flow.dto.d.ts +10 -0
  75. package/server/canvas-flow/dto/update-canvas-flow.dto.js +56 -0
  76. package/server/canvas-flow/dto/update-canvas-flow.dto.js.map +1 -0
  77. package/server/constants-global.d.ts +1 -0
  78. package/server/constants-global.js +5 -0
  79. package/server/constants-global.js.map +1 -0
  80. package/server/database/database.module.d.ts +2 -0
  81. package/server/database/database.module.js +23 -0
  82. package/server/database/database.module.js.map +1 -0
  83. package/server/database/database.providers.d.ts +7 -0
  84. package/server/database/database.providers.js +26 -0
  85. package/server/database/database.providers.js.map +1 -0
  86. package/server/documents/documents-connect-provider.d.ts +140 -0
  87. package/server/documents/documents-connect-provider.js +14 -0
  88. package/server/documents/documents-connect-provider.js.map +1 -0
  89. package/server/documents/documents-constants-model.d.ts +2 -0
  90. package/server/documents/documents-constants-model.js +6 -0
  91. package/server/documents/documents-constants-model.js.map +1 -0
  92. package/server/documents/documents-controller.d.ts +16 -0
  93. package/server/documents/documents-controller.js +117 -0
  94. package/server/documents/documents-controller.js.map +1 -0
  95. package/server/documents/documents-module.d.ts +2 -0
  96. package/server/documents/documents-module.js +27 -0
  97. package/server/documents/documents-module.js.map +1 -0
  98. package/server/documents/documents-schema.d.ts +96 -0
  99. package/server/documents/documents-schema.js +38 -0
  100. package/server/documents/documents-schema.js.map +1 -0
  101. package/server/documents/documents-service.d.ts +164 -0
  102. package/server/documents/documents-service.js +1417 -0
  103. package/server/documents/documents-service.js.map +1 -0
  104. package/server/flow-tag/flow-tag-connect-provider.d.ts +146 -0
  105. package/server/flow-tag/flow-tag-connect-provider.js +14 -0
  106. package/server/flow-tag/flow-tag-connect-provider.js.map +1 -0
  107. package/server/flow-tag/flow-tag-constants-model.d.ts +2 -0
  108. package/server/flow-tag/flow-tag-constants-model.js +6 -0
  109. package/server/flow-tag/flow-tag-constants-model.js.map +1 -0
  110. package/server/flow-tag/flow-tag-module.d.ts +2 -0
  111. package/server/flow-tag/flow-tag-module.js +24 -0
  112. package/server/flow-tag/flow-tag-module.js.map +1 -0
  113. package/server/flow-tag/flow-tag-schema.d.ts +100 -0
  114. package/server/flow-tag/flow-tag-schema.js +131 -0
  115. package/server/flow-tag/flow-tag-schema.js.map +1 -0
  116. package/server/flow-tag/flow-tag-service.d.ts +77 -0
  117. package/server/flow-tag/flow-tag-service.js +156 -0
  118. package/server/flow-tag/flow-tag-service.js.map +1 -0
  119. package/server/health.controller.d.ts +7 -0
  120. package/server/health.controller.js +33 -0
  121. package/server/health.controller.js.map +1 -0
  122. package/server/http-batch/http-batch-controller.d.ts +345 -0
  123. package/server/http-batch/http-batch-controller.js +40 -0
  124. package/server/http-batch/http-batch-controller.js.map +1 -0
  125. package/server/http-batch/http-batch-module.d.ts +2 -0
  126. package/server/http-batch/http-batch-module.js +25 -0
  127. package/server/http-batch/http-batch-module.js.map +1 -0
  128. package/server/http-batch/http-batch-service.d.ts +381 -0
  129. package/server/http-batch/http-batch-service.js +268 -0
  130. package/server/http-batch/http-batch-service.js.map +1 -0
  131. package/server/lambda.d.ts +2 -0
  132. package/server/lambda.js +115 -0
  133. package/server/lambda.js.map +1 -0
  134. package/server/llm/openai-provider.d.ts +8 -0
  135. package/server/llm/openai-provider.js +256 -0
  136. package/server/llm/openai-provider.js.map +1 -0
  137. package/server/main.d.ts +1 -0
  138. package/server/main.js +80 -0
  139. package/server/main.js.map +1 -0
  140. package/server/mcp-oauth/mcp-oauth-connect-provider.d.ts +164 -0
  141. package/server/mcp-oauth/mcp-oauth-connect-provider.js +14 -0
  142. package/server/mcp-oauth/mcp-oauth-connect-provider.js.map +1 -0
  143. package/server/mcp-oauth/mcp-oauth-constants-model.d.ts +2 -0
  144. package/server/mcp-oauth/mcp-oauth-constants-model.js +6 -0
  145. package/server/mcp-oauth/mcp-oauth-constants-model.js.map +1 -0
  146. package/server/mcp-oauth/mcp-oauth-controller.d.ts +66 -0
  147. package/server/mcp-oauth/mcp-oauth-controller.js +166 -0
  148. package/server/mcp-oauth/mcp-oauth-controller.js.map +1 -0
  149. package/server/mcp-oauth/mcp-oauth-module.d.ts +2 -0
  150. package/server/mcp-oauth/mcp-oauth-module.js +27 -0
  151. package/server/mcp-oauth/mcp-oauth-module.js.map +1 -0
  152. package/server/mcp-oauth/mcp-oauth-schema.d.ts +112 -0
  153. package/server/mcp-oauth/mcp-oauth-schema.js +148 -0
  154. package/server/mcp-oauth/mcp-oauth-schema.js.map +1 -0
  155. package/server/mcp-oauth/mcp-oauth-service.d.ts +189 -0
  156. package/server/mcp-oauth/mcp-oauth-service.js +545 -0
  157. package/server/mcp-oauth/mcp-oauth-service.js.map +1 -0
  158. package/server/memory/memory-connect-provider.d.ts +200 -0
  159. package/server/memory/memory-connect-provider.js +26 -0
  160. package/server/memory/memory-connect-provider.js.map +1 -0
  161. package/server/memory/memory-constants-model.d.ts +6 -0
  162. package/server/memory/memory-constants-model.js +10 -0
  163. package/server/memory/memory-constants-model.js.map +1 -0
  164. package/server/memory/memory-controller.d.ts +15 -0
  165. package/server/memory/memory-controller.js +53 -0
  166. package/server/memory/memory-controller.js.map +1 -0
  167. package/server/memory/memory-history-schema.d.ts +48 -0
  168. package/server/memory/memory-history-schema.js +62 -0
  169. package/server/memory/memory-history-schema.js.map +1 -0
  170. package/server/memory/memory-module.d.ts +2 -0
  171. package/server/memory/memory-module.js +26 -0
  172. package/server/memory/memory-module.js.map +1 -0
  173. package/server/memory/memory-schema.d.ts +48 -0
  174. package/server/memory/memory-schema.js +62 -0
  175. package/server/memory/memory-schema.js.map +1 -0
  176. package/server/memory/memory-service.d.ts +134 -0
  177. package/server/memory/memory-service.js +317 -0
  178. package/server/memory/memory-service.js.map +1 -0
  179. package/server/memory/memory-trace-history-schema.d.ts +48 -0
  180. package/server/memory/memory-trace-history-schema.js +62 -0
  181. package/server/memory/memory-trace-history-schema.js.map +1 -0
  182. package/server/observability/observability.d.ts +3 -0
  183. package/server/observability/observability.js +62 -0
  184. package/server/observability/observability.js.map +1 -0
  185. package/server/production-guard.d.ts +9 -0
  186. package/server/production-guard.js +105 -0
  187. package/server/production-guard.js.map +1 -0
  188. package/server/provider-config/provider-config-connect-provider.d.ts +44 -0
  189. package/server/provider-config/provider-config-connect-provider.js +14 -0
  190. package/server/provider-config/provider-config-connect-provider.js.map +1 -0
  191. package/server/provider-config/provider-config-constants-model.d.ts +3 -0
  192. package/server/provider-config/provider-config-constants-model.js +7 -0
  193. package/server/provider-config/provider-config-constants-model.js.map +1 -0
  194. package/server/provider-config/provider-config-controller.d.ts +23 -0
  195. package/server/provider-config/provider-config-controller.js +80 -0
  196. package/server/provider-config/provider-config-controller.js.map +1 -0
  197. package/server/provider-config/provider-config-module.d.ts +2 -0
  198. package/server/provider-config/provider-config-module.js +27 -0
  199. package/server/provider-config/provider-config-module.js.map +1 -0
  200. package/server/provider-config/provider-config-schema.d.ts +32 -0
  201. package/server/provider-config/provider-config-schema.js +46 -0
  202. package/server/provider-config/provider-config-schema.js.map +1 -0
  203. package/server/provider-config/provider-config-service.d.ts +178 -0
  204. package/server/provider-config/provider-config-service.js +689 -0
  205. package/server/provider-config/provider-config-service.js.map +1 -0
  206. package/server/queue/queue-job-connect-provider.d.ts +128 -0
  207. package/server/queue/queue-job-connect-provider.js +14 -0
  208. package/server/queue/queue-job-connect-provider.js.map +1 -0
  209. package/server/queue/queue-job-constants-model.d.ts +2 -0
  210. package/server/queue/queue-job-constants-model.js +6 -0
  211. package/server/queue/queue-job-constants-model.js.map +1 -0
  212. package/server/queue/queue-job-schema.d.ts +88 -0
  213. package/server/queue/queue-job-schema.js +119 -0
  214. package/server/queue/queue-job-schema.js.map +1 -0
  215. package/server/queue/queue-lock-connect-provider.d.ts +44 -0
  216. package/server/queue/queue-lock-connect-provider.js +14 -0
  217. package/server/queue/queue-lock-connect-provider.js.map +1 -0
  218. package/server/queue/queue-lock-constants-model.d.ts +2 -0
  219. package/server/queue/queue-lock-constants-model.js +6 -0
  220. package/server/queue/queue-lock-constants-model.js.map +1 -0
  221. package/server/queue/queue-lock-schema.d.ts +32 -0
  222. package/server/queue/queue-lock-schema.js +47 -0
  223. package/server/queue/queue-lock-schema.js.map +1 -0
  224. package/server/queue/queue-message-dedupe-connect-provider.d.ts +116 -0
  225. package/server/queue/queue-message-dedupe-connect-provider.js +14 -0
  226. package/server/queue/queue-message-dedupe-connect-provider.js.map +1 -0
  227. package/server/queue/queue-message-dedupe-constants-model.d.ts +2 -0
  228. package/server/queue/queue-message-dedupe-constants-model.js +6 -0
  229. package/server/queue/queue-message-dedupe-constants-model.js.map +1 -0
  230. package/server/queue/queue-message-dedupe-schema.d.ts +80 -0
  231. package/server/queue/queue-message-dedupe-schema.js +108 -0
  232. package/server/queue/queue-message-dedupe-schema.js.map +1 -0
  233. package/server/queue/queue-module.d.ts +2 -0
  234. package/server/queue/queue-module.js +33 -0
  235. package/server/queue/queue-module.js.map +1 -0
  236. package/server/queue/queue-rate-limit-connect-provider.d.ts +56 -0
  237. package/server/queue/queue-rate-limit-connect-provider.js +14 -0
  238. package/server/queue/queue-rate-limit-connect-provider.js.map +1 -0
  239. package/server/queue/queue-rate-limit-constants-model.d.ts +2 -0
  240. package/server/queue/queue-rate-limit-constants-model.js +6 -0
  241. package/server/queue/queue-rate-limit-constants-model.js.map +1 -0
  242. package/server/queue/queue-rate-limit-schema.d.ts +40 -0
  243. package/server/queue/queue-rate-limit-schema.js +57 -0
  244. package/server/queue/queue-rate-limit-schema.js.map +1 -0
  245. package/server/queue/sqs-transition-service.d.ts +123 -0
  246. package/server/queue/sqs-transition-service.js +442 -0
  247. package/server/queue/sqs-transition-service.js.map +1 -0
  248. package/server/rag/rag-controller.d.ts +167 -0
  249. package/server/rag/rag-controller.js +232 -0
  250. package/server/rag/rag-controller.js.map +1 -0
  251. package/server/rag/rag-module.d.ts +2 -0
  252. package/server/rag/rag-module.js +30 -0
  253. package/server/rag/rag-module.js.map +1 -0
  254. package/server/rag/rag-service.d.ts +361 -0
  255. package/server/rag/rag-service.js +2864 -0
  256. package/server/rag/rag-service.js.map +1 -0
  257. package/server/runner/flow-templates.d.ts +55 -0
  258. package/server/runner/flow-templates.js +388 -0
  259. package/server/runner/flow-templates.js.map +1 -0
  260. package/server/runner/langgraph-runtime.service.d.ts +77 -0
  261. package/server/runner/langgraph-runtime.service.js +221 -0
  262. package/server/runner/langgraph-runtime.service.js.map +1 -0
  263. package/server/runner/runner-controller.d.ts +1044 -0
  264. package/server/runner/runner-controller.js +751 -0
  265. package/server/runner/runner-controller.js.map +1 -0
  266. package/server/runner/runner-module.d.ts +2 -0
  267. package/server/runner/runner-module.js +37 -0
  268. package/server/runner/runner-module.js.map +1 -0
  269. package/server/runner/runner-queue-processor.d.ts +29 -0
  270. package/server/runner/runner-queue-processor.js +259 -0
  271. package/server/runner/runner-queue-processor.js.map +1 -0
  272. package/server/runner/runner-service.d.ts +1761 -0
  273. package/server/runner/runner-service.js +14256 -0
  274. package/server/runner/runner-service.js.map +1 -0
  275. package/server/scripts/migrate-canvas-flow-versions.d.ts +1 -0
  276. package/server/scripts/migrate-canvas-flow-versions.js +72 -0
  277. package/server/scripts/migrate-canvas-flow-versions.js.map +1 -0
  278. package/server/scripts/migrate-mcp-oauth-user-scope.d.ts +1 -0
  279. package/server/scripts/migrate-mcp-oauth-user-scope.js +95 -0
  280. package/server/scripts/migrate-mcp-oauth-user-scope.js.map +1 -0
  281. package/templates/config.example.json +204 -0
  282. package/templates/config.production.example.json +206 -0
  283. package/templates/docker-compose.yml +60 -0
@@ -0,0 +1,1681 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
12
+ return function (target, key) { decorator(target, key, paramIndex); }
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.CanvasFlowService = void 0;
16
+ const common_1 = require("@nestjs/common");
17
+ const mongoose_1 = require("mongoose");
18
+ const canvas_flow_constants_model_1 = require("./canvas-flow-constants-model");
19
+ let CanvasFlowService = class CanvasFlowService {
20
+ constructor(model, agentModel, versionModel) {
21
+ this.model = model;
22
+ this.agentModel = agentModel;
23
+ this.versionModel = versionModel;
24
+ }
25
+ normalizeAgentName(value) {
26
+ return String(value || '').trim() || 'default-agent';
27
+ }
28
+ createAgentIdSlug(value) {
29
+ const ascii = String(value || '')
30
+ .trim()
31
+ .normalize('NFD')
32
+ .replace(/[\u0300-\u036f]/g, '');
33
+ const slug = ascii
34
+ .toLowerCase()
35
+ .replace(/[^a-z0-9_-]+/g, '-')
36
+ .replace(/-+/g, '-')
37
+ .replace(/^[-_]+|[-_]+$/g, '');
38
+ return slug || 'default-agent';
39
+ }
40
+ normalizeAgentDisplayName(value, fallback) {
41
+ return String(value || fallback || '').trim() || 'Agente';
42
+ }
43
+ async nextAvailableAgentId(baseAgentId, organizationId) {
44
+ const base = this.normalizeAgentName(baseAgentId);
45
+ for (let index = 0; index < 1000; index += 1) {
46
+ const candidate = index === 0 ? base : `${base}-${index + 1}`;
47
+ const query = this.withOrganization({ agentId: candidate }, organizationId);
48
+ const [agent, flow] = await Promise.all([
49
+ this.agentModel.findOne(query).select('_id').lean().exec(),
50
+ this.model.findOne(query).select('_id').lean().exec(),
51
+ ]);
52
+ if (!agent && !flow)
53
+ return candidate;
54
+ }
55
+ return `${base}-${Date.now()}`;
56
+ }
57
+ async unsetOtherMainFlows(flow) {
58
+ if (flow?.config?.isMainFlow !== true)
59
+ return;
60
+ const query = {
61
+ _id: { $ne: flow._id },
62
+ 'config.isMainFlow': true,
63
+ };
64
+ if (flow.agentId)
65
+ query.agentId = flow.agentId;
66
+ if (flow.organizationId)
67
+ query.organizationId = flow.organizationId;
68
+ if (flow.config?.channel)
69
+ query['config.channel'] = flow.config.channel;
70
+ await this.model.updateMany(query, { $set: { 'config.isMainFlow': false } }).exec();
71
+ }
72
+ withOrganization(query, organizationId) {
73
+ if (organizationId)
74
+ query.organizationId = organizationId;
75
+ return query;
76
+ }
77
+ scopedQuery(agentId, organizationId) {
78
+ return this.withOrganization(agentId ? { agentId } : {}, organizationId);
79
+ }
80
+ agentQuery(agentId, organizationId) {
81
+ const query = {};
82
+ if (agentId) {
83
+ const normalized = this.normalizeAgentName(agentId);
84
+ query.$or = [
85
+ { agentId: normalized },
86
+ { name: normalized, $or: [{ agentId: { $exists: false } }, { agentId: '' }, { agentId: null }] },
87
+ ];
88
+ }
89
+ if (organizationId)
90
+ query.organizationId = organizationId;
91
+ return query;
92
+ }
93
+ agentDisplayNameQuery(name, organizationId, excludeAgentId) {
94
+ const query = { name: this.normalizeAgentDisplayName(name) };
95
+ if (organizationId)
96
+ query.organizationId = organizationId;
97
+ if (excludeAgentId)
98
+ query.agentId = { $ne: this.normalizeAgentName(excludeAgentId) };
99
+ return query;
100
+ }
101
+ async ensureAgent(agentId, auth, displayName) {
102
+ const id = this.normalizeAgentName(agentId);
103
+ const name = this.normalizeAgentDisplayName(displayName, id);
104
+ const sortOrder = await this.nextAgentSortOrder(auth?.organizationId);
105
+ await this.agentModel.updateOne(this.agentQuery(id, auth?.organizationId), {
106
+ $set: {
107
+ agentId: id,
108
+ },
109
+ $setOnInsert: {
110
+ name,
111
+ sortOrder,
112
+ ...(auth?.organizationId ? { organizationId: auth.organizationId } : {}),
113
+ ...(auth?.userId ? { createdBy: auth.userId } : {}),
114
+ },
115
+ }, { upsert: true }).exec();
116
+ }
117
+ sortFlows(flows) {
118
+ return flows.sort((a, b) => {
119
+ const orderA = Number.isFinite(Number(a.sortOrder)) ? Number(a.sortOrder) : Number.MAX_SAFE_INTEGER;
120
+ const orderB = Number.isFinite(Number(b.sortOrder)) ? Number(b.sortOrder) : Number.MAX_SAFE_INTEGER;
121
+ if (orderA !== orderB)
122
+ return orderA - orderB;
123
+ return new Date(b.updatedAt || 0).getTime() - new Date(a.updatedAt || 0).getTime();
124
+ });
125
+ }
126
+ sortAgents(agents) {
127
+ return agents.sort((a, b) => {
128
+ const orderA = Number.isFinite(Number(a.sortOrder)) ? Number(a.sortOrder) : Number.MAX_SAFE_INTEGER;
129
+ const orderB = Number.isFinite(Number(b.sortOrder)) ? Number(b.sortOrder) : Number.MAX_SAFE_INTEGER;
130
+ if (orderA !== orderB)
131
+ return orderA - orderB;
132
+ return String(a.name || '').localeCompare(String(b.name || ''));
133
+ });
134
+ }
135
+ cloneJson(value) {
136
+ if (value === undefined || value === null)
137
+ return value;
138
+ return JSON.parse(JSON.stringify(value));
139
+ }
140
+ isPlainObject(value) {
141
+ return Boolean(value && typeof value === 'object' && !Array.isArray(value));
142
+ }
143
+ normalizeObjectList(value) {
144
+ if (!Array.isArray(value))
145
+ return [];
146
+ return value
147
+ .filter((item) => this.isPlainObject(item))
148
+ .map((item) => this.cloneJson(item));
149
+ }
150
+ normalizeBlockedTerms(value) {
151
+ if (Array.isArray(value)) {
152
+ return value.map((item) => String(item || '').trim()).filter(Boolean);
153
+ }
154
+ return String(value || '')
155
+ .split(',')
156
+ .map((item) => item.trim())
157
+ .filter(Boolean);
158
+ }
159
+ normalizeAgentRuntimeConfig(value) {
160
+ const input = this.isPlainObject(value) ? value : {};
161
+ const provider = String(input.llmProvider || '').trim();
162
+ const allowedProviders = new Set(['openai', 'azure_openai', 'azure', 'gemini', 'claude', 'grok', 'bedrock']);
163
+ const spec = this.isPlainObject(input.agentSpec) ? input.agentSpec : {};
164
+ return {
165
+ ...(String(input.model || '').trim() ? { model: String(input.model || '').trim() } : {}),
166
+ ...(allowedProviders.has(provider) ? { llmProvider: provider } : {}),
167
+ agentSpec: {
168
+ agentsMd: String(spec.agentsMd || ''),
169
+ guardrails: String(spec.guardrails || ''),
170
+ blockedTerms: this.normalizeBlockedTerms(spec.blockedTerms),
171
+ rules: this.normalizeObjectList(spec.rules),
172
+ skills: this.normalizeObjectList(spec.skills),
173
+ subagents: this.normalizeObjectList(spec.subagents),
174
+ mcpServers: this.normalizeObjectList(spec.mcpServers),
175
+ },
176
+ };
177
+ }
178
+ safeWorkspaceFileName(value, fallback) {
179
+ const source = String(value || fallback || '').trim() || fallback;
180
+ return this.createAgentIdSlug(source).replace(/\.(rule|skill|agent|json|md)$/i, '') || fallback;
181
+ }
182
+ workspaceJson(value) {
183
+ return `${JSON.stringify(value ?? {}, null, 2)}\n`;
184
+ }
185
+ workspaceFile(path, content, type = 'text/plain') {
186
+ return {
187
+ path,
188
+ content: typeof content === 'string' ? content : this.workspaceJson(content),
189
+ type,
190
+ encoding: 'utf8',
191
+ };
192
+ }
193
+ normalizeWorkspacePath(path) {
194
+ return String(path || '')
195
+ .replace(/\\/g, '/')
196
+ .replace(/^\/+/, '')
197
+ .toLowerCase();
198
+ }
199
+ workspaceFilesFromPayload(payload) {
200
+ const source = this.isPlainObject(payload?.workspace) ? payload.workspace : payload;
201
+ const rawFiles = Array.isArray(source?.files)
202
+ ? source.files
203
+ : Array.isArray(payload?.files)
204
+ ? payload.files
205
+ : [];
206
+ const objectFiles = !rawFiles.length && this.isPlainObject(source?.files)
207
+ ? Object.entries(source.files).map(([path, content]) => ({ path, content }))
208
+ : [];
209
+ return [...rawFiles, ...objectFiles]
210
+ .map((file) => ({
211
+ path: String(file?.path || '').trim(),
212
+ content: typeof file?.content === 'string'
213
+ ? file.content
214
+ : file?.content === undefined || file?.content === null
215
+ ? ''
216
+ : this.workspaceJson(file.content),
217
+ type: String(file?.type || '').trim() || undefined,
218
+ encoding: String(file?.encoding || '').trim() || undefined,
219
+ }))
220
+ .filter((file) => file.path);
221
+ }
222
+ readWorkspaceFile(files, path) {
223
+ const target = this.normalizeWorkspacePath(path);
224
+ return files.find((file) => this.normalizeWorkspacePath(file.path) === target)?.content;
225
+ }
226
+ readWorkspaceFolder(files, folder) {
227
+ const target = this.normalizeWorkspacePath(folder).replace(/\/?$/, '/');
228
+ return files
229
+ .filter((file) => {
230
+ const path = this.normalizeWorkspacePath(file.path);
231
+ return path.startsWith(target) && path.endsWith('.json');
232
+ })
233
+ .sort((a, b) => this.normalizeWorkspacePath(a.path).localeCompare(this.normalizeWorkspacePath(b.path)));
234
+ }
235
+ parseWorkspaceJson(content, fallback) {
236
+ if (content === undefined || content === null || content === '')
237
+ return fallback;
238
+ if (typeof content !== 'string')
239
+ return content;
240
+ try {
241
+ return JSON.parse(content);
242
+ }
243
+ catch {
244
+ return fallback;
245
+ }
246
+ }
247
+ normalizeWorkspaceLoadMode(value, fallback = 'auto') {
248
+ const mode = String(value || '').trim();
249
+ if (mode === 'always' || mode === 'auto' || mode === 'on_demand' || mode === 'manual')
250
+ return mode;
251
+ return fallback;
252
+ }
253
+ catalogItemId(item, fallback) {
254
+ return String(item?.id || item?.key || item?.name || item?.label || fallback).trim() || fallback;
255
+ }
256
+ catalogItemName(item, fallback) {
257
+ return String(item?.name || item?.label || item?.title || this.catalogItemId(item, fallback)).trim() || fallback;
258
+ }
259
+ catalogItemDescription(item) {
260
+ return String(item?.description || item?.role || item?.instructions || item?.instruction || item?.action || '').trim();
261
+ }
262
+ buildWorkspaceManifest(config) {
263
+ const spec = config.agentSpec || {};
264
+ const toEntries = (items, kind, fallbackLoad) => (Array.isArray(items) ? items : []).filter((item) => this.isPlainObject(item)).map((item, index) => ({
265
+ id: this.catalogItemId(item, `${kind}-${index + 1}`),
266
+ name: this.catalogItemName(item, `${kind}-${index + 1}`),
267
+ description: this.catalogItemDescription(item),
268
+ path: String(item.path || '').trim() || (kind === 'skill'
269
+ ? `.canvas-flow/skills/${this.safeWorkspaceFileName(item.id || item.name, `skill-${index + 1}`)}/SKILL.md`
270
+ : kind === 'subagent'
271
+ ? `.canvas-flow/subagents/${this.safeWorkspaceFileName(item.id || item.name, `subagent-${index + 1}`)}.agent.md`
272
+ : kind === 'mcp'
273
+ ? '.canvas-flow/mcp.json'
274
+ : `.canvas-flow/rules/${this.safeWorkspaceFileName(item.id || item.name, `rule-${index + 1}`)}.rule.json`),
275
+ load: this.normalizeWorkspaceLoadMode(item.load || item.loadMode, fallbackLoad),
276
+ enabled: item.enabled !== false,
277
+ }));
278
+ return {
279
+ version: 1,
280
+ agentsMd: { path: '.canvas-flow/agents.md', load: 'always' },
281
+ guardrails: { path: '.canvas-flow/guardrails.md', load: 'always' },
282
+ rules: toEntries(spec.rules, 'rule', 'always'),
283
+ skills: toEntries(spec.skills, 'skill', 'auto'),
284
+ subagents: toEntries(spec.subagents, 'subagent', 'auto'),
285
+ mcpServers: toEntries(spec.mcpServers, 'mcp', 'on_demand'),
286
+ };
287
+ }
288
+ applyWorkspaceManifestList(list, manifestEntries, kind, fallbackLoad) {
289
+ const current = Array.isArray(list) ? list.filter((item) => this.isPlainObject(item)).map((item) => this.cloneJson(item)) : [];
290
+ const entries = Array.isArray(manifestEntries) ? manifestEntries.filter((item) => this.isPlainObject(item)) : [];
291
+ if (!entries.length)
292
+ return current.map((item, index) => ({
293
+ ...item,
294
+ id: this.catalogItemId(item, `${kind}-${index + 1}`),
295
+ load: this.normalizeWorkspaceLoadMode(item.load || item.loadMode, fallbackLoad),
296
+ }));
297
+ const byId = new Map(current.map((item, index) => [this.catalogItemId(item, `${kind}-${index + 1}`), item]));
298
+ entries.forEach((entry, index) => {
299
+ const id = this.catalogItemId(entry, `${kind}-${index + 1}`);
300
+ const existing = byId.get(id) || current.find((item) => String(item.path || '').trim() && String(item.path || '').trim() === String(entry.path || '').trim());
301
+ const merged = {
302
+ ...(existing || {}),
303
+ ...entry,
304
+ id,
305
+ name: this.catalogItemName(entry, this.catalogItemName(existing, id)),
306
+ description: this.catalogItemDescription(entry) || this.catalogItemDescription(existing),
307
+ load: this.normalizeWorkspaceLoadMode(entry.load || existing?.load, fallbackLoad),
308
+ enabled: entry.enabled !== false && existing?.enabled !== false,
309
+ };
310
+ if (existing) {
311
+ const existingIndex = current.indexOf(existing);
312
+ current[existingIndex] = merged;
313
+ }
314
+ else {
315
+ current.push(merged);
316
+ }
317
+ byId.set(id, merged);
318
+ });
319
+ return current;
320
+ }
321
+ applyWorkspaceManifestToSpec(spec, manifest) {
322
+ if (!this.isPlainObject(manifest))
323
+ return spec;
324
+ return {
325
+ ...spec,
326
+ rules: this.applyWorkspaceManifestList(spec.rules, manifest.rules, 'rule', 'always'),
327
+ skills: this.applyWorkspaceManifestList(spec.skills, manifest.skills, 'skill', 'auto'),
328
+ subagents: this.applyWorkspaceManifestList(spec.subagents, manifest.subagents, 'subagent', 'auto'),
329
+ mcpServers: this.applyWorkspaceManifestList(spec.mcpServers, manifest.mcpServers, 'mcp', 'on_demand'),
330
+ };
331
+ }
332
+ workspaceConfigFromPayload(payload) {
333
+ const source = this.isPlainObject(payload?.workspace) ? payload.workspace : payload;
334
+ const files = this.workspaceFilesFromPayload(payload);
335
+ const configSource = this.isPlainObject(source?.config)
336
+ ? source.config
337
+ : this.isPlainObject(payload?.config)
338
+ ? payload.config
339
+ : {};
340
+ const base = this.normalizeAgentRuntimeConfig(configSource);
341
+ const agentJson = this.parseWorkspaceJson(this.readWorkspaceFile(files, '.canvas-flow/agent.json'), {});
342
+ const agentsMd = this.readWorkspaceFile(files, '.canvas-flow/agents.md');
343
+ const guardrails = this.readWorkspaceFile(files, '.canvas-flow/guardrails.md');
344
+ const blockedTermsFile = this.readWorkspaceFile(files, '.canvas-flow/blocked-terms.json');
345
+ const rulesFile = this.readWorkspaceFile(files, '.canvas-flow/rules.json');
346
+ const skillsFile = this.readWorkspaceFile(files, '.canvas-flow/skills.json');
347
+ const subagentsFile = this.readWorkspaceFile(files, '.canvas-flow/subagents.json');
348
+ const mcpFile = this.readWorkspaceFile(files, '.canvas-flow/mcp.json');
349
+ const manifestFile = this.readWorkspaceFile(files, '.canvas-flow/manifest.json');
350
+ const rulesFromFolder = this.readWorkspaceFolder(files, '.canvas-flow/rules')
351
+ .map((file) => this.parseWorkspaceJson(file.content, null))
352
+ .filter((item) => this.isPlainObject(item));
353
+ const skillsFromFolder = this.readWorkspaceFolder(files, '.canvas-flow/skills')
354
+ .map((file) => this.parseWorkspaceJson(file.content, null))
355
+ .filter((item) => this.isPlainObject(item));
356
+ const subagentsFromFolder = this.readWorkspaceFolder(files, '.canvas-flow/subagents')
357
+ .map((file) => this.parseWorkspaceJson(file.content, null))
358
+ .filter((item) => this.isPlainObject(item));
359
+ const nextSpec = {
360
+ ...(base.agentSpec || {}),
361
+ ...(agentsMd !== undefined ? { agentsMd } : {}),
362
+ ...(guardrails !== undefined ? { guardrails } : {}),
363
+ ...(blockedTermsFile !== undefined ? { blockedTerms: this.parseWorkspaceJson(blockedTermsFile, blockedTermsFile) } : {}),
364
+ ...(rulesFromFolder.length ? { rules: rulesFromFolder } : rulesFile !== undefined ? { rules: this.parseWorkspaceJson(rulesFile, []) } : {}),
365
+ ...(skillsFromFolder.length ? { skills: skillsFromFolder } : skillsFile !== undefined ? { skills: this.parseWorkspaceJson(skillsFile, []) } : {}),
366
+ ...(subagentsFromFolder.length ? { subagents: subagentsFromFolder } : subagentsFile !== undefined ? { subagents: this.parseWorkspaceJson(subagentsFile, []) } : {}),
367
+ ...(mcpFile !== undefined ? { mcpServers: this.parseWorkspaceJson(mcpFile, []) } : {}),
368
+ };
369
+ const specWithManifest = this.applyWorkspaceManifestToSpec(nextSpec, this.parseWorkspaceJson(manifestFile, {}));
370
+ return this.normalizeAgentRuntimeConfig({
371
+ ...base,
372
+ model: String(agentJson?.model || base.model || '').trim() || base.model,
373
+ llmProvider: String(agentJson?.llmProvider || base.llmProvider || '').trim() || base.llmProvider,
374
+ agentSpec: specWithManifest,
375
+ });
376
+ }
377
+ normalizeVersionValue(value) {
378
+ if (value === undefined || value === null || value === '')
379
+ return undefined;
380
+ if (String(value).trim().toLowerCase() === 'active')
381
+ return undefined;
382
+ const version = Number(value);
383
+ return Number.isInteger(version) && version > 0 ? version : undefined;
384
+ }
385
+ async updateActiveAgentReleaseFlowVersion(flow, version, auth, timestamp = new Date().toISOString()) {
386
+ const agentId = this.normalizeAgentName(flow?.agentId);
387
+ const flowId = flow?._id ? String(flow._id) : '';
388
+ if (!flowId || !agentId || agentId === 'default-agent' || !version)
389
+ return;
390
+ const organizationId = flow?.organizationId || auth?.organizationId;
391
+ const agent = await this.agentModel.findOne(this.agentQuery(agentId, organizationId)).lean().exec();
392
+ const activeRelease = this.normalizeVersionValue(agent?.activeRelease);
393
+ if (!activeRelease)
394
+ return;
395
+ const flowName = flow?.name || flow?.config?.title || flowId;
396
+ await this.agentModel.findOneAndUpdate({ ...this.agentQuery(agentId, organizationId), 'releases.release': activeRelease }, {
397
+ $set: {
398
+ [`releases.$.versions.${flowId}`]: version,
399
+ [`releases.$.flowNames.${flowId}`]: flowName,
400
+ 'releases.$.updatedAt': timestamp,
401
+ 'releases.$.deployedAt': timestamp,
402
+ 'releases.$.deployedBy': auth?.userId || '',
403
+ 'releases.$.deployedByEmail': auth?.userEmail || '',
404
+ },
405
+ }).exec();
406
+ }
407
+ flowVersions(flow) {
408
+ return Array.isArray(flow?.versions) ? flow.versions : [];
409
+ }
410
+ flowId(flow) {
411
+ return String(flow?._id || flow?.flowId || flow || '').trim();
412
+ }
413
+ flowVersionQuery(flowOrId, organizationId, version) {
414
+ const query = { flowId: this.flowId(flowOrId) };
415
+ if (organizationId)
416
+ query.organizationId = organizationId;
417
+ if (version)
418
+ query.version = version;
419
+ return query;
420
+ }
421
+ flowObjectId(id) {
422
+ const value = String(id || '').trim();
423
+ return mongoose_1.Types.ObjectId.isValid(value) ? new mongoose_1.Types.ObjectId(value) : value;
424
+ }
425
+ versionRecordFromFlow(flow, version) {
426
+ const flowId = this.flowId(flow);
427
+ const versionNumber = Number(version?.version || 0);
428
+ const record = this.cloneJson(version || {});
429
+ delete record._id;
430
+ delete record.createdAt;
431
+ delete record.updatedAt;
432
+ return {
433
+ ...record,
434
+ flowId,
435
+ agentId: flow?.agentId || version?.agentId || '',
436
+ ...(flow?.organizationId || version?.organizationId ? { organizationId: flow?.organizationId || version?.organizationId } : {}),
437
+ version: versionNumber,
438
+ config: this.cloneJson(version?.config || {}),
439
+ };
440
+ }
441
+ async aggregateVersionRecords(query, includeBsonSize = true) {
442
+ if (!query.flowId)
443
+ return [];
444
+ if (includeBsonSize) {
445
+ try {
446
+ return await this.versionModel.aggregate([
447
+ { $match: query },
448
+ { $addFields: { bsonSizeBytes: { $bsonSize: '$$ROOT' } } },
449
+ { $sort: { version: -1 } },
450
+ ]).exec();
451
+ }
452
+ catch {
453
+ }
454
+ }
455
+ return await this.versionModel.find(query).sort({ version: -1 }).lean().exec();
456
+ }
457
+ async flowDocumentBsonSize(id, organizationId) {
458
+ const match = this.withOrganization({ _id: this.flowObjectId(id) }, organizationId);
459
+ try {
460
+ const [result] = await this.model.aggregate([
461
+ { $match: match },
462
+ { $project: { _id: 0, bsonSizeBytes: { $bsonSize: '$$ROOT' } } },
463
+ ]).exec();
464
+ const size = Number(result?.bsonSizeBytes);
465
+ return Number.isFinite(size) ? size : undefined;
466
+ }
467
+ catch {
468
+ return undefined;
469
+ }
470
+ }
471
+ async versionDocumentsBsonSize(flowOrId, organizationId) {
472
+ const query = this.flowVersionQuery(flowOrId, organizationId);
473
+ if (!query.flowId)
474
+ return undefined;
475
+ try {
476
+ const [result] = await this.versionModel.aggregate([
477
+ { $match: query },
478
+ { $group: { _id: null, bsonSizeBytes: { $sum: { $bsonSize: '$$ROOT' } } } },
479
+ ]).exec();
480
+ const size = Number(result?.bsonSizeBytes);
481
+ return Number.isFinite(size) ? size : undefined;
482
+ }
483
+ catch {
484
+ return undefined;
485
+ }
486
+ }
487
+ async migrateEmbeddedVersions(flow, organizationId, options) {
488
+ const embedded = this.flowVersions(flow).filter((version) => Number(version?.version || 0) > 0);
489
+ const flowId = this.flowId(flow);
490
+ if (!flowId || !embedded.length)
491
+ return { embeddedCount: 0, upsertedCount: 0 };
492
+ const operations = embedded.map((version) => {
493
+ const versionNumber = Number(version?.version);
494
+ return {
495
+ updateOne: {
496
+ filter: this.flowVersionQuery(flowId, organizationId || flow?.organizationId, versionNumber),
497
+ update: {
498
+ $setOnInsert: this.versionRecordFromFlow(flow, version),
499
+ },
500
+ upsert: true,
501
+ },
502
+ };
503
+ });
504
+ if (options?.dryRun) {
505
+ return { embeddedCount: embedded.length, upsertedCount: 0 };
506
+ }
507
+ let result;
508
+ if (operations.length) {
509
+ result = await this.versionModel.bulkWrite(operations, { ordered: false });
510
+ }
511
+ if (!options?.keepLegacy) {
512
+ await this.model.updateOne(this.withOrganization({ _id: flow._id }, organizationId || flow?.organizationId), { $unset: { versions: '' } }).exec().catch(() => undefined);
513
+ }
514
+ return {
515
+ embeddedCount: embedded.length,
516
+ upsertedCount: Number(result?.upsertedCount || result?.nUpserted || 0),
517
+ };
518
+ }
519
+ async loadFlowForVersionAccess(id, organizationId) {
520
+ const flow = await this.model
521
+ .findOne(this.withOrganization({ _id: id }, organizationId))
522
+ .select('+versions')
523
+ .lean()
524
+ .exec();
525
+ if (!flow) {
526
+ throw new common_1.HttpException('Canvas flow not found', common_1.HttpStatus.NOT_FOUND);
527
+ }
528
+ return flow;
529
+ }
530
+ async findFlowVersions(flow, organizationId, includeBsonSize = true) {
531
+ const flowId = this.flowId(flow);
532
+ if (!flowId)
533
+ return [];
534
+ const legacyFlow = this.flowVersions(flow).length
535
+ ? flow
536
+ : await this.model
537
+ .findOne(this.withOrganization({ _id: flowId }, organizationId || flow?.organizationId))
538
+ .select('+versions')
539
+ .lean()
540
+ .exec()
541
+ .catch(() => null);
542
+ if (this.flowVersions(legacyFlow).length) {
543
+ await this.migrateEmbeddedVersions(legacyFlow, organizationId || flow?.organizationId);
544
+ }
545
+ return await this.aggregateVersionRecords(this.flowVersionQuery(flowId, organizationId || flow?.organizationId), includeBsonSize);
546
+ }
547
+ async findFlowVersion(flow, version, organizationId) {
548
+ if (!version)
549
+ return undefined;
550
+ const query = this.flowVersionQuery(flow, organizationId || flow?.organizationId, version);
551
+ let record = await this.versionModel.findOne(query).lean().exec();
552
+ if (record)
553
+ return record;
554
+ const versions = await this.findFlowVersions(flow, organizationId || flow?.organizationId, false);
555
+ record = versions.find((item) => Number(item?.version) === version);
556
+ return record;
557
+ }
558
+ latestVersionFromRecords(flow, versions) {
559
+ const fromField = Number(flow?.latestVersion);
560
+ const fromVersions = (versions || []).reduce((max, version) => Math.max(max, Number(version?.version) || 0), 0);
561
+ return Math.max(Number.isFinite(fromField) ? fromField : 0, fromVersions);
562
+ }
563
+ async latestExistingFlowVersionNumberAsync(flow, organizationId) {
564
+ const versions = await this.findFlowVersions(flow, organizationId || flow?.organizationId, false);
565
+ return versions.reduce((max, version) => Math.max(max, Number(version?.version) || 0), 0);
566
+ }
567
+ async flowWithVersions(flow, organizationId) {
568
+ const versions = await this.findFlowVersions(flow, organizationId || flow?.organizationId, true);
569
+ const bsonSizeBytes = await this.flowDocumentBsonSize(flow?._id, organizationId || flow?.organizationId);
570
+ return {
571
+ ...flow,
572
+ versions,
573
+ latestVersion: this.latestVersionFromRecords(flow, versions),
574
+ activeVersion: this.normalizeVersionValue(flow?.activeVersion),
575
+ ...(bsonSizeBytes !== undefined ? { bsonSizeBytes } : {}),
576
+ };
577
+ }
578
+ async migrateEmbeddedFlowVersions(options = {}) {
579
+ const summary = {
580
+ dryRun: !!options.dryRun,
581
+ keepLegacy: !!options.keepLegacy || !!options.dryRun,
582
+ scannedFlows: 0,
583
+ migratedFlows: 0,
584
+ skippedFlows: 0,
585
+ failedFlows: 0,
586
+ embeddedVersions: 0,
587
+ insertedVersions: 0,
588
+ legacyFlowBsonSizeBytes: 0,
589
+ postMigrationFlowBsonSizeBytes: 0,
590
+ versionBsonSizeBytes: 0,
591
+ errors: [],
592
+ };
593
+ const limit = Math.max(0, Number(options.limit) || 0);
594
+ const query = this.withOrganization({ 'versions.0': { $exists: true } }, options.organizationId);
595
+ const flowQuery = this.model
596
+ .find(query)
597
+ .select('_id name agentId organizationId latestVersion activeVersion versions')
598
+ .lean();
599
+ if (limit)
600
+ flowQuery.limit(limit);
601
+ const cursor = flowQuery.cursor();
602
+ for await (const flow of cursor) {
603
+ summary.scannedFlows += 1;
604
+ const embeddedCount = this.flowVersions(flow).filter((version) => Number(version?.version || 0) > 0).length;
605
+ if (!embeddedCount) {
606
+ summary.skippedFlows += 1;
607
+ continue;
608
+ }
609
+ const organizationId = options.organizationId || flow?.organizationId;
610
+ const flowId = this.flowId(flow);
611
+ const legacySize = await this.flowDocumentBsonSize(flow?._id, organizationId);
612
+ if (legacySize !== undefined)
613
+ summary.legacyFlowBsonSizeBytes = (summary.legacyFlowBsonSizeBytes || 0) + legacySize;
614
+ try {
615
+ const result = await this.migrateEmbeddedVersions(flow, organizationId, {
616
+ dryRun: options.dryRun,
617
+ keepLegacy: summary.keepLegacy,
618
+ });
619
+ summary.migratedFlows += 1;
620
+ summary.embeddedVersions += result.embeddedCount;
621
+ summary.insertedVersions += result.upsertedCount;
622
+ if (!options.dryRun) {
623
+ const postSize = await this.flowDocumentBsonSize(flow?._id, organizationId);
624
+ const versionSize = await this.versionDocumentsBsonSize(flow, organizationId);
625
+ if (postSize !== undefined)
626
+ summary.postMigrationFlowBsonSizeBytes = (summary.postMigrationFlowBsonSizeBytes || 0) + postSize;
627
+ if (versionSize !== undefined)
628
+ summary.versionBsonSizeBytes = (summary.versionBsonSizeBytes || 0) + versionSize;
629
+ }
630
+ }
631
+ catch (error) {
632
+ summary.failedFlows += 1;
633
+ summary.errors.push({
634
+ flowId,
635
+ message: error?.message || String(error),
636
+ });
637
+ }
638
+ }
639
+ return summary;
640
+ }
641
+ agentReleases(agent) {
642
+ return Array.isArray(agent?.releases) ? agent.releases : [];
643
+ }
644
+ async agentReleasesForResponse(agent, organizationId, existingFlowIds) {
645
+ const releases = this.agentReleases(agent);
646
+ const snapshots = new Map();
647
+ releases.forEach((release) => {
648
+ const versions = release?.versions && typeof release.versions === 'object' && !Array.isArray(release.versions)
649
+ ? release.versions
650
+ : {};
651
+ Object.entries(versions).forEach(([flowId, rawVersion]) => {
652
+ const version = Number(rawVersion);
653
+ if ((!existingFlowIds || existingFlowIds.has(flowId)) && version > 0) {
654
+ snapshots.set(`${flowId}:${version}`, { flowId, version });
655
+ }
656
+ });
657
+ });
658
+ const snapshotRecords = snapshots.size
659
+ ? await this.versionModel
660
+ .find(this.withOrganization({ $or: [...snapshots.values()] }, organizationId || agent?.organizationId))
661
+ .select('flowId version name')
662
+ .lean()
663
+ .exec()
664
+ : [];
665
+ const snapshotNames = new Map(snapshotRecords
666
+ .filter((record) => String(record?.name || '').trim())
667
+ .map((record) => [`${String(record.flowId)}:${Number(record.version)}`, String(record.name)]));
668
+ return releases
669
+ .map((release) => {
670
+ const versions = release?.versions && typeof release.versions === 'object' && !Array.isArray(release.versions)
671
+ ? release.versions
672
+ : {};
673
+ const flowNames = release?.flowNames && typeof release.flowNames === 'object' && !Array.isArray(release.flowNames)
674
+ ? release.flowNames
675
+ : {};
676
+ const storedVersionNames = release?.versionNames && typeof release.versionNames === 'object' && !Array.isArray(release.versionNames)
677
+ ? release.versionNames
678
+ : {};
679
+ const nextVersions = {};
680
+ const nextFlowNames = {};
681
+ const nextVersionNames = {};
682
+ Object.entries(versions).forEach(([flowId, rawVersion]) => {
683
+ if (existingFlowIds && !existingFlowIds.has(flowId))
684
+ return;
685
+ const version = Number(rawVersion);
686
+ nextVersions[flowId] = version;
687
+ if (flowNames[flowId])
688
+ nextFlowNames[flowId] = String(flowNames[flowId]);
689
+ const versionName = snapshotNames.get(`${flowId}:${version}`) || storedVersionNames[flowId];
690
+ if (versionName)
691
+ nextVersionNames[flowId] = String(versionName);
692
+ });
693
+ return { ...release, versions: nextVersions, flowNames: nextFlowNames, versionNames: nextVersionNames };
694
+ })
695
+ .sort((a, b) => Number(b?.release || 0) - Number(a?.release || 0));
696
+ }
697
+ latestFlowVersionNumber(flow) {
698
+ const fromField = Number(flow?.latestVersion);
699
+ const fromVersions = this.flowVersions(flow).reduce((max, version) => Math.max(max, Number(version?.version) || 0), 0);
700
+ return Math.max(Number.isFinite(fromField) ? fromField : 0, fromVersions);
701
+ }
702
+ latestExistingFlowVersionNumber(flow) {
703
+ return this.flowVersions(flow).reduce((max, version) => Math.max(max, Number(version?.version) || 0), 0);
704
+ }
705
+ latestAgentReleaseNumber(agent) {
706
+ const fromField = Number(agent?.latestRelease);
707
+ const fromReleases = this.agentReleases(agent).reduce((max, release) => Math.max(max, Number(release?.release) || 0), 0);
708
+ return Math.max(Number.isFinite(fromField) ? fromField : 0, fromReleases);
709
+ }
710
+ resolveFlowVersion(flow, requestedVersion) {
711
+ const versions = this.flowVersions(flow);
712
+ const latestVersion = this.latestFlowVersionNumber(flow);
713
+ const rawRequest = String(requestedVersion ?? '').trim().toLowerCase();
714
+ if (rawRequest === 'draft') {
715
+ return {
716
+ config: this.cloneJson(flow?.config || {}),
717
+ source: 'draft',
718
+ activeVersion: this.normalizeVersionValue(flow?.activeVersion),
719
+ latestVersion,
720
+ };
721
+ }
722
+ const requested = this.normalizeVersionValue(requestedVersion);
723
+ const active = this.normalizeVersionValue(flow?.activeVersion);
724
+ const targetVersion = requested || active;
725
+ if (targetVersion) {
726
+ const snapshot = versions.find((version) => Number(version?.version) === targetVersion);
727
+ if (!snapshot) {
728
+ throw new common_1.HttpException(`Versao ${targetVersion} do fluxo nao encontrada.`, common_1.HttpStatus.NOT_FOUND);
729
+ }
730
+ return {
731
+ config: this.cloneJson(snapshot.config || {}),
732
+ version: targetVersion,
733
+ source: 'version',
734
+ activeVersion: active,
735
+ latestVersion,
736
+ };
737
+ }
738
+ return {
739
+ config: this.cloneJson(flow?.config || {}),
740
+ source: 'draft',
741
+ activeVersion: active,
742
+ latestVersion,
743
+ };
744
+ }
745
+ async resolveFlowVersionAsync(flow, requestedVersion) {
746
+ const rawRequest = String(requestedVersion ?? '').trim().toLowerCase();
747
+ const active = this.normalizeVersionValue(flow?.activeVersion);
748
+ if (rawRequest === 'draft') {
749
+ return {
750
+ config: this.cloneJson(flow?.config || {}),
751
+ source: 'draft',
752
+ activeVersion: active,
753
+ latestVersion: Number(flow?.latestVersion) || 0,
754
+ };
755
+ }
756
+ const requested = this.normalizeVersionValue(requestedVersion);
757
+ const targetVersion = requested || active;
758
+ if (targetVersion) {
759
+ const snapshot = await this.findFlowVersion(flow, targetVersion, flow?.organizationId);
760
+ if (!snapshot) {
761
+ throw new common_1.HttpException(`Versao ${targetVersion} do fluxo nao encontrada.`, common_1.HttpStatus.NOT_FOUND);
762
+ }
763
+ return {
764
+ config: this.cloneJson(snapshot.config || {}),
765
+ version: targetVersion,
766
+ source: 'version',
767
+ activeVersion: active,
768
+ latestVersion: Math.max(Number(flow?.latestVersion) || 0, Number(snapshot.version) || 0),
769
+ };
770
+ }
771
+ return {
772
+ config: this.cloneJson(flow?.config || {}),
773
+ source: 'draft',
774
+ activeVersion: active,
775
+ latestVersion: Number(flow?.latestVersion) || 0,
776
+ };
777
+ }
778
+ async resolveAgentRelease(agentId, organizationId, requestedRelease) {
779
+ const name = this.normalizeAgentName(agentId);
780
+ const agent = await this.agentModel.findOne(this.agentQuery(name, organizationId)).lean().exec();
781
+ if (!agent)
782
+ return { versions: {}, source: 'none' };
783
+ const releases = this.agentReleases(agent);
784
+ const requested = this.normalizeVersionValue(requestedRelease);
785
+ const active = this.normalizeVersionValue(agent.activeRelease);
786
+ const target = requested || active;
787
+ if (!target) {
788
+ return { versions: {}, source: 'none', latestRelease: this.latestAgentReleaseNumber(agent) };
789
+ }
790
+ const release = releases.find((item) => Number(item?.release) === target);
791
+ if (!release) {
792
+ if (!requested && active) {
793
+ return { versions: {}, source: 'none', latestRelease: this.latestAgentReleaseNumber(agent) };
794
+ }
795
+ throw new common_1.HttpException(`Release ${target} do agente nao encontrado.`, common_1.HttpStatus.NOT_FOUND);
796
+ }
797
+ return {
798
+ release: target,
799
+ versions: release.versions && typeof release.versions === 'object' && !Array.isArray(release.versions) ? release.versions : {},
800
+ source: requested ? 'requested' : 'active',
801
+ latestRelease: this.latestAgentReleaseNumber(agent),
802
+ };
803
+ }
804
+ async nextAgentSortOrder(organizationId) {
805
+ const last = await this.agentModel
806
+ .findOne(this.withOrganization({}, organizationId))
807
+ .sort({ sortOrder: -1, updatedAt: -1 })
808
+ .select('sortOrder')
809
+ .lean()
810
+ .exec();
811
+ const current = Number(last?.sortOrder);
812
+ return Number.isFinite(current) ? current + 1000 : 1000;
813
+ }
814
+ async nextSortOrder(agentId, organizationId) {
815
+ const last = await this.model
816
+ .findOne(this.scopedQuery(agentId, organizationId))
817
+ .sort({ sortOrder: -1, updatedAt: -1 })
818
+ .select('sortOrder')
819
+ .lean()
820
+ .exec();
821
+ const current = Number(last?.sortOrder);
822
+ return Number.isFinite(current) ? current + 1000 : 1000;
823
+ }
824
+ async create(createDto, auth) {
825
+ const agentId = this.normalizeAgentName(createDto.agentId);
826
+ if (agentId !== 'default-agent') {
827
+ await this.ensureAgent(agentId, auth);
828
+ }
829
+ const initialVersions = Array.isArray(createDto.versions) ? createDto.versions : [];
830
+ const { versions: _ignoredVersions, ...flowDto } = createDto;
831
+ const initialLatestVersion = initialVersions.reduce((max, version) => Math.max(max, Number(version?.version) || 0), 0);
832
+ const saved = await new this.model({
833
+ ...flowDto,
834
+ agentId,
835
+ sortOrder: Number.isFinite(Number(createDto.sortOrder))
836
+ ? Number(createDto.sortOrder)
837
+ : await this.nextSortOrder(agentId, auth?.organizationId),
838
+ config: createDto.config || {},
839
+ latestVersion: Math.max(Number.isFinite(Number(createDto.latestVersion)) ? Number(createDto.latestVersion) : 0, initialLatestVersion),
840
+ activeVersion: Number.isFinite(Number(createDto.activeVersion)) ? Number(createDto.activeVersion) : undefined,
841
+ ...(auth?.organizationId ? { organizationId: auth.organizationId } : {}),
842
+ ...(auth?.userId ? { createdBy: auth.userId } : {}),
843
+ }).save();
844
+ if (initialVersions.length) {
845
+ const operations = initialVersions
846
+ .filter((version) => Number(version?.version || 0) > 0)
847
+ .map((version) => ({
848
+ updateOne: {
849
+ filter: this.flowVersionQuery(saved, auth?.organizationId, Number(version.version)),
850
+ update: { $setOnInsert: this.versionRecordFromFlow(saved, version) },
851
+ upsert: true,
852
+ },
853
+ }));
854
+ if (operations.length) {
855
+ await this.versionModel.bulkWrite(operations, { ordered: false });
856
+ }
857
+ }
858
+ await this.unsetOtherMainFlows(saved);
859
+ return saved;
860
+ }
861
+ async findAll(agentId, organizationId, options) {
862
+ const query = this.model.find(this.scopedQuery(agentId, organizationId)).sort({ updatedAt: -1 });
863
+ if (!options?.includeConfig) {
864
+ query.select('_id name agentId organizationId description sortOrder activeVersion latestVersion createdAt updatedAt config.title config.responseName config.channel config.isMainFlow config.execute');
865
+ }
866
+ const flows = await query.lean().exec();
867
+ return this.sortFlows(flows);
868
+ }
869
+ async listAgents(organizationId) {
870
+ const flowAgents = await this.model.aggregate([
871
+ { $match: this.withOrganization({ agentId: { $exists: true, $ne: '' } }, organizationId) },
872
+ { $group: {
873
+ _id: '$agentId',
874
+ flowCount: { $sum: 1 },
875
+ updatedAt: { $max: '$updatedAt' },
876
+ } },
877
+ ]).exec();
878
+ const explicitAgents = await this.agentModel.find(this.withOrganization({}, organizationId)).lean().exec();
879
+ const byId = new Map();
880
+ explicitAgents.forEach((agent) => {
881
+ const id = this.normalizeAgentName(agent.agentId || agent.name);
882
+ const name = this.normalizeAgentDisplayName(agent.name, id);
883
+ byId.set(id, {
884
+ _id: String(agent._id),
885
+ agentId: id,
886
+ name,
887
+ flowCount: 0,
888
+ config: this.normalizeAgentRuntimeConfig(agent.config || {}),
889
+ sortOrder: agent.sortOrder,
890
+ activeRelease: agent.activeRelease,
891
+ latestRelease: agent.latestRelease,
892
+ createdAt: agent.createdAt,
893
+ updatedAt: agent.updatedAt,
894
+ });
895
+ });
896
+ flowAgents.forEach((item) => {
897
+ const id = this.normalizeAgentName(item._id);
898
+ const current = byId.get(id);
899
+ byId.set(id, {
900
+ ...(current || { agentId: id, name: id }),
901
+ flowCount: item.flowCount || 0,
902
+ activeRelease: current?.activeRelease,
903
+ latestRelease: current?.latestRelease,
904
+ updatedAt: item.updatedAt || current?.updatedAt,
905
+ });
906
+ });
907
+ return this.sortAgents(Array.from(byId.values()));
908
+ }
909
+ async createAgent(name, auth) {
910
+ const displayName = this.normalizeAgentDisplayName(name);
911
+ const duplicate = await this.agentModel.findOne(this.agentDisplayNameQuery(displayName, auth?.organizationId)).lean().exec();
912
+ if (duplicate) {
913
+ throw new common_1.HttpException('Ja existe um agente com este nome.', common_1.HttpStatus.CONFLICT);
914
+ }
915
+ const agentId = await this.nextAvailableAgentId(this.createAgentIdSlug(displayName), auth?.organizationId);
916
+ await this.ensureAgent(agentId, auth, displayName);
917
+ return (await this.listAgents(auth?.organizationId)).find((agent) => agent.agentId === agentId);
918
+ }
919
+ async getAgentConfig(agentId, organizationId) {
920
+ const target = this.normalizeAgentName(agentId);
921
+ const agent = await this.agentModel.findOne(this.agentQuery(target, organizationId)).lean().exec();
922
+ return this.normalizeAgentRuntimeConfig(agent?.config || {});
923
+ }
924
+ async updateAgentConfig(agentId, config, auth) {
925
+ const target = this.normalizeAgentName(agentId);
926
+ if (!target || target === 'default-agent') {
927
+ throw new common_1.HttpException('Selecione um agente real antes de configurar o Agent OS.', common_1.HttpStatus.BAD_REQUEST);
928
+ }
929
+ const existing = await this.agentModel.findOne(this.agentQuery(target, auth?.organizationId)).lean().exec();
930
+ const normalized = this.normalizeAgentRuntimeConfig(config);
931
+ await this.agentModel.updateOne(this.agentQuery(target, auth?.organizationId), {
932
+ $set: {
933
+ agentId: target,
934
+ name: this.normalizeAgentDisplayName(existing?.name, target),
935
+ config: normalized,
936
+ ...(auth?.organizationId ? { organizationId: auth.organizationId } : {}),
937
+ },
938
+ $setOnInsert: {
939
+ sortOrder: await this.nextAgentSortOrder(auth?.organizationId),
940
+ ...(auth?.userId ? { createdBy: auth.userId } : {}),
941
+ },
942
+ }, { upsert: true }).exec();
943
+ return (await this.listAgents(auth?.organizationId)).find((agent) => agent.agentId === target);
944
+ }
945
+ async exportAgentWorkspace(agentId, organizationId) {
946
+ const target = this.normalizeAgentName(agentId);
947
+ if (!target || target === 'default-agent') {
948
+ throw new common_1.HttpException('Selecione um agente real para exportar o workspace.', common_1.HttpStatus.BAD_REQUEST);
949
+ }
950
+ const agent = await this.agentModel.findOne(this.agentQuery(target, organizationId)).lean().exec();
951
+ if (!agent) {
952
+ throw new common_1.HttpException('Agente nao encontrado para exportar workspace.', common_1.HttpStatus.NOT_FOUND);
953
+ }
954
+ const config = this.normalizeAgentRuntimeConfig(agent.config || {});
955
+ const spec = config.agentSpec || {};
956
+ const agentName = this.normalizeAgentDisplayName(agent.name, target);
957
+ const exportedAt = new Date().toISOString();
958
+ const rules = this.normalizeObjectList(spec.rules);
959
+ const skills = this.normalizeObjectList(spec.skills);
960
+ const subagents = this.normalizeObjectList(spec.subagents);
961
+ const mcpServers = this.normalizeObjectList(spec.mcpServers);
962
+ const files = [
963
+ this.workspaceFile('.canvas-flow/agent.json', {
964
+ kind: 'canvas-flow-agent',
965
+ version: 1,
966
+ agentId: target,
967
+ name: agentName,
968
+ model: config.model || '',
969
+ llmProvider: config.llmProvider || 'openai',
970
+ exportedAt,
971
+ }, 'application/json'),
972
+ this.workspaceFile('.canvas-flow/README.md', [
973
+ '# Canvas Flow Agent Workspace',
974
+ '',
975
+ 'Este pacote representa a pasta .canvas-flow do agente.',
976
+ 'Edite os arquivos abaixo, versione no Git e importe de volta no Agent Studio.',
977
+ '',
978
+ '- manifest.json: indice leve com paths, descricoes e load mode.',
979
+ '- agents.md: arquitetura, decisoes e papel do orquestrador.',
980
+ '- guardrails.md: politicas e limites sempre presentes.',
981
+ '- blocked-terms.json: tripwires que bloqueiam entradas sensiveis.',
982
+ '- rules/: regras sempre presentes ou sob demanda.',
983
+ '- skills/: tarefas no contexto principal.',
984
+ '- subagents/: especialistas com contexto isolado.',
985
+ '- mcp.json: ferramentas externas sob demanda.',
986
+ '',
987
+ ].join('\n')),
988
+ this.workspaceFile('.canvas-flow/manifest.json', this.buildWorkspaceManifest(config), 'application/json'),
989
+ this.workspaceFile('.canvas-flow/agents.md', spec.agentsMd || ''),
990
+ this.workspaceFile('.canvas-flow/guardrails.md', spec.guardrails || ''),
991
+ this.workspaceFile('.canvas-flow/blocked-terms.json', spec.blockedTerms || [], 'application/json'),
992
+ this.workspaceFile('.canvas-flow/rules.json', rules, 'application/json'),
993
+ this.workspaceFile('.canvas-flow/skills.json', skills, 'application/json'),
994
+ this.workspaceFile('.canvas-flow/subagents.json', subagents, 'application/json'),
995
+ this.workspaceFile('.canvas-flow/mcp.json', mcpServers, 'application/json'),
996
+ ...rules.map((rule, index) => this.workspaceFile(`.canvas-flow/rules/${this.safeWorkspaceFileName(rule.id || rule.name, `rule-${index + 1}`)}.rule.json`, rule, 'application/json')),
997
+ ...skills.map((skill, index) => this.workspaceFile(`.canvas-flow/skills/${this.safeWorkspaceFileName(skill.id || skill.name, `skill-${index + 1}`)}.skill.json`, skill, 'application/json')),
998
+ ...subagents.map((subagent, index) => this.workspaceFile(`.canvas-flow/subagents/${this.safeWorkspaceFileName(subagent.id || subagent.name, `subagent-${index + 1}`)}.agent.json`, subagent, 'application/json')),
999
+ ];
1000
+ return {
1001
+ kind: 'canvas-flow-agent-workspace',
1002
+ version: 1,
1003
+ folderName: '.canvas-flow',
1004
+ agentId: target,
1005
+ agentName,
1006
+ exportedAt,
1007
+ config,
1008
+ files,
1009
+ };
1010
+ }
1011
+ async importAgentWorkspace(agentId, payload, auth) {
1012
+ const target = this.normalizeAgentName(agentId);
1013
+ if (!target || target === 'default-agent') {
1014
+ throw new common_1.HttpException('Selecione um agente real antes de importar workspace.', common_1.HttpStatus.BAD_REQUEST);
1015
+ }
1016
+ const config = this.workspaceConfigFromPayload(payload || {});
1017
+ return await this.updateAgentConfig(target, config, auth);
1018
+ }
1019
+ async renameAgent(currentAgentId, nextName, auth) {
1020
+ const agentId = this.normalizeAgentName(currentAgentId);
1021
+ const displayName = this.normalizeAgentDisplayName(nextName, agentId);
1022
+ const current = (await this.listAgents(auth?.organizationId)).find((agent) => agent.agentId === agentId);
1023
+ if (current?.name === displayName) {
1024
+ return current;
1025
+ }
1026
+ const duplicate = (await this.listAgents(auth?.organizationId))
1027
+ .find((agent) => agent.agentId !== agentId && this.normalizeAgentDisplayName(agent.name) === displayName);
1028
+ if (duplicate) {
1029
+ throw new common_1.HttpException('Ja existe um agente com este nome.', common_1.HttpStatus.CONFLICT);
1030
+ }
1031
+ await this.agentModel.updateOne(this.agentQuery(agentId, auth?.organizationId), {
1032
+ $set: { agentId, name: displayName },
1033
+ $setOnInsert: {
1034
+ sortOrder: await this.nextAgentSortOrder(auth?.organizationId),
1035
+ ...(auth?.organizationId ? { organizationId: auth.organizationId } : {}),
1036
+ },
1037
+ }, { upsert: true }).exec();
1038
+ return (await this.listAgents(auth?.organizationId)).find((agent) => agent.agentId === agentId);
1039
+ }
1040
+ async removeAgent(agentId, confirmationName, auth) {
1041
+ const target = this.normalizeAgentName(agentId);
1042
+ const current = (await this.listAgents(auth?.organizationId)).find((agent) => agent.agentId === target);
1043
+ const displayName = current?.name || target;
1044
+ const confirmation = String(confirmationName || '').trim();
1045
+ if (confirmation !== displayName && confirmation !== target) {
1046
+ throw new common_1.HttpException('Digite o nome exato do agente para confirmar a exclusao.', common_1.HttpStatus.BAD_REQUEST);
1047
+ }
1048
+ const flowDelete = await this.model.deleteMany(this.scopedQuery(target, auth?.organizationId)).exec();
1049
+ const agentDelete = await this.agentModel.deleteMany(this.agentQuery(target, auth?.organizationId)).exec();
1050
+ return {
1051
+ agentId: target,
1052
+ name: displayName,
1053
+ deletedFlows: flowDelete.deletedCount || 0,
1054
+ deletedAgents: agentDelete.deletedCount || 0,
1055
+ agents: await this.listAgents(auth?.organizationId),
1056
+ };
1057
+ }
1058
+ async reorderAgents(orderedAgentIds, auth) {
1059
+ const ids = Array.from(new Set(orderedAgentIds.map((id) => this.normalizeAgentName(id)).filter(Boolean)));
1060
+ if (!ids.length)
1061
+ return await this.listAgents(auth?.organizationId);
1062
+ const existingAgents = await this.listAgents(auth?.organizationId);
1063
+ const byId = new Map(existingAgents.map((agent) => [this.normalizeAgentName(agent.agentId || agent.name), agent]));
1064
+ const orderedKnownIds = ids.filter((id) => byId.has(id));
1065
+ const trailingIds = existingAgents
1066
+ .map((agent) => this.normalizeAgentName(agent.agentId || agent.name))
1067
+ .filter((id) => !orderedKnownIds.includes(id));
1068
+ const finalIds = [...orderedKnownIds, ...trailingIds];
1069
+ const operations = finalIds
1070
+ .filter((id) => id !== 'default-agent')
1071
+ .map((id, index) => ({
1072
+ updateOne: {
1073
+ filter: this.agentQuery(id, auth?.organizationId),
1074
+ update: {
1075
+ $set: {
1076
+ agentId: id,
1077
+ name: this.normalizeAgentDisplayName(byId.get(id)?.name, id),
1078
+ sortOrder: (index + 1) * 1000,
1079
+ ...(auth?.organizationId ? { organizationId: auth.organizationId } : {}),
1080
+ },
1081
+ $setOnInsert: {
1082
+ ...(auth?.userId ? { createdBy: auth.userId } : {}),
1083
+ },
1084
+ },
1085
+ upsert: true,
1086
+ },
1087
+ }));
1088
+ if (operations.length) {
1089
+ await this.agentModel.bulkWrite(operations);
1090
+ }
1091
+ return await this.listAgents(auth?.organizationId);
1092
+ }
1093
+ async reorder(orderedIds, agentId, organizationId) {
1094
+ const ids = orderedIds.filter(Boolean);
1095
+ if (!ids.length)
1096
+ return await this.findAll(agentId, organizationId);
1097
+ const query = this.withOrganization({ _id: { $in: ids } }, organizationId);
1098
+ if (agentId)
1099
+ query.agentId = agentId;
1100
+ const flows = await this.model.find(query).select('_id').lean().exec();
1101
+ const allowedIds = new Set(flows.map((flow) => String(flow._id)));
1102
+ const operations = ids
1103
+ .filter((id) => allowedIds.has(String(id)))
1104
+ .map((id, index) => ({
1105
+ updateOne: {
1106
+ filter: this.withOrganization({ _id: id }, organizationId),
1107
+ update: { $set: { sortOrder: (index + 1) * 1000 } },
1108
+ },
1109
+ }));
1110
+ if (operations.length) {
1111
+ await this.model.bulkWrite(operations);
1112
+ }
1113
+ return await this.findAll(agentId, organizationId);
1114
+ }
1115
+ async findMain(agentId, channel) {
1116
+ const baseQuery = {};
1117
+ if (agentId)
1118
+ baseQuery.agentId = agentId;
1119
+ const flows = await this.model.find(baseQuery).sort({ updatedAt: -1 }).lean().exec();
1120
+ const agentRelease = await this.resolveAgentRelease(agentId).catch(() => ({ versions: {}, source: 'none' }));
1121
+ const resolved = await Promise.all(flows.map(async (flow) => {
1122
+ try {
1123
+ return { flow, resolved: await this.resolveFlowVersionAsync(flow, agentRelease.versions?.[String(flow?._id || '')]) };
1124
+ }
1125
+ catch {
1126
+ return { flow, resolved: { config: flow?.config || {}, source: 'draft', latestVersion: 0 } };
1127
+ }
1128
+ }));
1129
+ const matchesChannel = (item) => !channel || item.resolved?.config?.channel === channel;
1130
+ const main = resolved.find((item) => matchesChannel(item) && item.resolved?.config?.isMainFlow === true);
1131
+ if (main)
1132
+ return main.flow;
1133
+ const fallback = resolved.find((item) => matchesChannel(item))?.flow;
1134
+ if (!fallback) {
1135
+ throw new common_1.HttpException('Canvas main flow not found', common_1.HttpStatus.NOT_FOUND);
1136
+ }
1137
+ return fallback;
1138
+ }
1139
+ async findOne(id, organizationId, options) {
1140
+ const query = this.withOrganization({ _id: id }, organizationId);
1141
+ const request = this.model.findOne(query);
1142
+ if (options?.includeVersions)
1143
+ request.select('+versions');
1144
+ const flow = await request.lean().exec();
1145
+ if (!flow) {
1146
+ throw new common_1.HttpException('Canvas flow not found', common_1.HttpStatus.NOT_FOUND);
1147
+ }
1148
+ if (options?.includeVersions) {
1149
+ return await this.flowWithVersions(flow, organizationId);
1150
+ }
1151
+ if (options?.includeBsonSize) {
1152
+ const bsonSizeBytes = await this.flowDocumentBsonSize(id, organizationId);
1153
+ return {
1154
+ ...flow,
1155
+ ...(bsonSizeBytes !== undefined ? { bsonSizeBytes } : {}),
1156
+ };
1157
+ }
1158
+ return flow;
1159
+ }
1160
+ async getVersions(id, auth) {
1161
+ const flow = await this.loadFlowForVersionAccess(id, auth?.organizationId);
1162
+ return await this.flowWithVersions(flow, auth?.organizationId);
1163
+ }
1164
+ async deployVersion(id, body = {}, auth) {
1165
+ const query = this.withOrganization({ _id: id }, auth?.organizationId);
1166
+ const flow = await this.loadFlowForVersionAccess(id, auth?.organizationId);
1167
+ const updates = {};
1168
+ if (body?.name !== undefined)
1169
+ updates.name = String(body.name || flow.name || 'Fluxo');
1170
+ if (body?.agentId !== undefined) {
1171
+ updates.agentId = this.normalizeAgentName(body.agentId);
1172
+ if (updates.agentId !== 'default-agent') {
1173
+ await this.ensureAgent(updates.agentId, auth);
1174
+ }
1175
+ }
1176
+ if (body?.description !== undefined)
1177
+ updates.description = body.description;
1178
+ if (body?.config && typeof body.config === 'object')
1179
+ updates.config = body.config;
1180
+ const draftConfig = updates.config || flow.config || {};
1181
+ const existingVersions = await this.findFlowVersions(flow, auth?.organizationId, false);
1182
+ const nextVersion = this.latestVersionFromRecords(flow, existingVersions) + 1;
1183
+ const now = new Date().toISOString();
1184
+ const versionRecord = {
1185
+ flowId: this.flowId(flow),
1186
+ agentId: updates.agentId || flow.agentId || '',
1187
+ ...(flow.organizationId || auth?.organizationId ? { organizationId: flow.organizationId || auth?.organizationId } : {}),
1188
+ version: nextVersion,
1189
+ name: String(body?.versionName || body?.name || flow.name || `v${nextVersion}`),
1190
+ notes: String(body?.notes || '').trim(),
1191
+ config: this.cloneJson(draftConfig),
1192
+ deployedAt: now,
1193
+ createdAt: now,
1194
+ deployedBy: auth?.userId || '',
1195
+ deployedByEmail: auth?.userEmail || '',
1196
+ ...(body?.activate === false ? {} : {
1197
+ activatedAt: now,
1198
+ activatedBy: auth?.userId || '',
1199
+ activatedByEmail: auth?.userEmail || '',
1200
+ }),
1201
+ };
1202
+ await this.versionModel.updateOne(this.flowVersionQuery(flow, auth?.organizationId, nextVersion), { $setOnInsert: versionRecord }, { upsert: true }).exec();
1203
+ const update = {
1204
+ $set: {
1205
+ ...updates,
1206
+ latestVersion: nextVersion,
1207
+ activeVersion: body?.activate === false ? this.normalizeVersionValue(flow.activeVersion) : nextVersion,
1208
+ },
1209
+ $unset: { versions: '' },
1210
+ };
1211
+ const deployed = await this.model.findOneAndUpdate(query, update, { new: true }).lean().exec();
1212
+ await this.unsetOtherMainFlows(deployed);
1213
+ if (body?.activate !== false) {
1214
+ await this.updateActiveAgentReleaseFlowVersion(deployed, nextVersion, auth, now);
1215
+ }
1216
+ return await this.flowWithVersions(deployed, auth?.organizationId);
1217
+ }
1218
+ async activateVersion(id, version, auth) {
1219
+ const targetVersion = this.normalizeVersionValue(version);
1220
+ if (!targetVersion) {
1221
+ throw new common_1.HttpException('Versao invalida para ativacao.', common_1.HttpStatus.BAD_REQUEST);
1222
+ }
1223
+ const flow = await this.loadFlowForVersionAccess(id, auth?.organizationId);
1224
+ const versionRecord = await this.findFlowVersion(flow, targetVersion, auth?.organizationId);
1225
+ if (!versionRecord) {
1226
+ throw new common_1.HttpException('Versao do fluxo nao encontrada.', common_1.HttpStatus.NOT_FOUND);
1227
+ }
1228
+ const activatedAt = new Date().toISOString();
1229
+ await this.versionModel.updateOne(this.flowVersionQuery(flow, auth?.organizationId, targetVersion), {
1230
+ $set: {
1231
+ activatedAt,
1232
+ activatedBy: auth?.userId || '',
1233
+ activatedByEmail: auth?.userEmail || '',
1234
+ },
1235
+ }).exec();
1236
+ const query = this.withOrganization({ _id: id }, auth?.organizationId);
1237
+ const updated = await this.model
1238
+ .findOneAndUpdate(query, {
1239
+ $set: {
1240
+ activeVersion: targetVersion,
1241
+ },
1242
+ $unset: { versions: '' },
1243
+ }, { new: true })
1244
+ .lean()
1245
+ .exec();
1246
+ if (!updated) {
1247
+ throw new common_1.HttpException('Versao do fluxo nao encontrada.', common_1.HttpStatus.NOT_FOUND);
1248
+ }
1249
+ await this.updateActiveAgentReleaseFlowVersion(updated, targetVersion, auth);
1250
+ return await this.flowWithVersions(updated, auth?.organizationId);
1251
+ }
1252
+ async renameVersion(id, version, body = {}, auth) {
1253
+ const targetVersion = this.normalizeVersionValue(version);
1254
+ const name = String(body?.name || '').trim();
1255
+ if (!targetVersion) {
1256
+ throw new common_1.HttpException('Versao invalida para renomear.', common_1.HttpStatus.BAD_REQUEST);
1257
+ }
1258
+ if (!name) {
1259
+ throw new common_1.HttpException('Informe um nome para a versao.', common_1.HttpStatus.BAD_REQUEST);
1260
+ }
1261
+ const flow = await this.loadFlowForVersionAccess(id, auth?.organizationId);
1262
+ const updatedVersion = await this.versionModel.findOneAndUpdate(this.flowVersionQuery(flow, auth?.organizationId, targetVersion), { $set: { name } }, { new: true }).lean().exec();
1263
+ if (!updatedVersion) {
1264
+ throw new common_1.HttpException('Versao do fluxo nao encontrada.', common_1.HttpStatus.NOT_FOUND);
1265
+ }
1266
+ return await this.flowWithVersions(flow, auth?.organizationId);
1267
+ }
1268
+ async deleteVersion(id, version, auth) {
1269
+ const targetVersion = this.normalizeVersionValue(version);
1270
+ if (!targetVersion) {
1271
+ throw new common_1.HttpException('Versao invalida para exclusao.', common_1.HttpStatus.BAD_REQUEST);
1272
+ }
1273
+ const flow = await this.loadFlowForVersionAccess(id, auth?.organizationId);
1274
+ const versionRecord = await this.findFlowVersion(flow, targetVersion, auth?.organizationId);
1275
+ if (!versionRecord) {
1276
+ throw new common_1.HttpException('Versao do fluxo nao encontrada.', common_1.HttpStatus.NOT_FOUND);
1277
+ }
1278
+ if (this.normalizeVersionValue(flow.activeVersion) === targetVersion) {
1279
+ throw new common_1.HttpException('Nao e possivel excluir a versao ativa. Ative outra versao antes.', common_1.HttpStatus.BAD_REQUEST);
1280
+ }
1281
+ await this.versionModel.deleteOne(this.flowVersionQuery(flow, auth?.organizationId, targetVersion)).exec();
1282
+ const remainingVersions = await this.findFlowVersions(flow, auth?.organizationId, false);
1283
+ const latestVersion = remainingVersions.reduce((max, item) => Math.max(max, Number(item?.version) || 0), 0);
1284
+ const query = this.withOrganization({ _id: id }, auth?.organizationId);
1285
+ const updated = await this.model
1286
+ .findOneAndUpdate(query, {
1287
+ $set: { latestVersion },
1288
+ $unset: { versions: '' },
1289
+ }, { new: true })
1290
+ .lean()
1291
+ .exec();
1292
+ if (!updated) {
1293
+ throw new common_1.HttpException('Versao do fluxo nao encontrada.', common_1.HttpStatus.NOT_FOUND);
1294
+ }
1295
+ return await this.flowWithVersions(updated, auth?.organizationId);
1296
+ }
1297
+ async overwriteVersion(id, version, body = {}, auth) {
1298
+ const targetVersion = this.normalizeVersionValue(version);
1299
+ if (!targetVersion) {
1300
+ throw new common_1.HttpException('Versao invalida para sobrescrita.', common_1.HttpStatus.BAD_REQUEST);
1301
+ }
1302
+ const flow = await this.loadFlowForVersionAccess(id, auth?.organizationId);
1303
+ const targetSnapshot = await this.findFlowVersion(flow, targetVersion, auth?.organizationId);
1304
+ if (!targetSnapshot) {
1305
+ throw new common_1.HttpException('Versao do fluxo nao encontrada.', common_1.HttpStatus.NOT_FOUND);
1306
+ }
1307
+ const sourceVersion = this.normalizeVersionValue(body?.sourceVersion);
1308
+ const sourceSnapshot = sourceVersion
1309
+ ? await this.findFlowVersion(flow, sourceVersion, auth?.organizationId)
1310
+ : null;
1311
+ if (sourceVersion && !sourceSnapshot) {
1312
+ throw new common_1.HttpException(`Versao ${sourceVersion} do fluxo nao encontrada para origem.`, common_1.HttpStatus.NOT_FOUND);
1313
+ }
1314
+ const draftConfig = sourceSnapshot?.config && typeof sourceSnapshot.config === 'object'
1315
+ ? sourceSnapshot.config
1316
+ : body?.config && typeof body.config === 'object'
1317
+ ? body.config
1318
+ : flow.config || {};
1319
+ const now = new Date().toISOString();
1320
+ const set = {
1321
+ config: this.cloneJson(draftConfig),
1322
+ deployedAt: now,
1323
+ updatedAt: now,
1324
+ deployedBy: auth?.userId || '',
1325
+ deployedByEmail: auth?.userEmail || '',
1326
+ };
1327
+ if (body?.notes !== undefined)
1328
+ set.notes = String(body.notes || '').trim();
1329
+ if (body?.name !== undefined)
1330
+ set.name = String(body.name || flow.name || `v${targetVersion}`);
1331
+ const updatedVersion = await this.versionModel.findOneAndUpdate(this.flowVersionQuery(flow, auth?.organizationId, targetVersion), { $set: set }, { new: true }).lean().exec();
1332
+ if (!updatedVersion) {
1333
+ throw new common_1.HttpException('Versao do fluxo nao encontrada.', common_1.HttpStatus.NOT_FOUND);
1334
+ }
1335
+ const updated = await this.model.findOneAndUpdate(this.withOrganization({ _id: id }, auth?.organizationId), { $unset: { versions: '' } }, { new: true }).lean().exec();
1336
+ await this.updateActiveAgentReleaseFlowVersion(updated, targetVersion, auth, now);
1337
+ return await this.flowWithVersions(updated, auth?.organizationId);
1338
+ }
1339
+ async getAgentReleases(agentId, auth) {
1340
+ const name = this.normalizeAgentName(agentId);
1341
+ const agent = await this.agentModel.findOne(this.agentQuery(name, auth?.organizationId)).lean().exec();
1342
+ if (!agent) {
1343
+ return { agentId: name, activeRelease: undefined, latestRelease: 0, releases: [] };
1344
+ }
1345
+ const flows = await this.model.find(this.scopedQuery(name, auth?.organizationId)).select('_id').lean().exec();
1346
+ const existingFlowIds = new Set(flows.map((flow) => String(flow._id)));
1347
+ const releases = await this.agentReleasesForResponse(agent, auth?.organizationId, existingFlowIds);
1348
+ return {
1349
+ agentId: name,
1350
+ activeRelease: this.normalizeVersionValue(agent?.activeRelease),
1351
+ latestRelease: this.latestAgentReleaseNumber(agent),
1352
+ releases,
1353
+ };
1354
+ }
1355
+ async deployAgentRelease(agentId, body = {}, auth) {
1356
+ const name = this.normalizeAgentName(agentId);
1357
+ await this.ensureAgent(name, auth);
1358
+ const agent = await this.agentModel.findOne(this.agentQuery(name, auth?.organizationId)).lean().exec();
1359
+ const flows = await this.model.find(this.scopedQuery(name, auth?.organizationId)).select('+versions').lean().exec();
1360
+ if (!flows.length) {
1361
+ throw new common_1.HttpException('Nenhum fluxo encontrado para este agente.', common_1.HttpStatus.BAD_REQUEST);
1362
+ }
1363
+ const versionMap = {};
1364
+ const flowNames = {};
1365
+ const flowOperations = [];
1366
+ const versionOperations = [];
1367
+ const now = new Date().toISOString();
1368
+ for (const flow of flows) {
1369
+ const flowId = String(flow._id);
1370
+ let latestVersion = await this.latestExistingFlowVersionNumberAsync(flow, auth?.organizationId);
1371
+ flowNames[flowId] = flow.name || flow.config?.title || flowId;
1372
+ if (!latestVersion) {
1373
+ latestVersion = 1;
1374
+ versionOperations.push({
1375
+ updateOne: {
1376
+ filter: this.flowVersionQuery(flow, auth?.organizationId, latestVersion),
1377
+ update: {
1378
+ $setOnInsert: {
1379
+ flowId,
1380
+ agentId: flow.agentId || name,
1381
+ ...(flow.organizationId || auth?.organizationId ? { organizationId: flow.organizationId || auth?.organizationId } : {}),
1382
+ version: latestVersion,
1383
+ name: String(flow.name || `v${latestVersion}`),
1384
+ notes: 'Versao inicial criada para pacote do agente.',
1385
+ config: this.cloneJson(flow.config || {}),
1386
+ deployedAt: now,
1387
+ createdAt: now,
1388
+ deployedBy: auth?.userId || '',
1389
+ deployedByEmail: auth?.userEmail || '',
1390
+ agentReleaseCandidate: true,
1391
+ },
1392
+ },
1393
+ upsert: true,
1394
+ },
1395
+ });
1396
+ flowOperations.push({
1397
+ updateOne: {
1398
+ filter: this.withOrganization({ _id: flow._id }, auth?.organizationId),
1399
+ update: {
1400
+ $set: { latestVersion },
1401
+ $unset: { versions: '' },
1402
+ },
1403
+ },
1404
+ });
1405
+ }
1406
+ versionMap[flowId] = latestVersion;
1407
+ }
1408
+ if (versionOperations.length) {
1409
+ await this.versionModel.bulkWrite(versionOperations, { ordered: false });
1410
+ }
1411
+ if (flowOperations.length) {
1412
+ await this.model.bulkWrite(flowOperations);
1413
+ }
1414
+ const nextRelease = this.latestAgentReleaseNumber(agent) + 1;
1415
+ const releaseRecord = {
1416
+ release: nextRelease,
1417
+ name: String(body?.name || `Release ${nextRelease}`),
1418
+ notes: String(body?.notes || '').trim(),
1419
+ versions: versionMap,
1420
+ flowNames,
1421
+ createdAt: now,
1422
+ deployedAt: now,
1423
+ deployedBy: auth?.userId || '',
1424
+ deployedByEmail: auth?.userEmail || '',
1425
+ ...(body?.activate === false ? {} : {
1426
+ activatedAt: now,
1427
+ activatedBy: auth?.userId || '',
1428
+ activatedByEmail: auth?.userEmail || '',
1429
+ }),
1430
+ };
1431
+ const set = {
1432
+ agentId: name,
1433
+ latestRelease: nextRelease,
1434
+ ...(auth?.organizationId ? { organizationId: auth.organizationId } : {}),
1435
+ };
1436
+ if (body?.activate !== false)
1437
+ set.activeRelease = nextRelease;
1438
+ const updated = await this.agentModel.findOneAndUpdate(this.agentQuery(name, auth?.organizationId), {
1439
+ $set: set,
1440
+ $setOnInsert: {
1441
+ name,
1442
+ sortOrder: await this.nextAgentSortOrder(auth?.organizationId),
1443
+ ...(auth?.userId ? { createdBy: auth.userId } : {}),
1444
+ },
1445
+ $push: { releases: releaseRecord },
1446
+ }, { new: true, upsert: true }).lean().exec();
1447
+ const releases = await this.agentReleasesForResponse(updated, auth?.organizationId);
1448
+ return {
1449
+ agentId: name,
1450
+ activeRelease: this.normalizeVersionValue(updated?.activeRelease),
1451
+ latestRelease: this.latestAgentReleaseNumber(updated),
1452
+ release: releases.find((release) => Number(release?.release) === nextRelease) || releaseRecord,
1453
+ releases,
1454
+ };
1455
+ }
1456
+ async activateAgentRelease(agentId, release, auth) {
1457
+ const name = this.normalizeAgentName(agentId);
1458
+ const targetRelease = this.normalizeVersionValue(release);
1459
+ if (!targetRelease) {
1460
+ throw new common_1.HttpException('Release invalido para ativacao.', common_1.HttpStatus.BAD_REQUEST);
1461
+ }
1462
+ const updated = await this.agentModel.findOneAndUpdate({ ...this.agentQuery(name, auth?.organizationId), 'releases.release': targetRelease }, {
1463
+ $set: {
1464
+ activeRelease: targetRelease,
1465
+ 'releases.$.activatedAt': new Date().toISOString(),
1466
+ 'releases.$.activatedBy': auth?.userId || '',
1467
+ 'releases.$.activatedByEmail': auth?.userEmail || '',
1468
+ },
1469
+ }, { new: true }).lean().exec();
1470
+ if (!updated) {
1471
+ throw new common_1.HttpException('Release do agente nao encontrado.', common_1.HttpStatus.NOT_FOUND);
1472
+ }
1473
+ return {
1474
+ agentId: name,
1475
+ activeRelease: this.normalizeVersionValue(updated.activeRelease),
1476
+ latestRelease: this.latestAgentReleaseNumber(updated),
1477
+ releases: await this.agentReleasesForResponse(updated, auth?.organizationId),
1478
+ };
1479
+ }
1480
+ async renameAgentRelease(agentId, release, body = {}, auth) {
1481
+ const name = this.normalizeAgentName(agentId);
1482
+ const targetRelease = this.normalizeVersionValue(release);
1483
+ const nextName = String(body?.name || '').trim();
1484
+ if (!targetRelease) {
1485
+ throw new common_1.HttpException('Versao do agente invalida para renomear.', common_1.HttpStatus.BAD_REQUEST);
1486
+ }
1487
+ if (!nextName) {
1488
+ throw new common_1.HttpException('Informe um nome para a versao do agente.', common_1.HttpStatus.BAD_REQUEST);
1489
+ }
1490
+ const updated = await this.agentModel.findOneAndUpdate({ ...this.agentQuery(name, auth?.organizationId), 'releases.release': targetRelease }, { $set: { 'releases.$.name': nextName } }, { new: true }).lean().exec();
1491
+ if (!updated) {
1492
+ throw new common_1.HttpException('Versao do agente nao encontrada.', common_1.HttpStatus.NOT_FOUND);
1493
+ }
1494
+ return {
1495
+ agentId: name,
1496
+ activeRelease: this.normalizeVersionValue(updated.activeRelease),
1497
+ latestRelease: this.latestAgentReleaseNumber(updated),
1498
+ releases: await this.agentReleasesForResponse(updated, auth?.organizationId),
1499
+ };
1500
+ }
1501
+ async overwriteAgentRelease(agentId, release, body = {}, auth) {
1502
+ const name = this.normalizeAgentName(agentId);
1503
+ const targetRelease = this.normalizeVersionValue(release);
1504
+ if (!targetRelease) {
1505
+ throw new common_1.HttpException('Versao do agente invalida para sobrescrita.', common_1.HttpStatus.BAD_REQUEST);
1506
+ }
1507
+ const query = { ...this.agentQuery(name, auth?.organizationId), 'releases.release': targetRelease };
1508
+ const agent = await this.agentModel.findOne(query).lean().exec();
1509
+ if (!agent) {
1510
+ throw new common_1.HttpException('Versao do agente nao encontrada.', common_1.HttpStatus.NOT_FOUND);
1511
+ }
1512
+ const now = new Date().toISOString();
1513
+ let versionMap = {};
1514
+ let flowNames = {};
1515
+ const sourceRelease = this.normalizeVersionValue(body?.sourceRelease);
1516
+ if (sourceRelease) {
1517
+ const source = this.agentReleases(agent).find((item) => Number(item?.release) === sourceRelease);
1518
+ if (!source) {
1519
+ throw new common_1.HttpException(`Versao do agente r${sourceRelease} nao encontrada para origem.`, common_1.HttpStatus.NOT_FOUND);
1520
+ }
1521
+ versionMap = source.versions && typeof source.versions === 'object' && !Array.isArray(source.versions)
1522
+ ? this.cloneJson(source.versions)
1523
+ : {};
1524
+ flowNames = source.flowNames && typeof source.flowNames === 'object' && !Array.isArray(source.flowNames)
1525
+ ? this.cloneJson(source.flowNames)
1526
+ : {};
1527
+ }
1528
+ else {
1529
+ const flows = await this.model.find(this.scopedQuery(name, auth?.organizationId)).select('+versions').lean().exec();
1530
+ if (!flows.length) {
1531
+ throw new common_1.HttpException('Nenhum fluxo encontrado para este agente.', common_1.HttpStatus.BAD_REQUEST);
1532
+ }
1533
+ const flowOperations = [];
1534
+ const versionOperations = [];
1535
+ for (const flow of flows) {
1536
+ const flowId = String(flow._id);
1537
+ let latestVersion = await this.latestExistingFlowVersionNumberAsync(flow, auth?.organizationId);
1538
+ flowNames[flowId] = flow.name || flow.config?.title || flowId;
1539
+ if (!latestVersion) {
1540
+ latestVersion = 1;
1541
+ versionOperations.push({
1542
+ updateOne: {
1543
+ filter: this.flowVersionQuery(flow, auth?.organizationId, latestVersion),
1544
+ update: {
1545
+ $setOnInsert: {
1546
+ flowId,
1547
+ agentId: flow.agentId || name,
1548
+ ...(flow.organizationId || auth?.organizationId ? { organizationId: flow.organizationId || auth?.organizationId } : {}),
1549
+ version: latestVersion,
1550
+ name: String(flow.name || `v${latestVersion}`),
1551
+ notes: 'Versao inicial criada para pacote do agente.',
1552
+ config: this.cloneJson(flow.config || {}),
1553
+ deployedAt: now,
1554
+ createdAt: now,
1555
+ deployedBy: auth?.userId || '',
1556
+ deployedByEmail: auth?.userEmail || '',
1557
+ agentReleaseCandidate: true,
1558
+ overwrittenAgentRelease: targetRelease,
1559
+ },
1560
+ },
1561
+ upsert: true,
1562
+ },
1563
+ });
1564
+ flowOperations.push({
1565
+ updateOne: {
1566
+ filter: this.withOrganization({ _id: flow._id }, auth?.organizationId),
1567
+ update: {
1568
+ $set: { latestVersion },
1569
+ $unset: { versions: '' },
1570
+ },
1571
+ },
1572
+ });
1573
+ }
1574
+ versionMap[flowId] = latestVersion;
1575
+ }
1576
+ if (versionOperations.length) {
1577
+ await this.versionModel.bulkWrite(versionOperations, { ordered: false });
1578
+ }
1579
+ if (flowOperations.length) {
1580
+ await this.model.bulkWrite(flowOperations);
1581
+ }
1582
+ }
1583
+ const set = {
1584
+ 'releases.$.versions': versionMap,
1585
+ 'releases.$.flowNames': flowNames,
1586
+ 'releases.$.deployedAt': now,
1587
+ 'releases.$.updatedAt': now,
1588
+ 'releases.$.deployedBy': auth?.userId || '',
1589
+ 'releases.$.deployedByEmail': auth?.userEmail || '',
1590
+ };
1591
+ if (body?.name !== undefined)
1592
+ set['releases.$.name'] = String(body.name || `Release ${targetRelease}`);
1593
+ if (body?.notes !== undefined)
1594
+ set['releases.$.notes'] = String(body.notes || '').trim();
1595
+ const updated = await this.agentModel.findOneAndUpdate(query, { $set: set }, { new: true }).lean().exec();
1596
+ if (!updated) {
1597
+ throw new common_1.HttpException('Versao do agente nao encontrada.', common_1.HttpStatus.NOT_FOUND);
1598
+ }
1599
+ return {
1600
+ agentId: name,
1601
+ activeRelease: this.normalizeVersionValue(updated.activeRelease),
1602
+ latestRelease: this.latestAgentReleaseNumber(updated),
1603
+ releases: await this.agentReleasesForResponse(updated, auth?.organizationId),
1604
+ };
1605
+ }
1606
+ async deleteAgentRelease(agentId, release, auth) {
1607
+ const name = this.normalizeAgentName(agentId);
1608
+ const targetRelease = this.normalizeVersionValue(release);
1609
+ if (!targetRelease) {
1610
+ throw new common_1.HttpException('Versao do agente invalida para exclusao.', common_1.HttpStatus.BAD_REQUEST);
1611
+ }
1612
+ const query = { ...this.agentQuery(name, auth?.organizationId), 'releases.release': targetRelease };
1613
+ const agent = await this.agentModel.findOne(query).lean().exec();
1614
+ if (!agent) {
1615
+ throw new common_1.HttpException('Versao do agente nao encontrada.', common_1.HttpStatus.NOT_FOUND);
1616
+ }
1617
+ if (this.normalizeVersionValue(agent.activeRelease) === targetRelease) {
1618
+ throw new common_1.HttpException('Nao e possivel excluir a versao ativa do agente. Ative outra versao antes.', common_1.HttpStatus.BAD_REQUEST);
1619
+ }
1620
+ const remainingReleases = this.agentReleases(agent).filter((item) => Number(item?.release) !== targetRelease);
1621
+ const latestRelease = remainingReleases.reduce((max, item) => Math.max(max, Number(item?.release) || 0), 0);
1622
+ const updated = await this.agentModel.findOneAndUpdate(query, {
1623
+ $pull: { releases: { release: targetRelease } },
1624
+ $set: { latestRelease },
1625
+ }, { new: true }).lean().exec();
1626
+ if (!updated) {
1627
+ throw new common_1.HttpException('Versao do agente nao encontrada.', common_1.HttpStatus.NOT_FOUND);
1628
+ }
1629
+ return {
1630
+ agentId: name,
1631
+ activeRelease: this.normalizeVersionValue(updated.activeRelease),
1632
+ latestRelease: this.latestAgentReleaseNumber(updated),
1633
+ releases: await this.agentReleasesForResponse(updated, auth?.organizationId),
1634
+ };
1635
+ }
1636
+ async update(id, updateDto, auth) {
1637
+ if (updateDto.agentId) {
1638
+ updateDto.agentId = this.normalizeAgentName(updateDto.agentId);
1639
+ if (updateDto.agentId !== 'default-agent') {
1640
+ await this.ensureAgent(updateDto.agentId, auth);
1641
+ }
1642
+ }
1643
+ const { versions: _ignoredVersions, ...safeUpdateDto } = updateDto;
1644
+ const query = this.withOrganization({ _id: id }, auth?.organizationId);
1645
+ const updated = await this.model
1646
+ .findOneAndUpdate(query, safeUpdateDto, { new: true })
1647
+ .lean()
1648
+ .exec();
1649
+ if (!updated) {
1650
+ throw new common_1.HttpException('Canvas flow not found', common_1.HttpStatus.NOT_FOUND);
1651
+ }
1652
+ await this.unsetOtherMainFlows(updated);
1653
+ return updated;
1654
+ }
1655
+ async remove(id, organizationId) {
1656
+ const flow = await this.model.findOneAndDelete(this.withOrganization({ _id: id }, organizationId)).lean().exec();
1657
+ if (!flow)
1658
+ return null;
1659
+ const flowId = String(flow._id || id);
1660
+ const cleanupQuery = this.withOrganization({}, organizationId || flow.organizationId);
1661
+ await this.versionModel.deleteMany(this.flowVersionQuery(flowId, organizationId || flow.organizationId)).exec();
1662
+ await this.agentModel.updateMany(cleanupQuery, {
1663
+ $unset: {
1664
+ [`releases.$[].versions.${flowId}`]: '',
1665
+ [`releases.$[].flowNames.${flowId}`]: '',
1666
+ },
1667
+ }).exec();
1668
+ return flow;
1669
+ }
1670
+ };
1671
+ exports.CanvasFlowService = CanvasFlowService;
1672
+ exports.CanvasFlowService = CanvasFlowService = __decorate([
1673
+ (0, common_1.Injectable)(),
1674
+ __param(0, (0, common_1.Inject)(canvas_flow_constants_model_1.MODEL_NAME)),
1675
+ __param(1, (0, common_1.Inject)(canvas_flow_constants_model_1.AGENT_MODEL_NAME)),
1676
+ __param(2, (0, common_1.Inject)(canvas_flow_constants_model_1.VERSION_MODEL_NAME)),
1677
+ __metadata("design:paramtypes", [mongoose_1.Model,
1678
+ mongoose_1.Model,
1679
+ mongoose_1.Model])
1680
+ ], CanvasFlowService);
1681
+ //# sourceMappingURL=canvas-flow-service.js.map