@eventcatalog/core 3.40.1 → 3.41.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 (139) hide show
  1. package/bin/eventcatalog.config.d.ts +1 -0
  2. package/dist/__mocks__/astro-content.d.cts +1 -1
  3. package/dist/__mocks__/astro-content.d.ts +1 -1
  4. package/dist/analytics/analytics.cjs +1 -1
  5. package/dist/analytics/analytics.js +2 -2
  6. package/dist/analytics/count-resources.cjs +1 -0
  7. package/dist/analytics/count-resources.js +1 -1
  8. package/dist/analytics/log-build.cjs +3 -2
  9. package/dist/analytics/log-build.js +4 -4
  10. package/dist/catalog-to-astro-content-directory.cjs +1 -0
  11. package/dist/catalog-to-astro-content-directory.js +2 -2
  12. package/dist/{chunk-4UVFXLPI.js → chunk-3DVHEVHQ.js} +1 -0
  13. package/dist/{chunk-K3ZVEX2Y.js → chunk-3H2RT3CM.js} +1 -1
  14. package/dist/{chunk-BRMLU4PR.js → chunk-3R6TKNHG.js} +1 -1
  15. package/dist/{chunk-OIVICT4V.js → chunk-DKFIEB24.js} +1 -1
  16. package/dist/{chunk-55D645EH.js → chunk-IR4IAKWS.js} +1 -0
  17. package/dist/{chunk-YDXB3BD2.js → chunk-O6KT4DPL.js} +1 -1
  18. package/dist/{chunk-D6IBLY3O.js → chunk-QMORF42U.js} +1 -0
  19. package/dist/{chunk-HNG4KOYQ.js → chunk-QVJGIQYP.js} +1 -1
  20. package/dist/{chunk-4OEF5W6Y.js → chunk-TWFS6THS.js} +1 -1
  21. package/dist/{chunk-7UR72UMK.js → chunk-ZN3JKTWB.js} +5 -5
  22. package/dist/constants.cjs +1 -1
  23. package/dist/constants.js +1 -1
  24. package/dist/eventcatalog.cjs +5 -2
  25. package/dist/eventcatalog.config.d.cts +27 -11
  26. package/dist/eventcatalog.config.d.ts +27 -11
  27. package/dist/eventcatalog.js +14 -14
  28. package/dist/generate.cjs +1 -1
  29. package/dist/generate.js +3 -3
  30. package/dist/map-catalog-to-astro.cjs +1 -0
  31. package/dist/map-catalog-to-astro.js +1 -1
  32. package/dist/search-indexer.cjs +1 -0
  33. package/dist/search-indexer.js +1 -1
  34. package/dist/utils/cli-logger.cjs +1 -1
  35. package/dist/utils/cli-logger.js +2 -2
  36. package/dist/watcher.cjs +1 -0
  37. package/dist/watcher.js +2 -2
  38. package/eventcatalog/public/agents/anthropic-dark.svg +1 -0
  39. package/eventcatalog/public/agents/anthropic-light.svg +1 -0
  40. package/eventcatalog/public/agents/openai-dark.svg +1 -0
  41. package/eventcatalog/public/agents/openai-light.svg +1 -0
  42. package/eventcatalog/public/agents/openai.svg +1 -0
  43. package/eventcatalog/public/icons/agent/anthropic-dark.svg +1 -0
  44. package/eventcatalog/public/icons/agent/anthropic-light.svg +1 -0
  45. package/eventcatalog/public/icons/agent/anthropic.svg +1 -0
  46. package/eventcatalog/public/icons/agent/gemini.svg +1 -0
  47. package/eventcatalog/public/icons/agent/openai-dark.svg +1 -0
  48. package/eventcatalog/public/icons/agent/openai-light.svg +1 -0
  49. package/eventcatalog/public/icons/agent/openai.svg +1 -0
  50. package/eventcatalog/public/icons/agents/anthropic-dark.svg +1 -0
  51. package/eventcatalog/public/icons/agents/anthropic-light.svg +1 -0
  52. package/eventcatalog/public/icons/agents/anthropic.svg +1 -0
  53. package/eventcatalog/public/icons/agents/gemini.svg +1 -0
  54. package/eventcatalog/public/icons/agents/openai-dark.svg +1 -0
  55. package/eventcatalog/public/icons/agents/openai-light.svg +1 -0
  56. package/eventcatalog/public/icons/agents/openai.svg +1 -0
  57. package/eventcatalog/public/icons/protocols/mcp-dark.svg +1 -0
  58. package/eventcatalog/public/icons/protocols/mcp-light.svg +1 -0
  59. package/eventcatalog/public/icons/protocols/mcp.svg +1 -0
  60. package/eventcatalog/public/icons/tools/datadog.svg +1 -0
  61. package/eventcatalog/public/icons/tools/github.svg +1 -0
  62. package/eventcatalog/public/icons/tools/hubspot.svg +1 -0
  63. package/eventcatalog/public/icons/tools/slack.svg +1 -0
  64. package/eventcatalog/public/icons/tools/snowflake.svg +1 -0
  65. package/eventcatalog/public/icons/tools/zendesk.svg +1 -0
  66. package/eventcatalog/src/components/Badge.astro +41 -2
  67. package/eventcatalog/src/components/Grids/MessageGrid.tsx +16 -11
  68. package/eventcatalog/src/components/MDX/AgentTools/AgentTools.astro +132 -0
  69. package/eventcatalog/src/components/MDX/NodeGraph/NodeGraph.astro +2 -0
  70. package/eventcatalog/src/components/MDX/ResourceRef/ResourceRef.astro +8 -3
  71. package/eventcatalog/src/components/MDX/components.tsx +2 -0
  72. package/eventcatalog/src/components/Search/SearchModal.tsx +3 -0
  73. package/eventcatalog/src/components/Search/search-utils.ts +2 -0
  74. package/eventcatalog/src/components/SideNav/NestedSideBar/SearchBar.tsx +3 -0
  75. package/eventcatalog/src/components/SideNav/NestedSideBar/index.tsx +3 -0
  76. package/eventcatalog/src/components/Tables/Discover/DiscoverTable.tsx +165 -0
  77. package/eventcatalog/src/components/Tables/Discover/columns.tsx +132 -7
  78. package/eventcatalog/src/components/Tables/Table.tsx +2 -0
  79. package/eventcatalog/src/components/Tables/columns/TeamsTableColumns.tsx +17 -0
  80. package/eventcatalog/src/components/Tables/columns/UserTableColumns.tsx +17 -0
  81. package/eventcatalog/src/content.config.ts +83 -25
  82. package/eventcatalog/src/enterprise/collections/resource-docs-utils.ts +7 -4
  83. package/eventcatalog/src/enterprise/mcp/mcp-server.ts +9 -2
  84. package/eventcatalog/src/enterprise/tools/catalog-tools.ts +62 -28
  85. package/eventcatalog/src/layouts/VerticalSideBarLayout.astro +13 -1
  86. package/eventcatalog/src/pages/_index.astro +1 -3
  87. package/eventcatalog/src/pages/architecture/[type]/[id]/[version]/_index.data.ts +5 -5
  88. package/eventcatalog/src/pages/architecture/[type]/[id]/[version]/index.astro +5 -2
  89. package/eventcatalog/src/pages/directory/[type]/index.astro +2 -0
  90. package/eventcatalog/src/pages/discover/[type]/_index.data.ts +5 -0
  91. package/eventcatalog/src/pages/discover/[type]/index.astro +69 -23
  92. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/[docType]/[docId].mdx.ts +1 -0
  93. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/_index.data.ts +1 -0
  94. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/changelog/_index.data.ts +1 -1
  95. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/index.astro +47 -2
  96. package/eventcatalog/src/pages/docs/[type]/[id]/[version].md.ts +2 -0
  97. package/eventcatalog/src/pages/docs/[type]/[id]/[version].mdx.ts +2 -0
  98. package/eventcatalog/src/pages/docs/[type]/[id]/_index.data.ts +1 -0
  99. package/eventcatalog/src/pages/docs/llm/llms-full.txt.ts +3 -0
  100. package/eventcatalog/src/pages/docs/llm/llms.txt.ts +3 -0
  101. package/eventcatalog/src/pages/docs/teams/[id]/index.astro +24 -1
  102. package/eventcatalog/src/pages/docs/users/[id]/index.astro +24 -1
  103. package/eventcatalog/src/pages/visualiser/[type]/[id]/[version]/_index.data.ts +2 -1
  104. package/eventcatalog/src/stores/sidebar-store/builders/agent.ts +141 -0
  105. package/eventcatalog/src/stores/sidebar-store/builders/domain.ts +9 -0
  106. package/eventcatalog/src/stores/sidebar-store/builders/flow.ts +19 -0
  107. package/eventcatalog/src/stores/sidebar-store/builders/message.ts +8 -2
  108. package/eventcatalog/src/stores/sidebar-store/builders/shared.ts +10 -7
  109. package/eventcatalog/src/stores/sidebar-store/state.ts +121 -2
  110. package/eventcatalog/src/types/index.ts +3 -1
  111. package/eventcatalog/src/utils/collection-colors.ts +5 -0
  112. package/eventcatalog/src/utils/collections/agents.ts +163 -0
  113. package/eventcatalog/src/utils/collections/commands.ts +3 -2
  114. package/eventcatalog/src/utils/collections/domains.ts +94 -15
  115. package/eventcatalog/src/utils/collections/events.ts +3 -2
  116. package/eventcatalog/src/utils/collections/flows.ts +20 -3
  117. package/eventcatalog/src/utils/collections/icons.ts +3 -1
  118. package/eventcatalog/src/utils/collections/messages.ts +35 -5
  119. package/eventcatalog/src/utils/collections/queries.ts +3 -2
  120. package/eventcatalog/src/utils/collections/schemas.ts +4 -4
  121. package/eventcatalog/src/utils/collections/services.ts +1 -1
  122. package/eventcatalog/src/utils/collections/teams.ts +5 -1
  123. package/eventcatalog/src/utils/collections/types.ts +1 -0
  124. package/eventcatalog/src/utils/collections/users.ts +5 -1
  125. package/eventcatalog/src/utils/collections/util.ts +2 -0
  126. package/eventcatalog/src/utils/eventcatalog-config/catalog.ts +1 -0
  127. package/eventcatalog/src/utils/llms.ts +1 -1
  128. package/eventcatalog/src/utils/node-graphs/agents-node-graph.ts +4 -0
  129. package/eventcatalog/src/utils/node-graphs/container-node-graph.ts +2 -2
  130. package/eventcatalog/src/utils/node-graphs/domains-node-graph.ts +42 -9
  131. package/eventcatalog/src/utils/node-graphs/export-mermaid.ts +20 -0
  132. package/eventcatalog/src/utils/node-graphs/export-node-graph.ts +4 -0
  133. package/eventcatalog/src/utils/node-graphs/flows-node-graph.ts +22 -1
  134. package/eventcatalog/src/utils/node-graphs/message-node-graph.ts +175 -98
  135. package/eventcatalog/src/utils/node-graphs/services-node-graph.ts +117 -23
  136. package/eventcatalog/src/utils/node-graphs/utils/utils.ts +30 -0
  137. package/eventcatalog/src/utils/page-loaders/page-data-loader.ts +2 -0
  138. package/eventcatalog/src/utils/resource-reference-colors.ts +1 -0
  139. package/package.json +11 -4
