@inkeep/agents-core 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 (259) hide show
  1. package/LICENSE.md +51 -0
  2. package/README.md +464 -0
  3. package/dist/__tests__/integration/helpers.d.ts +5 -0
  4. package/dist/__tests__/integration/helpers.d.ts.map +1 -0
  5. package/dist/__tests__/integration/helpers.js +37 -0
  6. package/dist/__tests__/integration/helpers.js.map +1 -0
  7. package/dist/__tests__/setup.d.ts +3 -0
  8. package/dist/__tests__/setup.d.ts.map +1 -0
  9. package/dist/__tests__/setup.js +29 -0
  10. package/dist/__tests__/setup.js.map +1 -0
  11. package/dist/client-exports.d.ts +301 -0
  12. package/dist/client-exports.d.ts.map +1 -0
  13. package/dist/client-exports.js +251 -0
  14. package/dist/client-exports.js.map +1 -0
  15. package/dist/context/ContextConfig.d.ts +55 -0
  16. package/dist/context/ContextConfig.d.ts.map +1 -0
  17. package/dist/context/ContextConfig.js +302 -0
  18. package/dist/context/ContextConfig.js.map +1 -0
  19. package/dist/context/ContextFetcher.d.ts +64 -0
  20. package/dist/context/ContextFetcher.d.ts.map +1 -0
  21. package/dist/context/ContextFetcher.js +325 -0
  22. package/dist/context/ContextFetcher.js.map +1 -0
  23. package/dist/context/ContextResolver.d.ts +52 -0
  24. package/dist/context/ContextResolver.d.ts.map +1 -0
  25. package/dist/context/ContextResolver.js +298 -0
  26. package/dist/context/ContextResolver.js.map +1 -0
  27. package/dist/context/TemplateEngine.d.ts +46 -0
  28. package/dist/context/TemplateEngine.d.ts.map +1 -0
  29. package/dist/context/TemplateEngine.js +175 -0
  30. package/dist/context/TemplateEngine.js.map +1 -0
  31. package/dist/context/context.d.ts +7 -0
  32. package/dist/context/context.d.ts.map +1 -0
  33. package/dist/context/context.js +157 -0
  34. package/dist/context/context.js.map +1 -0
  35. package/dist/context/contextCache.d.ts +50 -0
  36. package/dist/context/contextCache.d.ts.map +1 -0
  37. package/dist/context/contextCache.js +175 -0
  38. package/dist/context/contextCache.js.map +1 -0
  39. package/dist/context/index.d.ts +11 -0
  40. package/dist/context/index.d.ts.map +1 -0
  41. package/dist/context/index.js +8 -0
  42. package/dist/context/index.js.map +1 -0
  43. package/dist/credential-stores/index.d.ts +4 -0
  44. package/dist/credential-stores/index.d.ts.map +1 -0
  45. package/dist/credential-stores/index.js +4 -0
  46. package/dist/credential-stores/index.js.map +1 -0
  47. package/dist/credential-stores/keychain-store.d.ts +100 -0
  48. package/dist/credential-stores/keychain-store.d.ts.map +1 -0
  49. package/dist/credential-stores/keychain-store.js +225 -0
  50. package/dist/credential-stores/keychain-store.js.map +1 -0
  51. package/dist/credential-stores/memory-store.d.ts +39 -0
  52. package/dist/credential-stores/memory-store.d.ts.map +1 -0
  53. package/dist/credential-stores/memory-store.js +58 -0
  54. package/dist/credential-stores/memory-store.js.map +1 -0
  55. package/dist/credential-stores/nango-store.d.ts +59 -0
  56. package/dist/credential-stores/nango-store.d.ts.map +1 -0
  57. package/dist/credential-stores/nango-store.js +264 -0
  58. package/dist/credential-stores/nango-store.js.map +1 -0
  59. package/dist/credential-stuffer/CredentialStuffer.d.ts +80 -0
  60. package/dist/credential-stuffer/CredentialStuffer.d.ts.map +1 -0
  61. package/dist/credential-stuffer/CredentialStuffer.js +186 -0
  62. package/dist/credential-stuffer/CredentialStuffer.js.map +1 -0
  63. package/dist/credential-stuffer/index.d.ts +2 -0
  64. package/dist/credential-stuffer/index.d.ts.map +1 -0
  65. package/dist/credential-stuffer/index.js +2 -0
  66. package/dist/credential-stuffer/index.js.map +1 -0
  67. package/dist/data-access/agentDataComponents.d.ts +1 -0
  68. package/dist/data-access/agentDataComponents.d.ts.map +1 -0
  69. package/dist/data-access/agentDataComponents.js +2 -0
  70. package/dist/data-access/agentDataComponents.js.map +1 -0
  71. package/dist/data-access/agentGraphs.d.ts +406 -0
  72. package/dist/data-access/agentGraphs.d.ts.map +1 -0
  73. package/dist/data-access/agentGraphs.js +551 -0
  74. package/dist/data-access/agentGraphs.js.map +1 -0
  75. package/dist/data-access/agentRelations.d.ts +456 -0
  76. package/dist/data-access/agentRelations.d.ts.map +1 -0
  77. package/dist/data-access/agentRelations.js +471 -0
  78. package/dist/data-access/agentRelations.js.map +1 -0
  79. package/dist/data-access/agents.d.ts +218 -0
  80. package/dist/data-access/agents.d.ts.map +1 -0
  81. package/dist/data-access/agents.js +130 -0
  82. package/dist/data-access/agents.js.map +1 -0
  83. package/dist/data-access/apiKeys.d.ts +114 -0
  84. package/dist/data-access/apiKeys.d.ts.map +1 -0
  85. package/dist/data-access/apiKeys.js +185 -0
  86. package/dist/data-access/apiKeys.js.map +1 -0
  87. package/dist/data-access/artifactComponents.d.ts +152 -0
  88. package/dist/data-access/artifactComponents.d.ts.map +1 -0
  89. package/dist/data-access/artifactComponents.js +214 -0
  90. package/dist/data-access/artifactComponents.js.map +1 -0
  91. package/dist/data-access/contextCache.d.ts +68 -0
  92. package/dist/data-access/contextCache.d.ts.map +1 -0
  93. package/dist/data-access/contextCache.js +160 -0
  94. package/dist/data-access/contextCache.js.map +1 -0
  95. package/dist/data-access/contextConfigs.d.ts +110 -0
  96. package/dist/data-access/contextConfigs.d.ts.map +1 -0
  97. package/dist/data-access/contextConfigs.js +156 -0
  98. package/dist/data-access/contextConfigs.js.map +1 -0
  99. package/dist/data-access/conversations.d.ts +125 -0
  100. package/dist/data-access/conversations.d.ts.map +1 -0
  101. package/dist/data-access/conversations.js +244 -0
  102. package/dist/data-access/conversations.js.map +1 -0
  103. package/dist/data-access/credentialReferences.d.ts +86 -0
  104. package/dist/data-access/credentialReferences.d.ts.map +1 -0
  105. package/dist/data-access/credentialReferences.js +175 -0
  106. package/dist/data-access/credentialReferences.js.map +1 -0
  107. package/dist/data-access/dataComponents.d.ts +129 -0
  108. package/dist/data-access/dataComponents.d.ts.map +1 -0
  109. package/dist/data-access/dataComponents.js +213 -0
  110. package/dist/data-access/dataComponents.js.map +1 -0
  111. package/dist/data-access/externalAgents.d.ts +83 -0
  112. package/dist/data-access/externalAgents.d.ts.map +1 -0
  113. package/dist/data-access/externalAgents.js +163 -0
  114. package/dist/data-access/externalAgents.js.map +1 -0
  115. package/dist/data-access/graphFull.d.ts +32 -0
  116. package/dist/data-access/graphFull.d.ts.map +1 -0
  117. package/dist/data-access/graphFull.js +995 -0
  118. package/dist/data-access/graphFull.js.map +1 -0
  119. package/dist/data-access/index.d.ts +21 -0
  120. package/dist/data-access/index.d.ts.map +1 -0
  121. package/dist/data-access/index.js +22 -0
  122. package/dist/data-access/index.js.map +1 -0
  123. package/dist/data-access/ledgerArtifacts.d.ts +50 -0
  124. package/dist/data-access/ledgerArtifacts.d.ts.map +1 -0
  125. package/dist/data-access/ledgerArtifacts.js +112 -0
  126. package/dist/data-access/ledgerArtifacts.js.map +1 -0
  127. package/dist/data-access/messages.d.ts +209 -0
  128. package/dist/data-access/messages.d.ts.map +1 -0
  129. package/dist/data-access/messages.js +100 -0
  130. package/dist/data-access/messages.js.map +1 -0
  131. package/dist/data-access/projects.d.ts +67 -0
  132. package/dist/data-access/projects.d.ts.map +1 -0
  133. package/dist/data-access/projects.js +336 -0
  134. package/dist/data-access/projects.js.map +1 -0
  135. package/dist/data-access/tasks.d.ts +37 -0
  136. package/dist/data-access/tasks.d.ts.map +1 -0
  137. package/dist/data-access/tasks.js +40 -0
  138. package/dist/data-access/tasks.js.map +1 -0
  139. package/dist/data-access/tools.d.ts +277 -0
  140. package/dist/data-access/tools.d.ts.map +1 -0
  141. package/dist/data-access/tools.js +183 -0
  142. package/dist/data-access/tools.js.map +1 -0
  143. package/dist/data-access/validation.d.ts +17 -0
  144. package/dist/data-access/validation.d.ts.map +1 -0
  145. package/dist/data-access/validation.js +52 -0
  146. package/dist/data-access/validation.js.map +1 -0
  147. package/dist/db/clean.d.ts +6 -0
  148. package/dist/db/clean.d.ts.map +1 -0
  149. package/dist/db/clean.js +81 -0
  150. package/dist/db/clean.js.map +1 -0
  151. package/dist/db/client.d.ts +19 -0
  152. package/dist/db/client.d.ts.map +1 -0
  153. package/dist/db/client.js +24 -0
  154. package/dist/db/client.js.map +1 -0
  155. package/dist/db/schema.d.ts +4337 -0
  156. package/dist/db/schema.d.ts.map +1 -0
  157. package/dist/db/schema.js +696 -0
  158. package/dist/db/schema.js.map +1 -0
  159. package/dist/db/test-client.d.ts +25 -0
  160. package/dist/db/test-client.d.ts.map +1 -0
  161. package/dist/db/test-client.js +136 -0
  162. package/dist/db/test-client.js.map +1 -0
  163. package/dist/env.d.ts +17 -0
  164. package/dist/env.d.ts.map +1 -0
  165. package/dist/env.js +48 -0
  166. package/dist/env.js.map +1 -0
  167. package/dist/index.d.ts +13 -0
  168. package/dist/index.d.ts.map +1 -0
  169. package/dist/index.js +14 -0
  170. package/dist/index.js.map +1 -0
  171. package/dist/middleware/contextValidation.d.ts +48 -0
  172. package/dist/middleware/contextValidation.d.ts.map +1 -0
  173. package/dist/middleware/contextValidation.js +469 -0
  174. package/dist/middleware/contextValidation.js.map +1 -0
  175. package/dist/middleware/index.d.ts +2 -0
  176. package/dist/middleware/index.d.ts.map +1 -0
  177. package/dist/middleware/index.js +2 -0
  178. package/dist/middleware/index.js.map +1 -0
  179. package/dist/server/BaseServer.d.ts +83 -0
  180. package/dist/server/BaseServer.d.ts.map +1 -0
  181. package/dist/server/BaseServer.js +218 -0
  182. package/dist/server/BaseServer.js.map +1 -0
  183. package/dist/types/a2a.d.ts +373 -0
  184. package/dist/types/a2a.d.ts.map +1 -0
  185. package/dist/types/a2a.js +14 -0
  186. package/dist/types/a2a.js.map +1 -0
  187. package/dist/types/entities.d.ts +147 -0
  188. package/dist/types/entities.d.ts.map +1 -0
  189. package/dist/types/entities.js +2 -0
  190. package/dist/types/entities.js.map +1 -0
  191. package/dist/types/index.d.ts +5 -0
  192. package/dist/types/index.d.ts.map +1 -0
  193. package/dist/types/index.js +6 -0
  194. package/dist/types/index.js.map +1 -0
  195. package/dist/types/server.d.ts +116 -0
  196. package/dist/types/server.d.ts.map +1 -0
  197. package/dist/types/server.js +2 -0
  198. package/dist/types/server.js.map +1 -0
  199. package/dist/types/utility.d.ts +214 -0
  200. package/dist/types/utility.d.ts.map +1 -0
  201. package/dist/types/utility.js +9 -0
  202. package/dist/types/utility.js.map +1 -0
  203. package/dist/utils/apiKeys.d.ts +32 -0
  204. package/dist/utils/apiKeys.d.ts.map +1 -0
  205. package/dist/utils/apiKeys.js +117 -0
  206. package/dist/utils/apiKeys.js.map +1 -0
  207. package/dist/utils/auth-detection.d.ts +23 -0
  208. package/dist/utils/auth-detection.d.ts.map +1 -0
  209. package/dist/utils/auth-detection.js +148 -0
  210. package/dist/utils/auth-detection.js.map +1 -0
  211. package/dist/utils/credential-store-utils.d.ts +11 -0
  212. package/dist/utils/credential-store-utils.d.ts.map +1 -0
  213. package/dist/utils/credential-store-utils.js +19 -0
  214. package/dist/utils/credential-store-utils.js.map +1 -0
  215. package/dist/utils/error.d.ts +526 -0
  216. package/dist/utils/error.d.ts.map +1 -0
  217. package/dist/utils/error.js +282 -0
  218. package/dist/utils/error.js.map +1 -0
  219. package/dist/utils/execution.d.ts +18 -0
  220. package/dist/utils/execution.d.ts.map +1 -0
  221. package/dist/utils/execution.js +25 -0
  222. package/dist/utils/execution.js.map +1 -0
  223. package/dist/utils/index.d.ts +9 -0
  224. package/dist/utils/index.d.ts.map +1 -0
  225. package/dist/utils/index.js +9 -0
  226. package/dist/utils/index.js.map +1 -0
  227. package/dist/utils/logger.d.ts +79 -0
  228. package/dist/utils/logger.d.ts.map +1 -0
  229. package/dist/utils/logger.js +102 -0
  230. package/dist/utils/logger.js.map +1 -0
  231. package/dist/utils/logging.d.ts +11 -0
  232. package/dist/utils/logging.d.ts.map +1 -0
  233. package/dist/utils/logging.js +6 -0
  234. package/dist/utils/logging.js.map +1 -0
  235. package/dist/utils/mcp-client.d.ts +48 -0
  236. package/dist/utils/mcp-client.d.ts.map +1 -0
  237. package/dist/utils/mcp-client.js +171 -0
  238. package/dist/utils/mcp-client.js.map +1 -0
  239. package/dist/utils/tracer.d.ts +24 -0
  240. package/dist/utils/tracer.d.ts.map +1 -0
  241. package/dist/utils/tracer.js +98 -0
  242. package/dist/utils/tracer.js.map +1 -0
  243. package/dist/validation/graphFull.d.ts +36 -0
  244. package/dist/validation/graphFull.d.ts.map +1 -0
  245. package/dist/validation/graphFull.js +128 -0
  246. package/dist/validation/graphFull.js.map +1 -0
  247. package/dist/validation/id-validation.d.ts +38 -0
  248. package/dist/validation/id-validation.d.ts.map +1 -0
  249. package/dist/validation/id-validation.js +60 -0
  250. package/dist/validation/id-validation.js.map +1 -0
  251. package/dist/validation/index.d.ts +4 -0
  252. package/dist/validation/index.d.ts.map +1 -0
  253. package/dist/validation/index.js +5 -0
  254. package/dist/validation/index.js.map +1 -0
  255. package/dist/validation/schemas.d.ts +7233 -0
  256. package/dist/validation/schemas.d.ts.map +1 -0
  257. package/dist/validation/schemas.js +525 -0
  258. package/dist/validation/schemas.js.map +1 -0
  259. package/package.json +89 -0