@@ -0,0 +1,141 @@
1
+ import type { CollectionEntry } from 'astro:content';
2
+ import { buildUrl } from '@utils/url-builder';
3
+ import type { NavNode, ChildRef, ResourceGroupContext } from './shared';
4
+ import {
5
+ uniqueBy,
6
+ buildQuickReferenceSection,
7
+ buildOwnersSection,
8
+ shouldRenderSideBarSection,
9
+ buildResourceGroupSections,
10
+ buildRepositorySection,
11
+ buildAttachmentsSection,
12
+ buildDiagramNavItems,
13
+ buildResourceDocsSection,
14
+ } from './shared';
15
+ import { isChangelogEnabled } from '@utils/feature';
16
+ import { isVisualiserEnabled } from '@utils/feature';
17
+ import { pluralizeMessageType } from '@utils/collections/messages';
18
+ import { iconFieldsForResource } from '@utils/icon';
19
+
20
+ const uniqueRefs = (refs: string[]) => [...new Set(refs)];
21
+
22
+ export const buildAgentNode = (
23
+ agent: CollectionEntry<'agents'>,
24
+ owners: any[],
25
+ context: ResourceGroupContext,
26
+ agentChannels: CollectionEntry<'channels'>[] = [],
27
+ flowRefs: string[] = []
28
+ ): NavNode => {
29
+ const sendsMessages = agent.data.sends || [];
30
+ const receivesMessages = agent.data.receives || [];
31
+
32
+ const dataStoresInAgent = uniqueBy([...(agent.data.writesTo || []), ...(agent.data.readsFrom || [])], 'id');
33
+
34
+ const agentFlows = agent.data.flows || [];
35
+ const agentFlowRefs = uniqueRefs([
36
+ ...agentFlows.map((flow) => `flow:${(flow as any).data.id}:${(flow as any).data.version}`),
37
+ ...flowRefs,
38
+ ]);
39
+ const hasFlows = agentFlowRefs.length > 0;
40
+
41
+ const hasAttachments = agent.data.attachments && agent.data.attachments.length > 0;
42
+ const hasDataStores = dataStoresInAgent.length > 0;
43
+ const resourceGroups = agent.data.resourceGroups || [];
44
+ const hasResourceGroups = resourceGroups.length > 0;
45
+
46
+ const renderResourceGroups = hasResourceGroups && shouldRenderSideBarSection(agent, 'resourceGroups');
47
+ const renderMessages = shouldRenderSideBarSection(agent, 'messages');
48
+ const renderVisualiser = isVisualiserEnabled();
49
+ const renderOwners = owners.length > 0 && shouldRenderSideBarSection(agent, 'owners');
50
+ const renderRepository = agent.data.repository && shouldRenderSideBarSection(agent, 'repository');
51
+ const docsSection = buildResourceDocsSection(
52
+ 'agents',
53
+ agent.data.id,
54
+ agent.data.version,
55
+ context.resourceDocs,
56
+ context.resourceDocCategories
57
+ );
58
+
59
+ const agentDiagrams = agent.data.diagrams || [];
60
+ const diagramNavItems = buildDiagramNavItems(agentDiagrams, context.diagrams);
61
+ const hasDiagrams = diagramNavItems.length > 0;
62
+
63
+ return {
64
+ type: 'item',
65
+ title: agent.data.name,
66
+ badge: 'Agent',
67
+ summary: agent.data.summary,
68
+ ...iconFieldsForResource(agent.data, 'Bot'),
69
+ pages: [
70
+ buildQuickReferenceSection(
71
+ [
72
+ { title: 'Overview', href: buildUrl(`/docs/agents/${agent.data.id}/${agent.data.version}`) },
73
+ isChangelogEnabled() &&
74
+ shouldRenderSideBarSection(agent, 'changelog') && {
75
+ title: 'Changelog',
76
+ href: buildUrl(`/docs/agents/${agent.data.id}/${agent.data.version}/changelog`),
77
+ },
78
+ ].filter(Boolean) as { title: string; href: string }[]
79
+ ),
80
+ docsSection,
81
+ renderVisualiser && {
82
+ type: 'group',
83
+ title: 'Architecture',
84
+ icon: 'Workflow',
85
+ pages: [
86
+ {
87
+ type: 'item',
88
+ title: 'Map',
89
+ href: buildUrl(`/visualiser/agents/${agent.data.id}/${agent.data.version}`),
90
+ },
91
+ ].filter(Boolean) as ChildRef[],
92
+ },
93
+ hasDiagrams && {
94
+ type: 'group',
95
+ title: 'Diagrams',
96
+ icon: 'FileImage',
97
+ pages: diagramNavItems,
98
+ },
99
+ renderResourceGroups && buildResourceGroupSections(resourceGroups, context),
100
+ hasDataStores && {
101
+ type: 'group',
102
+ title: 'State and Persistence',
103
+ icon: 'Database',
104
+ pages: dataStoresInAgent.map((dataStore) => `container:${(dataStore as any).data.id}:${(dataStore as any).data.version}`),
105
+ },
106
+ sendsMessages.length > 0 &&
107
+ renderMessages && {
108
+ type: 'group',
109
+ title: 'Outbound Messages',
110
+ icon: 'Mail',
111
+ pages: sendsMessages.map(
112
+ (message) => `${pluralizeMessageType(message as any)}:${(message as any).data.id}:${(message as any).data.version}`
113
+ ),
114
+ },
115
+ receivesMessages.length > 0 &&
116
+ renderMessages && {
117
+ type: 'group',
118
+ title: 'Inbound Messages',
119
+ icon: 'Mail',
120
+ pages: receivesMessages.map(
121
+ (receive) => `${pluralizeMessageType(receive as any)}:${(receive as any).data.id}:${(receive as any).data.version}`
122
+ ),
123
+ },
124
+ agentChannels.length > 0 && {
125
+ type: 'group',
126
+ title: 'Channels',
127
+ icon: 'ArrowRightLeft',
128
+ pages: agentChannels.map((channel) => `channel:${(channel as any).data.id}:${(channel as any).data.version}`),
129
+ },
130
+ hasFlows && {
131
+ type: 'group',
132
+ title: 'Flows',
133
+ icon: 'Waypoints',
134
+ pages: agentFlowRefs,
135
+ },
136
+ renderOwners && buildOwnersSection(owners),
137
+ renderRepository && buildRepositorySection(agent.data.repository as { url: string; language: string }),
138
+ hasAttachments && buildAttachmentsSection(agent.data.attachments as any[]),
139
+ ].filter(Boolean) as ChildRef[],
140
+ };
141
+ };
@@ -16,6 +16,9 @@ import { pluralizeMessageType } from '@utils/collections/messages';
16
16
  import { getSpecificationsForDomain } from '@utils/collections/domains';
17
17
 
18
18
  export const buildDomainNode = (domain: CollectionEntry<'domains'>, owners: any[], context: ResourceGroupContext): NavNode => {
19
+ const agentsInDomain = domain.data.agents || [];
20
+ const renderAgents = agentsInDomain.length > 0 && shouldRenderSideBarSection(domain, 'agents');
21
+
19
22
  const allServicesInDomain = domain.data.services || [];
20
23
  const servicesInDomain = allServicesInDomain.filter((service) => !(service as any).data?.externalSystem);
21
24
  const externalSystemsInDomain = allServicesInDomain.filter((service) => (service as any).data?.externalSystem);
@@ -182,6 +185,12 @@ export const buildDomainNode = (domain: CollectionEntry<'domains'>, owners: any[
182
185
  icon: 'Server',
183
186
  pages: servicesInDomain.map((service) => `service:${(service as any).data.id}:${(service as any).data.version}`),
184
187
  },
188
+ renderAgents && {
189
+ type: 'group',
190
+ title: 'Agents In Domain',
191
+ icon: 'Bot',
192
+ pages: agentsInDomain.map((agent) => `agent:${(agent as any).data.id}:${(agent as any).data.version}`),
193
+ },
185
194
  renderExternalSystems && {
186
195
  type: 'group',
187
196
  title: 'External Integrations',
@@ -54,6 +54,7 @@ export const buildFlowNode = (flow: CollectionEntry<'flows'>, context: ResourceG
54
54
  const eventMap = createVersionedMap(context.events);
55
55
  const commandMap = createVersionedMap(context.commands);
56
56
  const queryMap = createVersionedMap(context.queries);
57
+ const agentMap = createVersionedMap(context.agents || []);
57
58
  const serviceMap = createVersionedMap(context.services);
58
59
  const flowMap = createVersionedMap(context.flows);
59
60
  const containerMap = createVersionedMap(context.containers);
@@ -67,6 +68,18 @@ export const buildFlowNode = (flow: CollectionEntry<'flows'>, context: ResourceG
67
68
  .filter(Boolean)
68
69
  .map((service) => `service:${service!.data.id}:${service!.data.version}`)
69
70
  );
71
+ const agentRefs = uniqueRefs(
72
+ steps
73
+ .map((step: any) => {
74
+ const hydratedAgent = Array.isArray(step.agent) ? step.agent[0] : undefined;
75
+ if (hydratedAgent?.collection && hydratedAgent?.data) return hydratedAgent;
76
+
77
+ const pointer = Array.isArray(step.agent) ? undefined : step.agent;
78
+ return pointer ? resolvePointer(agentMap, pointer) : undefined;
79
+ })
80
+ .filter(Boolean)
81
+ .map((agent) => `agent:${agent!.data.id}:${agent!.data.version}`)
82
+ );
70
83
  const flowRefs = uniqueRefs(
71
84
  steps
72
85
  .map((step) => (step.flow ? resolvePointer(flowMap, step.flow) : undefined))
@@ -140,6 +153,12 @@ export const buildFlowNode = (flow: CollectionEntry<'flows'>, context: ResourceG
140
153
  icon: 'Server',
141
154
  pages: serviceRefs,
142
155
  },
156
+ agentRefs.length > 0 && {
157
+ type: 'group',
158
+ title: 'Agents',
159
+ icon: 'Bot',
160
+ pages: agentRefs,
161
+ },
143
162
  flowRefs.length > 0 && {
144
163
  type: 'group',
145
164
  title: 'Subflows',
@@ -13,6 +13,12 @@ import {
13
13
  } from './shared';
14
14
  import { isVisualiserEnabled, isChangelogEnabled } from '@utils/feature';
15
15
  import { iconFieldsForResource } from '@utils/icon';
16
+ import { collectionToResourceMap } from '@utils/collections/util';
17
+
18
+ const getProducerConsumerPageRef = (resource: any) => {
19
+ const resourceType = collectionToResourceMap[resource.collection as keyof typeof collectionToResourceMap];
20
+ return `${resourceType}:${resource.data.id}:${resource.data.version}`;
21
+ };
16
22
 
17
23
  export const buildMessageNode = (
18
24
  message: CollectionEntry<'events' | 'commands' | 'queries'>,
@@ -124,14 +130,14 @@ export const buildMessageNode = (
124
130
  type: 'group',
125
131
  title: 'Producers',
126
132
  icon: 'Server',
127
- pages: producers.map((producer) => `service:${(producer as any).data.id}:${(producer as any).data.version}`),
133
+ pages: producers.map(getProducerConsumerPageRef),
128
134
  visible: producers.length > 0,
129
135
  },
130
136
  renderConsumers && {
131
137
  type: 'group',
132
138
  title: 'Consumers',
133
139
  icon: 'Server',
134
- pages: consumers.map((consumer) => `service:${(consumer as any).data.id}:${(consumer as any).data.version}`),
140
+ pages: consumers.map(getProducerConsumerPageRef),
135
141
  visible: consumers.length > 0,
136
142
  },
137
143
  renderFlows && {
@@ -54,6 +54,7 @@ export const uniqueBy = <T>(array: T[], key: keyof T): T[] => {
54
54
  };
55
55
 
56
56
  export type ResourceGroupContext = {
57
+ agents?: CollectionEntry<'agents'>[];
57
58
  services: CollectionEntry<'services'>[];
58
59
  domains: CollectionEntry<'domains'>[];
59
60
  events: CollectionEntry<'events'>[];
@@ -210,14 +211,16 @@ const buildResourceGroupSection = (resourceGroup: ResourceGroup, context: Resour
210
211
  // If no version is provided, we need to get the latest version
211
212
  const resourcesWithVersions = resourcesWithTypes.map((item) => {
212
213
  let collection: any[] = [];
214
+ const itemType = item.type as string;
213
215
 
214
- if (item.type === 'service') collection = context.services;
215
- else if (item.type === 'domain') collection = context.domains;
216
- else if (item.type === 'event') collection = context.events;
217
- else if (item.type === 'command') collection = context.commands;
218
- else if (item.type === 'query') collection = context.queries;
219
- else if (item.type === 'flow') collection = context.flows;
220
- else if (item.type === 'container') collection = context.containers;
216
+ if (itemType === 'agent') collection = context.agents || [];
217
+ else if (itemType === 'service') collection = context.services;
218
+ else if (itemType === 'domain') collection = context.domains;
219
+ else if (itemType === 'event') collection = context.events;
220
+ else if (itemType === 'command') collection = context.commands;
221
+ else if (itemType === 'query') collection = context.queries;
222
+ else if (itemType === 'flow') collection = context.flows;
223
+ else if (itemType === 'container') collection = context.containers;
221
224
 
222
225
  if (item.version === undefined || item.version === 'latest') {
223
226
  return { ...item, version: getLatestVersionInCollectionById(collection, item.id as string) };
@@ -1,5 +1,6 @@
1
1
  import { getCollection } from 'astro:content';
2
2
  import type { CollectionEntry } from 'astro:content';
3
+ import { getAgents } from '@utils/collections/agents';
3
4
  import { getContainers } from '@utils/collections/containers';
4
5
  import { getDomains } from '@utils/collections/domains';
5
6
  import { getServices } from '@utils/collections/services';
@@ -14,6 +15,7 @@ import { getEntities } from '@utils/collections/entities';
14
15
  import { getResourceDocCategories, getResourceDocs } from '@utils/collections/resource-docs';
15
16
  import { buildUrl } from '@utils/url-builder';
16
17
  import type { NavigationData, NavNode, ChildRef } from './builders/shared';
18
+ import { buildAgentNode } from './builders/agent';
17
19
  import { buildDomainNode } from './builders/domain';
18
20
  import { buildServiceNode } from './builders/service';
19
21
  import { buildMessageNode } from './builders/message';
@@ -34,6 +36,7 @@ const CACHE_ENABLED = process.env.DISABLE_EVENTCATALOG_CACHE !== 'true';
34
36
  let memoryCache: NavigationData | null = null;
35
37
 
36
38
  type MessageEntry = CollectionEntry<'events' | 'commands' | 'queries'>;
39
+ type AgentEntry = CollectionEntry<'agents'>;
37
40
  type ServiceEntry = CollectionEntry<'services'>;
38
41
  type ContainerEntry = CollectionEntry<'containers'>;
39
42
  type DataProductEntry = CollectionEntry<'data-products'>;
@@ -121,6 +124,42 @@ const buildFlowReferencesByService = ({
121
124
  return flowRefsByService;
122
125
  };
123
126
 
127
+ const buildFlowReferencesByAgent = ({
128
+ flows,
129
+ agents,
130
+ }: {
131
+ flows: CollectionEntry<'flows'>[];
132
+ agents: CollectionEntry<'agents'>[];
133
+ }) => {
134
+ const agentMap = createVersionedMap(agents);
135
+ const flowRefsByAgent = new Map<string, string[]>();
136
+
137
+ const addFlowRef = (agent: AgentEntry, flow: CollectionEntry<'flows'>) => {
138
+ const agentKey = `agent:${agent.data.id}:${agent.data.version}`;
139
+ const flowKey = `flow:${flow.data.id}:${flow.data.version}`;
140
+ flowRefsByAgent.set(agentKey, uniqueRefs([...(flowRefsByAgent.get(agentKey) || []), flowKey]));
141
+ };
142
+
143
+ for (const flow of flows) {
144
+ for (const step of flow.data.steps || []) {
145
+ if (!(step as any).agent) continue;
146
+
147
+ const hydratedAgent = Array.isArray((step as any).agent) ? (step as any).agent[0] : undefined;
148
+ if (hydratedAgent?.collection && hydratedAgent?.data) {
149
+ addFlowRef(hydratedAgent as AgentEntry, flow);
150
+ continue;
151
+ }
152
+
153
+ if (Array.isArray((step as any).agent)) continue;
154
+
155
+ const agent = findInMap(agentMap, (step as any).agent.id, (step as any).agent.version);
156
+ if (agent) addFlowRef(agent, flow);
157
+ }
158
+ }
159
+
160
+ return flowRefsByAgent;
161
+ };
162
+
124
163
  const buildFlowReferencesByContainer = ({
125
164
  flows,
126
165
  containers,
@@ -203,6 +242,7 @@ export const getNestedSideBarData = async (): Promise<NavigationData> => {
203
242
 
204
243
  const [
205
244
  domains,
245
+ agents,
206
246
  services,
207
247
  { events, commands, queries },
208
248
  containers,
@@ -218,6 +258,7 @@ export const getNestedSideBarData = async (): Promise<NavigationData> => {
218
258
  resourceDocCategories,
219
259
  ] = await Promise.all([
220
260
  getDomains({ getAllVersions: false, includeServicesInSubdomains: false }),
261
+ getAgents({ getAllVersions: false }),
221
262
  getServices({ getAllVersions: false }),
222
263
  getMessages({ getAllVersions: false }),
223
264
  getContainers({ getAllVersions: false }),
@@ -240,6 +281,7 @@ export const getNestedSideBarData = async (): Promise<NavigationData> => {
240
281
  const messages = [...events, ...commands, ...queries];
241
282
 
242
283
  const context = {
284
+ agents,
243
285
  services,
244
286
  domains,
245
287
  events,
@@ -268,6 +310,15 @@ export const getNestedSideBarData = async (): Promise<NavigationData> => {
268
310
  })
269
311
  );
270
312
 
313
+ const agentsWithOwners = await Promise.all(
314
+ agents.map(async (agent) => {
315
+ const ownersInAgent = agent.data.owners || [];
316
+ const owners = await Promise.all(ownersInAgent.map((owner) => getOwner(owner)));
317
+ const filteredOwners = owners.filter((o) => o !== undefined) as Array<NonNullable<(typeof owners)[0]>>;
318
+ return { agent, owners: filteredOwners };
319
+ })
320
+ );
321
+
271
322
  // Services with owners
272
323
  const servicesWithOwners = await Promise.all(
273
324
  services.map(async (service) => {
@@ -318,6 +369,8 @@ export const getNestedSideBarData = async (): Promise<NavigationData> => {
318
369
  {} as Record<string, NavNode | string>
319
370
  );
320
371
 
372
+ const rawAgents = await getCollection('agents');
373
+
321
374
  // Compute channels for each service from raw sends[].to / receives[].from pointers
322
375
  const rawServices = await getCollection('services');
323
376
  const channelMap = createVersionedMap(channels);
@@ -354,8 +407,55 @@ export const getNestedSideBarData = async (): Promise<NavigationData> => {
354
407
  }
355
408
  }
356
409
 
410
+ const agentChannelsMap = new Map<string, CollectionEntry<'channels'>[]>();
411
+
412
+ for (const agent of agents) {
413
+ const rawAgent = rawAgents.find((a) => a.data.id === agent.data.id && a.data.version === agent.data.version);
414
+ if (!rawAgent) continue;
415
+
416
+ const pointers: Array<{ id: string; version?: string }> = [];
417
+ for (const send of rawAgent.data.sends ?? []) {
418
+ for (const ch of send.to ?? []) {
419
+ pointers.push({ id: ch.id, version: ch.version });
420
+ }
421
+ }
422
+ for (const receive of rawAgent.data.receives ?? []) {
423
+ for (const ch of receive.from ?? []) {
424
+ pointers.push({ id: ch.id, version: ch.version });
425
+ }
426
+ }
427
+
428
+ const seen = new Set<string>();
429
+ const resolved: CollectionEntry<'channels'>[] = [];
430
+ for (const pointer of pointers) {
431
+ const key = `${pointer.id}-${pointer.version ?? 'latest'}`;
432
+ if (seen.has(key)) continue;
433
+ seen.add(key);
434
+ const match = findInMap(channelMap, pointer.id, pointer.version);
435
+ if (match) resolved.push(match as CollectionEntry<'channels'>);
436
+ }
437
+
438
+ if (resolved.length > 0) {
439
+ agentChannelsMap.set(`${agent.data.id}:${agent.data.version}`, resolved);
440
+ }
441
+ }
442
+
443
+ const flowRefsByAgent = buildFlowReferencesByAgent({ flows, agents });
357
444
  const flowRefsByService = buildFlowReferencesByService({ flows, services });
358
445
 
446
+ const agentNodes = agentsWithOwners.reduce(
447
+ (acc, { agent, owners }) => {
448
+ const versionedKey = `agent:${agent.data.id}:${agent.data.version}`;
449
+ const agentChannels = agentChannelsMap.get(`${agent.data.id}:${agent.data.version}`) || [];
450
+ acc[versionedKey] = buildAgentNode(agent, owners, context, agentChannels, flowRefsByAgent.get(versionedKey) || []);
451
+ if (agent.data.latestVersion === agent.data.version) {
452
+ acc[`agent:${agent.data.id}`] = versionedKey;
453
+ }
454
+ return acc;
455
+ },
456
+ {} as Record<string, NavNode | string>
457
+ );
458
+
359
459
  const serviceNodes = servicesWithOwners.reduce(
360
460
  (acc, { service, owners }) => {
361
461
  const versionedKey = `service:${service.data.id}:${service.data.version}`;
@@ -370,10 +470,18 @@ export const getNestedSideBarData = async (): Promise<NavigationData> => {
370
470
  {} as Record<string, NavNode | string>
371
471
  );
372
472
 
373
- // Build a set of message IDs that have field usage declared by any service.
374
- // We use rawServices (from getCollection) because the hydrated services replace
473
+ // Build a set of message IDs that have field usage declared by any service or agent.
474
+ // We use raw collections because the hydrated resources replace
375
475
  // sends/receives pointers with resolved message entries, which strips the fields property.
376
476
  const messagesWithFieldUsage = new Set<string>();
477
+ for (const agent of rawAgents) {
478
+ for (const pointer of agent.data.sends || []) {
479
+ if (pointer.fields?.length) messagesWithFieldUsage.add(pointer.id);
480
+ }
481
+ for (const pointer of agent.data.receives || []) {
482
+ if (pointer.fields?.length) messagesWithFieldUsage.add(pointer.id);
483
+ }
484
+ }
377
485
  for (const service of rawServices) {
378
486
  for (const pointer of service.data.sends || []) {
379
487
  if (pointer.fields?.length) messagesWithFieldUsage.add(pointer.id);
@@ -572,6 +680,13 @@ export const getNestedSideBarData = async (): Promise<NavigationData> => {
572
680
  pages: domains.map((domain) => `domain:${domain.data.id}:${domain.data.version}`),
573
681
  });
574
682
 
683
+ const agentsList = createLeaf(agents, {
684
+ type: 'item',
685
+ title: 'Agents',
686
+ icon: 'Bot',
687
+ pages: agents.map((agent) => `agent:${agent.data.id}:${agent.data.version}`),
688
+ });
689
+
575
690
  const internalServices = services.filter((service) => !service.data.externalSystem);
576
691
  const externalServices = services.filter((service) => service.data.externalSystem);
577
692
 
@@ -695,6 +810,7 @@ export const getNestedSideBarData = async (): Promise<NavigationData> => {
695
810
  const allChildrenKeys = [
696
811
  'list:domains',
697
812
  'list:services',
813
+ 'list:agents',
698
814
  'list:external-systems',
699
815
  'list:messages',
700
816
  'list:channels',
@@ -708,6 +824,7 @@ export const getNestedSideBarData = async (): Promise<NavigationData> => {
708
824
  const allChildrenNodes = [
709
825
  domainsList,
710
826
  servicesList,
827
+ agentsList,
711
828
  externalSystemsList,
712
829
  messagesList,
713
830
  channelList,
@@ -734,6 +851,7 @@ export const getNestedSideBarData = async (): Promise<NavigationData> => {
734
851
  const allNodes: Record<string, NavNode> = {
735
852
  ...(domainsList ? { 'list:domains': domainsList } : {}),
736
853
  ...(servicesList ? { 'list:services': servicesList } : {}),
854
+ ...(agentsList ? { 'list:agents': agentsList } : {}),
737
855
  ...(externalSystemsList ? { 'list:external-systems': externalSystemsList } : {}),
738
856
  ...(eventsList ? { 'list:events': eventsList } : {}),
739
857
  ...(commandsList ? { 'list:commands': commandsList } : {}),
@@ -773,6 +891,7 @@ export const getNestedSideBarData = async (): Promise<NavigationData> => {
773
891
  const allGeneratedNodes = {
774
892
  ...rootDomainsNodes,
775
893
  ...domainNodes,
894
+ ...agentNodes,
776
895
  ...serviceNodes,
777
896
  ...messageNodes,
778
897
  ...channelNodes,
@@ -1,4 +1,5 @@
1
1
  export type CollectionTypes =
2
+ | 'agents'
2
3
  | 'commands'
3
4
  | 'events'
4
5
  | 'queries'
@@ -11,8 +12,9 @@ export type CollectionTypes =
11
12
  | 'diagrams'
12
13
  | 'data-products';
13
14
  export type CollectionMessageTypes = 'commands' | 'events' | 'queries';
14
- export type CollectionUserTypes = 'users';
15
+ export type CollectionUserTypes = 'users' | 'teams';
15
16
  export type PageTypes =
17
+ | 'agents'
16
18
  | 'events'
17
19
  | 'commands'
18
20
  | 'queries'
@@ -9,6 +9,7 @@ export type CollectionColor =
9
9
  | 'red'
10
10
  | 'gray'
11
11
  | 'cyan'
12
+ | 'sky'
12
13
  | 'indigo';
13
14
 
14
15
  export const getColorForCollection = (collection: string): CollectionColor => {
@@ -33,6 +34,8 @@ export const getColorForCollection = (collection: string): CollectionColor => {
33
34
  return 'purple';
34
35
  case 'domains':
35
36
  return 'yellow';
37
+ case 'agents':
38
+ return 'sky';
36
39
  case 'services':
37
40
  return 'pink';
38
41
  case 'data-products':
@@ -55,6 +58,7 @@ export const tailwind500RgbByColor: Record<CollectionColor, string> = {
55
58
  red: '239 68 68',
56
59
  gray: '107 114 128',
57
60
  cyan: '6 182 212',
61
+ sky: '14 165 233',
58
62
  indigo: '99 102 241',
59
63
  };
60
64
 
@@ -69,6 +73,7 @@ export const collectionTextColorClassByColor: Record<CollectionColor, string> =
69
73
  red: 'text-red-500',
70
74
  gray: 'text-gray-500',
71
75
  cyan: 'text-cyan-500',
76
+ sky: 'text-sky-500',
72
77
  indigo: 'text-indigo-500',
73
78
  };
74
79