@@ -0,0 +1,995 @@
1
+ import { validateAndTypeGraphData, validateGraphStructure, isInternalAgent, isExternalAgent, } from '../validation/graphFull.js';
2
+ import { and, eq } from 'drizzle-orm';
3
+ import { agents, projects } from '../db/schema.js';
4
+ import { upsertTool, upsertAgentToolRelation } from './tools.js';
5
+ import { upsertContextConfig } from './contextConfigs.js';
6
+ import { upsertDataComponent, upsertAgentDataComponentRelation, associateDataComponentWithAgent, deleteAgentDataComponentRelationByAgent, } from './dataComponents.js';
7
+ import { upsertArtifactComponent, upsertAgentArtifactComponentRelation, deleteAgentArtifactComponentRelationByAgent, associateArtifactComponentWithAgent, } from './artifactComponents.js';
8
+ import { upsertAgent } from './agents.js';
9
+ import { upsertExternalAgent } from './externalAgents.js';
10
+ import { getFullGraphDefinition, upsertAgentGraph, getAgentGraphById, updateAgentGraph, deleteAgentGraph, } from './agentGraphs.js';
11
+ import { upsertCredentialReference } from './credentialReferences.js';
12
+ import { deleteAgentToolRelationByAgent, upsertAgentRelation, createAgentToolRelation, deleteAgentRelationsByGraph, createAgentRelation, } from './agentRelations.js';
13
+ import { nanoid } from 'nanoid';
14
+ // Default no-op logger
15
+ const defaultLogger = {
16
+ info: () => { },
17
+ error: () => { },
18
+ };
19
+ /**
20
+ * Apply execution limits inheritance from project to graph and agents
21
+ */
22
+ async function applyExecutionLimitsInheritance(db, logger, scopes, graphData) {
23
+ const { tenantId, projectId } = scopes;
24
+ try {
25
+ // Get project stopWhen configuration
26
+ const project = await db.query.projects.findFirst({
27
+ where: and(eq(projects.tenantId, tenantId), eq(projects.id, projectId)),
28
+ });
29
+ if (!project?.stopWhen) {
30
+ logger.info({ projectId }, 'No project stopWhen configuration found');
31
+ return;
32
+ }
33
+ const projectStopWhen = project.stopWhen;
34
+ logger.info({
35
+ projectId,
36
+ projectStopWhen: projectStopWhen
37
+ }, 'Found project stopWhen configuration');
38
+ // Initialize graph stopWhen if not exists
39
+ if (!graphData.stopWhen) {
40
+ graphData.stopWhen = {};
41
+ }
42
+ // Inherit handoffCountIs from project if graph doesn't have it explicitly set
43
+ if (graphData.stopWhen.handoffCountIs === undefined && projectStopWhen?.handoffCountIs !== undefined) {
44
+ graphData.stopWhen.handoffCountIs = projectStopWhen.handoffCountIs;
45
+ logger.info({
46
+ graphId: graphData.id,
47
+ inheritedValue: projectStopWhen.handoffCountIs,
48
+ }, 'Graph inherited handoffCountIs from project');
49
+ }
50
+ // Set default handoffCountIs if still not set
51
+ if (graphData.stopWhen.handoffCountIs === undefined) {
52
+ graphData.stopWhen.handoffCountIs = 10;
53
+ logger.info({
54
+ graphId: graphData.id,
55
+ defaultValue: 10,
56
+ }, 'Graph set to default handoffCountIs');
57
+ }
58
+ // Propagate stepCountIs from project to agents
59
+ if (projectStopWhen?.stepCountIs !== undefined) {
60
+ logger.info({
61
+ projectId,
62
+ stepCountIs: projectStopWhen.stepCountIs,
63
+ }, 'Propagating stepCountIs to agents');
64
+ for (const [agentId, agentData] of Object.entries(graphData.agents)) {
65
+ // Only apply to internal agents (have instructions)
66
+ if (isInternalAgent(agentData)) {
67
+ const agent = agentData;
68
+ // Initialize agent stopWhen if it doesn't exist
69
+ if (!agent.stopWhen) {
70
+ agent.stopWhen = {};
71
+ }
72
+ // Set stepCountIs in stopWhen if not explicitly set
73
+ if (agent.stopWhen.stepCountIs === undefined) {
74
+ agent.stopWhen.stepCountIs = projectStopWhen.stepCountIs;
75
+ logger.info({
76
+ agentId,
77
+ inheritedValue: projectStopWhen.stepCountIs,
78
+ }, 'Agent inherited stepCountIs from project');
79
+ }
80
+ }
81
+ }
82
+ }
83
+ }
84
+ catch (error) {
85
+ logger.error({
86
+ projectId,
87
+ error: error instanceof Error ? error.message : 'Unknown error'
88
+ }, 'Failed to apply execution limits inheritance');
89
+ // Don't throw - inheritance failure shouldn't block graph creation
90
+ }
91
+ }
92
+ /**
93
+ * Server-side implementation of createFullGraph that performs actual database operations.
94
+ * This function creates a complete graph with all agents, tools, and relationships.
95
+ */
96
+ export const createFullGraphServerSide = (db, logger = defaultLogger) => async (scopes, graphData) => {
97
+ const { tenantId, projectId } = scopes;
98
+ const typed = validateAndTypeGraphData(graphData);
99
+ // Validate the graph structure
100
+ validateGraphStructure(typed);
101
+ // Apply inheritance logic for execution limits
102
+ await applyExecutionLimitsInheritance(db, logger, { tenantId, projectId }, typed);
103
+ try {
104
+ // Step 1: Create/update credential references first (tools and context configs depend on them)
105
+ if (typed.credentialReferences) {
106
+ logger.info({ credentialReferencesCount: typed.credentialReferences.length }, 'Processing credential references');
107
+ const credentialRefPromises = typed.credentialReferences.map(async (credData) => {
108
+ try {
109
+ logger.info({ credId: credData.id }, 'Processing credential reference');
110
+ await upsertCredentialReference(db)({
111
+ data: {
112
+ ...credData,
113
+ tenantId,
114
+ projectId,
115
+ },
116
+ });
117
+ logger.info({ credId: credData.id }, 'Credential reference processed successfully');
118
+ }
119
+ catch (error) {
120
+ logger.error({ credId: credData.id, error }, 'Failed to create/update credential reference');
121
+ throw error;
122
+ }
123
+ });
124
+ await Promise.all(credentialRefPromises);
125
+ logger.info({ credentialReferencesCount: typed.credentialReferences.length }, 'All credential references created/updated successfully');
126
+ }
127
+ // Step 2: Create/update tools (agents depend on them)
128
+ const toolPromises = Object.entries(typed.tools).map(async ([toolId, toolData]) => {
129
+ try {
130
+ logger.info({ toolId }, 'Processing tool');
131
+ await upsertTool(db)({
132
+ data: {
133
+ tenantId,
134
+ projectId,
135
+ ...toolData,
136
+ },
137
+ });
138
+ logger.info({ toolId }, 'Tool processed successfully');
139
+ }
140
+ catch (error) {
141
+ logger.error({ toolId, error }, 'Failed to create/update tool');
142
+ throw error;
143
+ }
144
+ });
145
+ await Promise.all(toolPromises);
146
+ logger.info({ toolCount: Object.keys(typed.tools).length }, 'All tools created/updated successfully');
147
+ // Step 3: create/update context config
148
+ let contextConfigId;
149
+ if (typed.contextConfig) {
150
+ try {
151
+ logger.info({ contextConfigId: typed.contextConfig.id }, 'Processing context config');
152
+ const contextConfig = await upsertContextConfig(db)({
153
+ data: {
154
+ ...typed.contextConfig,
155
+ tenantId,
156
+ projectId,
157
+ },
158
+ });
159
+ contextConfigId = contextConfig.id;
160
+ logger.info({ contextConfigId }, 'Context config processed successfully');
161
+ }
162
+ catch (error) {
163
+ logger.error({ contextConfigId: typed.contextConfig.id, error }, 'Failed to create/update context config');
164
+ throw error;
165
+ }
166
+ }
167
+ // Step 4: Create/update dataComponents (agents depend on them)
168
+ if (typed.dataComponents && Object.keys(typed.dataComponents).length > 0) {
169
+ const dataComponentPromises = Object.entries(typed.dataComponents).map(async ([dataComponentId, dataComponentData]) => {
170
+ try {
171
+ logger.info({ dataComponentId }, 'Processing data component');
172
+ await upsertDataComponent(db)({
173
+ data: {
174
+ id: dataComponentId,
175
+ tenantId,
176
+ projectId,
177
+ name: dataComponentData.name,
178
+ description: dataComponentData.description || '',
179
+ props: dataComponentData.props || {},
180
+ },
181
+ });
182
+ logger.info({ dataComponentId }, 'Data component processed successfully');
183
+ }
184
+ catch (error) {
185
+ logger.error({ dataComponentId, error }, 'Failed to create/update dataComponent');
186
+ throw error;
187
+ }
188
+ });
189
+ await Promise.all(dataComponentPromises);
190
+ logger.info({ dataComponentCount: Object.keys(typed.dataComponents).length }, 'All dataComponents created/updated successfully');
191
+ }
192
+ // Step 5: Create/update artifactComponents (agents depend on them)
193
+ if (typed.artifactComponents && Object.keys(typed.artifactComponents).length > 0) {
194
+ const artifactComponentPromises = Object.entries(typed.artifactComponents).map(async ([artifactComponentId, artifactComponentData]) => {
195
+ try {
196
+ logger.info({ artifactComponentId }, 'Processing artifact component');
197
+ await upsertArtifactComponent(db)({
198
+ data: {
199
+ id: artifactComponentId,
200
+ tenantId,
201
+ projectId,
202
+ name: artifactComponentData.name,
203
+ description: artifactComponentData.description || '',
204
+ summaryProps: artifactComponentData.summaryProps || {},
205
+ fullProps: artifactComponentData.fullProps || {},
206
+ },
207
+ });
208
+ logger.info({ artifactComponentId }, 'Artifact component processed successfully');
209
+ }
210
+ catch (error) {
211
+ logger.error({ artifactComponentId, error }, 'Failed to create/update artifactComponent');
212
+ throw error;
213
+ }
214
+ });
215
+ await Promise.all(artifactComponentPromises);
216
+ logger.info({ artifactComponentCount: Object.keys(typed.artifactComponents).length }, 'All artifactComponents created/updated successfully');
217
+ }
218
+ // Step 6: Create/update internal agents
219
+ const internalAgentPromises = Object.entries(typed.agents)
220
+ .filter(([_, agentData]) => isInternalAgent(agentData)) // Internal agents have instructions
221
+ .map(async ([agentId, agentData]) => {
222
+ // Type assertion since we've filtered for internal agents
223
+ const internalAgent = agentData;
224
+ try {
225
+ logger.info({ agentId }, 'Processing internal agent');
226
+ await upsertAgent(db)({
227
+ data: {
228
+ id: agentId,
229
+ tenantId,
230
+ projectId,
231
+ name: internalAgent.name || '',
232
+ description: internalAgent.description || '',
233
+ instructions: internalAgent.instructions || '',
234
+ conversationHistoryConfig: internalAgent.conversationHistoryConfig,
235
+ models: internalAgent.models,
236
+ stopWhen: internalAgent.stopWhen,
237
+ },
238
+ });
239
+ logger.info({ agentId }, 'Internal agent processed successfully');
240
+ }
241
+ catch (error) {
242
+ logger.error({ agentId, error }, 'Failed to create/update internal agent');
243
+ throw error;
244
+ }
245
+ });
246
+ await Promise.all(internalAgentPromises);
247
+ const internalAgentCount = Object.entries(typed.agents).filter(([_, agentData]) => isInternalAgent(agentData)).length;
248
+ logger.info({ internalAgentCount }, 'All internal agents created/updated successfully');
249
+ // Step 7: Create/update external agents
250
+ const externalAgentPromises = Object.entries(typed.agents)
251
+ .filter(([_, agentData]) => isExternalAgent(agentData)) // External agents have baseUrl
252
+ .map(async ([agentId, agentData]) => {
253
+ // Type assertion since we've filtered for external agents
254
+ const externalAgent = agentData;
255
+ try {
256
+ logger.info({ agentId }, 'Processing external agent');
257
+ await upsertExternalAgent(db)({
258
+ data: {
259
+ id: agentId,
260
+ tenantId,
261
+ projectId,
262
+ name: externalAgent.name,
263
+ description: externalAgent.description || '',
264
+ baseUrl: externalAgent.baseUrl,
265
+ credentialReferenceId: externalAgent.credentialReferenceId || undefined,
266
+ headers: externalAgent.headers || undefined,
267
+ },
268
+ });
269
+ logger.info({ agentId }, 'External agent processed successfully');
270
+ }
271
+ catch (error) {
272
+ logger.error({ agentId, error }, 'Failed to create/update external agent');
273
+ throw error;
274
+ }
275
+ });
276
+ await Promise.all(externalAgentPromises);
277
+ const externalAgentCount = Object.entries(typed.agents).filter(([_, agentData]) => isExternalAgent(agentData)).length;
278
+ logger.info({ externalAgentCount }, 'All external agents created/updated successfully');
279
+ // Step 8: Create the graph metadata (or update if exists for upsert behavior)
280
+ let finalGraphId;
281
+ try {
282
+ logger.info({ graphId: typed.id }, 'Processing agent graph metadata');
283
+ const agentGraph = await upsertAgentGraph(db)({
284
+ data: {
285
+ id: typed.id || nanoid(),
286
+ tenantId,
287
+ projectId,
288
+ name: typed.name,
289
+ defaultAgentId: typed.defaultAgentId,
290
+ description: typed.description,
291
+ contextConfigId,
292
+ models: typed.models,
293
+ statusUpdates: typed.statusUpdates,
294
+ graphPrompt: typed.graphPrompt,
295
+ stopWhen: typed.stopWhen,
296
+ },
297
+ });
298
+ finalGraphId = agentGraph.id;
299
+ logger.info({ graphId: finalGraphId }, 'Agent graph metadata processed successfully');
300
+ }
301
+ catch (error) {
302
+ logger.error({ graphId: typed.id, error }, 'Failed to create/update graph metadata');
303
+ throw error;
304
+ }
305
+ // Step 9: Create agent-tool relationships
306
+ const agentToolPromises = [];
307
+ for (const [agentId, agentData] of Object.entries(typed.agents)) {
308
+ if (isInternalAgent(agentData) && agentData.tools && Array.isArray(agentData.tools)) {
309
+ for (const toolId of agentData.tools) {
310
+ agentToolPromises.push((async () => {
311
+ try {
312
+ logger.info({ agentId, toolId }, 'Processing agent-tool relation');
313
+ await upsertAgentToolRelation(db)({
314
+ scopes: { tenantId, projectId },
315
+ agentId,
316
+ toolId,
317
+ });
318
+ logger.info({ agentId, toolId }, 'Agent-tool relation processed successfully');
319
+ }
320
+ catch (error) {
321
+ logger.error({ agentId, toolId, error }, 'Failed to create agent-tool relation');
322
+ // Don't throw - allow partial success for relations
323
+ }
324
+ })());
325
+ }
326
+ }
327
+ }
328
+ await Promise.all(agentToolPromises);
329
+ logger.info({ agentToolCount: Object.keys(typed.agents).length }, 'All agent-tool relations created');
330
+ // Step 10: Create agent-dataComponent relationships
331
+ const agentDataComponentPromises = [];
332
+ for (const [agentId, agentData] of Object.entries(typed.agents)) {
333
+ if (isInternalAgent(agentData) && agentData.dataComponents) {
334
+ for (const dataComponentId of agentData.dataComponents) {
335
+ agentDataComponentPromises.push((async () => {
336
+ try {
337
+ logger.info({ agentId, dataComponentId }, 'Processing agent-data component relation');
338
+ await upsertAgentDataComponentRelation(db)({
339
+ scopes: { tenantId, projectId },
340
+ agentId,
341
+ dataComponentId,
342
+ });
343
+ logger.info({ agentId, dataComponentId }, 'Agent-data component relation processed successfully');
344
+ }
345
+ catch (error) {
346
+ logger.error({ agentId, dataComponentId, error }, 'Failed to create agent-data component relation');
347
+ // Don't throw - allow partial success for relations
348
+ }
349
+ })());
350
+ }
351
+ }
352
+ }
353
+ await Promise.all(agentDataComponentPromises);
354
+ logger.info({}, 'All agent-data component relations created');
355
+ // Step 11: Create agent-artifactComponent relationships
356
+ const agentArtifactComponentPromises = [];
357
+ for (const [agentId, agentData] of Object.entries(typed.agents)) {
358
+ if (isInternalAgent(agentData) && agentData.artifactComponents) {
359
+ for (const artifactComponentId of agentData.artifactComponents) {
360
+ agentArtifactComponentPromises.push((async () => {
361
+ try {
362
+ logger.info({ agentId, artifactComponentId }, 'Processing agent-artifact component relation');
363
+ await upsertAgentArtifactComponentRelation(db)({
364
+ scopes: { tenantId, projectId },
365
+ agentId,
366
+ artifactComponentId,
367
+ });
368
+ logger.info({ agentId, artifactComponentId }, 'Agent-artifact component relation processed successfully');
369
+ }
370
+ catch (error) {
371
+ logger.error({ agentId, artifactComponentId, error }, 'Failed to create agent-artifact component relation');
372
+ // Don't throw - allow partial success for relations
373
+ }
374
+ })());
375
+ }
376
+ }
377
+ }
378
+ await Promise.all(agentArtifactComponentPromises);
379
+ logger.info({}, 'All agent-artifact component relations created');
380
+ // Step 12: Create agent relationships (handoff/delegation)
381
+ const agentRelationPromises = [];
382
+ for (const [agentId, agentData] of Object.entries(typed.agents)) {
383
+ // Create handoff relations
384
+ if (isInternalAgent(agentData) && agentData.canHandOffTo) {
385
+ for (const targetAgentId of agentData.canHandOffTo) {
386
+ agentRelationPromises.push((async () => {
387
+ try {
388
+ logger.info({ agentId, targetAgentId, type: 'handoff' }, 'Processing agent handoff relation');
389
+ await upsertAgentRelation(db)({
390
+ id: nanoid(),
391
+ tenantId,
392
+ projectId,
393
+ graphId: finalGraphId,
394
+ sourceAgentId: agentId,
395
+ targetAgentId,
396
+ relationType: 'handoff',
397
+ });
398
+ logger.info({ agentId, targetAgentId, type: 'handoff' }, 'Agent handoff relation processed successfully');
399
+ }
400
+ catch (error) {
401
+ logger.error({ agentId, targetAgentId, type: 'handoff', error }, 'Failed to create handoff relation');
402
+ }
403
+ })());
404
+ }
405
+ }
406
+ // Create delegation relations
407
+ if (isInternalAgent(agentData) && agentData.canDelegateTo) {
408
+ for (const targetAgentId of agentData.canDelegateTo) {
409
+ // Check if the target agent is external by looking it up in the typed.agents
410
+ const targetAgentData = typed.agents[targetAgentId];
411
+ const isTargetExternal = isExternalAgent(targetAgentData);
412
+ agentRelationPromises.push((async () => {
413
+ try {
414
+ logger.info({ agentId, targetAgentId, type: 'delegate' }, 'Processing agent delegation relation');
415
+ await upsertAgentRelation(db)({
416
+ id: nanoid(),
417
+ tenantId,
418
+ projectId,
419
+ graphId: finalGraphId,
420
+ sourceAgentId: agentId,
421
+ targetAgentId: isTargetExternal ? undefined : targetAgentId,
422
+ externalAgentId: isTargetExternal ? targetAgentId : undefined,
423
+ relationType: 'delegate',
424
+ });
425
+ logger.info({ agentId, targetAgentId, type: 'delegate' }, 'Agent delegation relation processed successfully');
426
+ }
427
+ catch (error) {
428
+ logger.error({ agentId, targetAgentId, type: 'delegate', error }, 'Failed to create delegation relation');
429
+ }
430
+ })());
431
+ }
432
+ }
433
+ }
434
+ await Promise.all(agentRelationPromises);
435
+ logger.info({ agentRelationCount: agentRelationPromises.length }, 'All agent relations created');
436
+ // Retrieve and return the created graph
437
+ const createdGraph = await getFullGraphDefinition(db)({
438
+ scopes: { tenantId, projectId },
439
+ graphId: finalGraphId,
440
+ });
441
+ if (!createdGraph) {
442
+ throw new Error('Failed to retrieve created graph');
443
+ }
444
+ logger.info({ tenantId, graphId: finalGraphId }, 'Full graph created successfully');
445
+ return createdGraph;
446
+ }
447
+ catch (error) {
448
+ const errorGraphId = typed.id || 'unknown';
449
+ logger.error({ tenantId, graphId: errorGraphId, error }, 'Failed to create full graph');
450
+ throw error;
451
+ }
452
+ };
453
+ /**
454
+ * Server-side implementation of updateFullGraph that performs actual database operations.
455
+ * This function updates a complete graph with all agents, tools, and relationships.
456
+ */
457
+ export const updateFullGraphServerSide = (db, logger = defaultLogger) => async (scopes, graphData) => {
458
+ const { tenantId, projectId } = scopes;
459
+ const typedGraphDefinition = validateAndTypeGraphData(graphData);
460
+ if (!typedGraphDefinition.id) {
461
+ throw new Error('Graph ID is required');
462
+ }
463
+ logger.info({
464
+ tenantId,
465
+ graphId: typedGraphDefinition.id,
466
+ agentCount: Object.keys(typedGraphDefinition.agents).length,
467
+ toolCount: Object.keys(typedGraphDefinition.tools).length,
468
+ }, 'Updating full graph in database');
469
+ // Validate the graph structure
470
+ validateGraphStructure(typedGraphDefinition);
471
+ // Apply inheritance logic for execution limits
472
+ await applyExecutionLimitsInheritance(db, logger, { tenantId, projectId }, typedGraphDefinition);
473
+ try {
474
+ // Verify graph exists and get existing models for cascade logic
475
+ const existingGraph = await getAgentGraphById(db)({
476
+ scopes: { tenantId, projectId },
477
+ graphId: typedGraphDefinition.id,
478
+ });
479
+ if (!existingGraph) {
480
+ // If graph doesn't exist, create it (upsert behavior)
481
+ logger.info({ graphId: typedGraphDefinition.id }, 'Graph does not exist, creating new graph');
482
+ return createFullGraphServerSide(db)(scopes, graphData);
483
+ }
484
+ // Store existing graph models for cascade comparison
485
+ const existingGraphModels = existingGraph.models;
486
+ // Step 1: Create/update credential references first (tools and context configs depend on them)
487
+ if (typedGraphDefinition.credentialReferences) {
488
+ logger.info({ credentialReferencesCount: typedGraphDefinition.credentialReferences.length }, 'Processing credential references');
489
+ const credentialRefPromises = typedGraphDefinition.credentialReferences.map(async (credData) => {
490
+ try {
491
+ logger.info({ credId: credData.id }, 'Processing credential reference');
492
+ await upsertCredentialReference(db)({
493
+ data: {
494
+ ...credData,
495
+ tenantId,
496
+ projectId,
497
+ },
498
+ });
499
+ logger.info({ credId: credData.id }, 'Credential reference processed successfully');
500
+ }
501
+ catch (error) {
502
+ logger.error({ credId: credData.id, error }, 'Failed to create/update credential reference');
503
+ throw error;
504
+ }
505
+ });
506
+ await Promise.all(credentialRefPromises);
507
+ logger.info({ credentialReferencesCount: typedGraphDefinition.credentialReferences.length }, 'All credential references created/updated successfully');
508
+ }
509
+ // Step 2: Create/update tools (agents depend on them)
510
+ const toolPromises = Object.entries(typedGraphDefinition.tools).map(async ([toolId, toolData]) => {
511
+ try {
512
+ logger.info({ toolId }, 'Processing tool');
513
+ await upsertTool(db)({
514
+ data: {
515
+ tenantId,
516
+ projectId,
517
+ ...toolData,
518
+ },
519
+ });
520
+ logger.info({ toolId }, 'Tool processed successfully');
521
+ }
522
+ catch (error) {
523
+ logger.error({ toolId, error }, 'Failed to create/update tool');
524
+ throw error;
525
+ }
526
+ });
527
+ await Promise.all(toolPromises);
528
+ logger.info({ toolCount: Object.keys(typedGraphDefinition.tools).length }, 'All tools created/updated successfully');
529
+ // Step 3: create/update context config
530
+ let contextConfigId;
531
+ if (typedGraphDefinition.contextConfig) {
532
+ try {
533
+ logger.info({ contextConfigId: typedGraphDefinition.contextConfig.id }, 'Processing context config');
534
+ const contextConfig = await upsertContextConfig(db)({
535
+ data: {
536
+ ...typedGraphDefinition.contextConfig,
537
+ tenantId,
538
+ projectId,
539
+ },
540
+ });
541
+ contextConfigId = contextConfig.id;
542
+ logger.info({ contextConfigId }, 'Context config processed successfully');
543
+ }
544
+ catch (error) {
545
+ logger.error({ contextConfigId: typedGraphDefinition.contextConfig.id, error }, 'Failed to create/update context config');
546
+ throw error;
547
+ }
548
+ }
549
+ // Step 4: Create/update dataComponents (agents depend on them)
550
+ if (typedGraphDefinition.dataComponents &&
551
+ Object.keys(typedGraphDefinition.dataComponents).length > 0) {
552
+ const dataComponentPromises = Object.entries(typedGraphDefinition.dataComponents).map(async ([dataComponentId, dataComponentData]) => {
553
+ try {
554
+ logger.info({ dataComponentId }, 'Processing data component');
555
+ await upsertDataComponent(db)({
556
+ data: {
557
+ id: dataComponentId,
558
+ tenantId,
559
+ projectId,
560
+ name: dataComponentData.name,
561
+ description: dataComponentData.description || '',
562
+ props: dataComponentData.props || {},
563
+ },
564
+ });
565
+ logger.info({ dataComponentId }, 'Data component processed successfully');
566
+ }
567
+ catch (error) {
568
+ logger.error({ dataComponentId, error }, 'Failed to create/update dataComponent');
569
+ throw error;
570
+ }
571
+ });
572
+ await Promise.all(dataComponentPromises);
573
+ logger.info({ dataComponentCount: Object.keys(typedGraphDefinition.dataComponents).length }, 'All dataComponents created/updated successfully');
574
+ }
575
+ // Step 5: Create/update artifactComponents (agents depend on them)
576
+ if (typedGraphDefinition.artifactComponents &&
577
+ Object.keys(typedGraphDefinition.artifactComponents).length > 0) {
578
+ const artifactComponentPromises = Object.entries(typedGraphDefinition.artifactComponents).map(async ([artifactComponentId, artifactComponentData]) => {
579
+ try {
580
+ logger.info({ artifactComponentId }, 'Processing artifact component');
581
+ await upsertArtifactComponent(db)({
582
+ data: {
583
+ id: artifactComponentId,
584
+ tenantId,
585
+ projectId,
586
+ name: artifactComponentData.name,
587
+ description: artifactComponentData.description || '',
588
+ summaryProps: artifactComponentData.summaryProps || {},
589
+ fullProps: artifactComponentData.fullProps || {},
590
+ },
591
+ });
592
+ logger.info({ artifactComponentId }, 'Artifact component processed successfully');
593
+ }
594
+ catch (error) {
595
+ logger.error({ artifactComponentId, error }, 'Failed to create/update artifactComponent');
596
+ throw error;
597
+ }
598
+ });
599
+ await Promise.all(artifactComponentPromises);
600
+ logger.info({ artifactComponentCount: Object.keys(typedGraphDefinition.artifactComponents).length }, 'All artifactComponents created/updated successfully');
601
+ }
602
+ // Step 6: Create/update internal agents with model cascade logic
603
+ const internalAgentPromises = Object.entries(typedGraphDefinition.agents)
604
+ .filter(([_, agentData]) => isInternalAgent(agentData)) // Internal agents have instructions
605
+ .map(async ([agentId, agentData]) => {
606
+ const internalAgent = agentData;
607
+ // Get the existing agent to check for inheritance
608
+ let existingAgent = null;
609
+ try {
610
+ existingAgent = await db.query.agents.findFirst({
611
+ where: and(eq(agents.id, agentId), eq(agents.tenantId, tenantId), eq(agents.projectId, projectId)),
612
+ columns: {
613
+ models: true,
614
+ },
615
+ });
616
+ }
617
+ catch (error) {
618
+ // Agent might not exist yet, that's ok
619
+ }
620
+ // Determine final model settings with cascade logic
621
+ let finalModelSettings = internalAgent.models === undefined ? {} : internalAgent.models;
622
+ // If graph models changed, cascade to agents that were inheriting
623
+ if (existingAgent && existingAgent.models && typedGraphDefinition.models) {
624
+ const agentModels = existingAgent.models;
625
+ const graphModels = typedGraphDefinition.models;
626
+ // Check each model type for inheritance and cascade if needed
627
+ const modelTypes = ['base', 'structuredOutput', 'summarizer'];
628
+ const cascadedModels = { ...finalModelSettings };
629
+ for (const modelType of modelTypes) {
630
+ // If the agent's current model matches the old graph model (was inheriting)
631
+ // and the graph model has changed, cascade the change
632
+ if (agentModels[modelType]?.model &&
633
+ existingGraphModels?.[modelType]?.model &&
634
+ agentModels[modelType].model === existingGraphModels[modelType].model &&
635
+ graphModels[modelType]?.model &&
636
+ graphModels[modelType].model !== existingGraphModels[modelType].model) {
637
+ // Agent was inheriting from graph, cascade the new value
638
+ cascadedModels[modelType] = {
639
+ ...cascadedModels[modelType],
640
+ model: graphModels[modelType].model,
641
+ };
642
+ logger.info({
643
+ agentId,
644
+ modelType,
645
+ oldModel: agentModels[modelType].model,
646
+ newModel: graphModels[modelType].model,
647
+ }, 'Cascading model change from graph to agent');
648
+ }
649
+ }
650
+ finalModelSettings = cascadedModels;
651
+ }
652
+ try {
653
+ logger.info({ agentId }, 'Processing internal agent');
654
+ await upsertAgent(db)({
655
+ data: {
656
+ id: agentId,
657
+ tenantId,
658
+ projectId,
659
+ name: internalAgent.name || '',
660
+ description: internalAgent.description || '',
661
+ instructions: internalAgent.instructions || '',
662
+ conversationHistoryConfig: internalAgent.conversationHistoryConfig,
663
+ models: finalModelSettings,
664
+ stopWhen: internalAgent.stopWhen,
665
+ },
666
+ });
667
+ logger.info({ agentId }, 'Internal agent processed successfully');
668
+ }
669
+ catch (error) {
670
+ logger.error({ agentId, error }, 'Failed to create/update internal agent');
671
+ throw error;
672
+ }
673
+ });
674
+ await Promise.all(internalAgentPromises);
675
+ const internalAgentCount = Object.entries(typedGraphDefinition.agents).filter(([_, agentData]) => isInternalAgent(agentData)).length;
676
+ logger.info({ internalAgentCount }, 'All internal agents created/updated successfully');
677
+ // Step 7: Create/update external agents
678
+ const externalAgentPromises = Object.entries(typedGraphDefinition.agents)
679
+ .filter(([_, agentData]) => isExternalAgent(agentData)) // External agents have baseUrl
680
+ .map(async ([agentId, agentData]) => {
681
+ // Type assertion since we've filtered for external agents
682
+ const externalAgent = agentData;
683
+ try {
684
+ logger.info({ agentId }, 'Processing external agent');
685
+ await upsertExternalAgent(db)({
686
+ data: {
687
+ id: agentId,
688
+ tenantId,
689
+ projectId,
690
+ name: externalAgent.name,
691
+ description: externalAgent.description || '',
692
+ baseUrl: externalAgent.baseUrl,
693
+ credentialReferenceId: externalAgent.credentialReferenceId || undefined,
694
+ headers: externalAgent.headers || undefined,
695
+ },
696
+ });
697
+ logger.info({ agentId }, 'External agent processed successfully');
698
+ }
699
+ catch (error) {
700
+ logger.error({ agentId, error }, 'Failed to create/update external agent');
701
+ throw error;
702
+ }
703
+ });
704
+ await Promise.all(externalAgentPromises);
705
+ const externalAgentCount = Object.entries(typedGraphDefinition.agents).filter(([_, agentData]) => isExternalAgent(agentData)).length;
706
+ logger.info({ externalAgentCount }, 'All external agents created/updated successfully');
707
+ // Step 8: Update the graph metadata
708
+ await updateAgentGraph(db)({
709
+ scopes: { tenantId, projectId },
710
+ graphId: typedGraphDefinition.id,
711
+ data: {
712
+ name: typedGraphDefinition.name,
713
+ defaultAgentId: typedGraphDefinition.defaultAgentId,
714
+ description: typedGraphDefinition.description,
715
+ contextConfigId: contextConfigId,
716
+ models: typedGraphDefinition.models,
717
+ statusUpdates: typedGraphDefinition.statusUpdates,
718
+ graphPrompt: typedGraphDefinition.graphPrompt,
719
+ stopWhen: typedGraphDefinition.stopWhen,
720
+ },
721
+ });
722
+ logger.info({ graphId: typedGraphDefinition.id }, 'Graph metadata updated');
723
+ // Step 9: Clear and recreate agent-tool relationships
724
+ // First, delete existing relationships for all agents in this graph
725
+ for (const agentId of Object.keys(typedGraphDefinition.agents)) {
726
+ await deleteAgentToolRelationByAgent(db)({
727
+ scopes: { tenantId, projectId },
728
+ agentId,
729
+ });
730
+ }
731
+ // Then create new relationships
732
+ const agentToolPromises = [];
733
+ for (const [agentId, agentData] of Object.entries(typedGraphDefinition.agents)) {
734
+ if (isInternalAgent(agentData) && agentData.tools && Array.isArray(agentData.tools)) {
735
+ for (const toolId of agentData.tools) {
736
+ agentToolPromises.push((async () => {
737
+ try {
738
+ await createAgentToolRelation(db)({
739
+ scopes: { tenantId, projectId },
740
+ data: {
741
+ agentId,
742
+ toolId,
743
+ },
744
+ });
745
+ logger.info({ agentId, toolId }, 'Agent-tool relation created');
746
+ }
747
+ catch (error) {
748
+ logger.error({ agentId, toolId, error }, 'Failed to create agent-tool relation');
749
+ // Don't throw - allow partial success for relations
750
+ }
751
+ })());
752
+ }
753
+ }
754
+ }
755
+ await Promise.all(agentToolPromises);
756
+ logger.info({ agentToolPromisesCount: agentToolPromises.length }, 'All agent-tool relations updated');
757
+ // Step 10: Clear and recreate agent-dataComponent relationships
758
+ // First, delete existing relationships for all agents in this graph
759
+ for (const agentId of Object.keys(typedGraphDefinition.agents)) {
760
+ await deleteAgentDataComponentRelationByAgent(db)({
761
+ scopes: { tenantId, projectId },
762
+ agentId,
763
+ });
764
+ }
765
+ // Then create new agent-dataComponent relationships
766
+ const agentDataComponentPromises = [];
767
+ for (const [agentId, agentData] of Object.entries(typedGraphDefinition.agents)) {
768
+ if (isInternalAgent(agentData) && agentData.dataComponents) {
769
+ for (const dataComponentId of agentData.dataComponents) {
770
+ agentDataComponentPromises.push((async () => {
771
+ try {
772
+ await associateDataComponentWithAgent(db)({
773
+ scopes: { tenantId, projectId },
774
+ agentId,
775
+ dataComponentId,
776
+ });
777
+ logger.info({ agentId, dataComponentId }, 'Agent-dataComponent relation created');
778
+ }
779
+ catch (error) {
780
+ logger.error({ agentId, dataComponentId, error }, 'Failed to create agent-dataComponent relation');
781
+ // Don't throw - allow partial success for relations
782
+ }
783
+ })());
784
+ }
785
+ }
786
+ }
787
+ await Promise.all(agentDataComponentPromises);
788
+ logger.info({ agentDataComponentPromisesCount: agentDataComponentPromises.length }, 'All agent-dataComponent relations updated');
789
+ // Step 11: Clear and recreate agent-artifactComponent relationships
790
+ // First, delete existing relationships for all agents in this graph
791
+ for (const agentId of Object.keys(typedGraphDefinition.agents)) {
792
+ await deleteAgentArtifactComponentRelationByAgent(db)({
793
+ scopes: { tenantId, projectId },
794
+ agentId,
795
+ });
796
+ }
797
+ // Then create new agent-artifactComponent relationships
798
+ const agentArtifactComponentPromises = [];
799
+ for (const [agentId, agentData] of Object.entries(typedGraphDefinition.agents)) {
800
+ if (isInternalAgent(agentData) && agentData.artifactComponents) {
801
+ for (const artifactComponentId of agentData.artifactComponents) {
802
+ agentArtifactComponentPromises.push((async () => {
803
+ try {
804
+ await associateArtifactComponentWithAgent(db)({
805
+ scopes: { tenantId, projectId },
806
+ agentId,
807
+ artifactComponentId,
808
+ });
809
+ logger.info({ agentId, artifactComponentId }, 'Agent-artifactComponent relation created');
810
+ }
811
+ catch (error) {
812
+ logger.error({ agentId, artifactComponentId, error }, 'Failed to create agent-artifactComponent relation');
813
+ // Don't throw - allow partial success for relations
814
+ }
815
+ })());
816
+ }
817
+ }
818
+ }
819
+ await Promise.all(agentArtifactComponentPromises);
820
+ logger.info({ agentArtifactComponentPromisesCount: agentArtifactComponentPromises.length }, 'All agent-artifactComponent relations updated');
821
+ // Step 12: Clear and recreate agent relationships
822
+ // First, delete existing relationships for this graph
823
+ await deleteAgentRelationsByGraph(db)({
824
+ scopes: { tenantId, projectId },
825
+ graphId: typedGraphDefinition.id,
826
+ });
827
+ // Then create new relationships
828
+ const agentRelationPromises = [];
829
+ for (const [agentId, agentData] of Object.entries(typedGraphDefinition.agents)) {
830
+ // Create handoff relations
831
+ if (isInternalAgent(agentData) && agentData.canHandOffTo) {
832
+ for (const targetAgentId of agentData.canHandOffTo) {
833
+ agentRelationPromises.push((async () => {
834
+ try {
835
+ // Check if the target agent is external by looking it up in the typed.agents
836
+ const targetAgentData = typedGraphDefinition.agents[targetAgentId];
837
+ const isTargetExternal = isExternalAgent(targetAgentData);
838
+ const targetField = isTargetExternal ? 'externalAgentId' : 'targetAgentId';
839
+ const relationData = {
840
+ id: nanoid(),
841
+ graphId: typedGraphDefinition.id || '',
842
+ sourceAgentId: agentId,
843
+ relationType: 'handoff',
844
+ [targetField]: targetAgentId,
845
+ };
846
+ await createAgentRelation(db)({
847
+ tenantId,
848
+ projectId,
849
+ ...relationData,
850
+ });
851
+ logger.info({ agentId, targetAgentId, isTargetExternal }, 'Handoff relation created');
852
+ }
853
+ catch (error) {
854
+ logger.error({ agentId, targetAgentId, error }, 'Failed to create handoff relation');
855
+ }
856
+ })());
857
+ }
858
+ }
859
+ // Create delegation relations
860
+ if (isInternalAgent(agentData) && agentData.canDelegateTo) {
861
+ for (const targetAgentId of agentData.canDelegateTo) {
862
+ // External agents can't delegate to other agents
863
+ // Check if the target agent is external by looking it up in the typed.agents
864
+ const targetAgentData = typedGraphDefinition.agents[targetAgentId];
865
+ const isTargetExternal = isExternalAgent(targetAgentData);
866
+ const targetField = isTargetExternal ? 'externalAgentId' : 'targetAgentId';
867
+ agentRelationPromises.push((async () => {
868
+ try {
869
+ const relationData = {
870
+ id: nanoid(),
871
+ graphId: typedGraphDefinition.id || '',
872
+ sourceAgentId: agentId,
873
+ relationType: 'delegate',
874
+ [targetField]: targetAgentId,
875
+ };
876
+ await createAgentRelation(db)({
877
+ tenantId,
878
+ projectId,
879
+ ...relationData,
880
+ });
881
+ logger.info({ agentId, targetAgentId }, 'Delegation relation created');
882
+ }
883
+ catch (error) {
884
+ logger.error({ agentId, targetAgentId, error }, 'Failed to create delegation relation');
885
+ }
886
+ })());
887
+ }
888
+ }
889
+ }
890
+ await Promise.all(agentRelationPromises);
891
+ logger.info({ agentRelationPromisesCount: agentRelationPromises.length }, 'All agent relations updated');
892
+ // Retrieve and return the updated graph
893
+ const updatedGraph = await getFullGraphDefinition(db)({
894
+ scopes: { tenantId, projectId },
895
+ graphId: typedGraphDefinition.id,
896
+ });
897
+ if (!updatedGraph) {
898
+ throw new Error('Failed to retrieve updated graph');
899
+ }
900
+ logger.info({ graphId: typedGraphDefinition.id }, 'Full graph updated successfully');
901
+ return updatedGraph;
902
+ }
903
+ catch (error) {
904
+ logger.error({ graphId: typedGraphDefinition.id, error }, 'Failed to update full graph');
905
+ throw error;
906
+ }
907
+ };
908
+ /**
909
+ * Get a complete graph definition by ID
910
+ */
911
+ export const getFullGraph = (db, logger = defaultLogger) => async (params) => {
912
+ const { scopes, graphId } = params;
913
+ const { tenantId, projectId } = scopes;
914
+ logger.info({ tenantId, graphId }, 'Retrieving full graph definition');
915
+ try {
916
+ const graph = await getFullGraphDefinition(db)({
917
+ scopes: { tenantId, projectId },
918
+ graphId,
919
+ });
920
+ if (!graph) {
921
+ logger.info({ tenantId, graphId }, 'Graph not found');
922
+ return null;
923
+ }
924
+ logger.info({
925
+ tenantId,
926
+ graphId,
927
+ agentCount: Object.keys(graph.agents).length,
928
+ }, 'Full graph retrieved successfully');
929
+ return graph;
930
+ }
931
+ catch (error) {
932
+ logger.error({
933
+ tenantId,
934
+ graphId,
935
+ error: error instanceof Error ? error.message : 'Unknown error',
936
+ }, 'Failed to retrieve full graph');
937
+ throw error;
938
+ }
939
+ };
940
+ /**
941
+ * Delete a complete graph and cascade to all related entities
942
+ */
943
+ export const deleteFullGraph = (db, logger = defaultLogger) => async (params) => {
944
+ const { scopes, graphId } = params;
945
+ const { tenantId, projectId } = scopes;
946
+ logger.info({ tenantId, graphId }, 'Deleting full graph and related entities');
947
+ try {
948
+ // Get the graph first to ensure it exists
949
+ const graph = await getFullGraphDefinition(db)({
950
+ scopes: { tenantId, projectId },
951
+ graphId,
952
+ });
953
+ if (!graph) {
954
+ logger.info({ tenantId, graphId }, 'Graph not found for deletion');
955
+ return false;
956
+ }
957
+ // Step 1: Delete all agent relations for this graph
958
+ await deleteAgentRelationsByGraph(db)({
959
+ scopes: { tenantId, projectId },
960
+ graphId,
961
+ });
962
+ logger.info({ tenantId, graphId }, 'Agent relations deleted');
963
+ // Step 2: Delete agent-tool relations for agents in this graph
964
+ const agentIds = Object.keys(graph.agents);
965
+ if (agentIds.length > 0) {
966
+ // Delete agent-tool relations for all agents in this graph
967
+ for (const agentId of agentIds) {
968
+ await deleteAgentToolRelationByAgent(db)({
969
+ scopes: { tenantId, projectId },
970
+ agentId,
971
+ });
972
+ }
973
+ logger.info({ tenantId, graphId, agentCount: agentIds.length }, 'Agent-tool relations deleted');
974
+ }
975
+ // Step 3: Delete the graph metadata
976
+ await deleteAgentGraph(db)({
977
+ scopes: { tenantId, projectId },
978
+ graphId,
979
+ });
980
+ logger.info({ tenantId, graphId }, 'Graph metadata deleted');
981
+ // Note: We don't delete agents or tools themselves as they might be used in other graphs
982
+ // Only relationships specific to this graph are deleted
983
+ logger.info({ tenantId, graphId }, 'Full graph deleted successfully');
984
+ return true;
985
+ }
986
+ catch (error) {
987
+ logger.error({
988
+ tenantId,
989
+ graphId,
990
+ error: error instanceof Error ? error.message : 'Unknown error',
991
+ }, 'Failed to delete full graph');
992
+ throw error;
993
+ }
994
+ };
995
+ //# sourceMappingURL=graphFull.js.map