diff --git a/src/commands/agent/generate/test-spec.ts b/src/commands/agent/generate/test-spec.ts
index dfac863b..e7ea3746 100644
--- a/src/commands/agent/generate/test-spec.ts
+++ b/src/commands/agent/generate/test-spec.ts
@@ -358,7 +358,7 @@ export async function getPluginsAndFunctions(
localDeveloperName: string;
masterLabel: string;
pluginType: string;
- localActionLinks?: Array<{ functionName: string }>;
+ localActionLinks?: Array<{ functionName: string }> | { functionName: string };
genAiPluginInstructions: {
description: string;
developerName: string;
@@ -371,7 +371,7 @@ export async function getPluginsAndFunctions(
};
genAiFunctions = ensureArray(
parsedPlannerBundle.GenAiPlannerBundle.localTopics
- .flatMap((topic) => topic.localActionLinks?.map((lal) => lal.functionName))
+ .flatMap((topic) => ensureArray(topic.localActionLinks)?.map((lal) => lal.functionName))
.filter((f) => typeof f === 'string')
);
diff --git a/test/commands/agent/generate/test-spec.test.ts b/test/commands/agent/generate/test-spec.test.ts
index f399115b..9df3fbd6 100644
--- a/test/commands/agent/generate/test-spec.test.ts
+++ b/test/commands/agent/generate/test-spec.test.ts
@@ -186,6 +186,94 @@ describe('AgentGenerateTestSpec Helper Methods', () => {
});
});
+ it('should not fail when a topic has a single localActionLinks (fast-xml-parser returns object, not array)', async () => {
+ const name = 'myAgent';
+ const cs = new ComponentSet([
+ { fullName: name, type: { name: 'Bot', id: 'bot', directoryName: 'bot' } },
+ {
+ fullName: 'myGenAiPlannerBundle',
+ type: { name: 'GenAiPlannerBundle', id: 'genaiplannerbundle', directoryName: 'genaiplannerbundle' },
+ },
+ {
+ fullName: 'local_weather_16jDU000000Gmm5',
+ type: { name: 'GenAiPlugin', id: 'genaiplugin', directoryName: 'genaiplugin' },
+ },
+ ]);
+
+ // Call sequence for getComponentFilenamesByNameAndType:
+ // 1. Bot (getMetadataFilePaths)
+ // 2. GenAiPlannerBundle (getMetadataFilePaths) — no GenAiPlanner in CS so no call for it
+ // 3. GenAiPlugin for local_weather (reduce over localTopicLinks)
+ $$.stub(cs, 'getComponentFilenamesByNameAndType')
+ .onFirstCall()
+ .returns(['myBot.bot-meta.xml'])
+ .onSecondCall()
+ .returns(['myGenAiPlannerBundle.genAiPlannerBundle-meta.xml'])
+ .onThirdCall()
+ .returns(['local_weather.genAiPlugin-meta.xml']);
+
+ // Call sequence for readFile:
+ // 1. BotVersion XML
+ // 2. GenAiPlanner attempt — readFile(undefined) because {} has no key → rejects (caught silently)
+ // 3. GenAiPlannerBundle XML
+ $$.stub(fs.promises, 'readFile')
+ .onFirstCall()
+ .resolves(
+ `
+
+
+ myGenAiPlannerBundle
+
+
+`
+ )
+ .onSecondCall()
+ .rejects() // readFile(undefined) from empty genAiPlanners map
+ .onThirdCall()
+ .resolves(
+ `
+
+ A resort agent.
+
+ local_weather_16jDU000000Gmm5
+
+
+ off_topic_16jDU000000Gmm5
+ false
+ Redirect off-topic requests
+ off_topic_16jDU000000Gmm5
+ en_US
+ off_topic
+ Off Topic
+ Topic
+
+
+ local_weather_16jDU000000Gmm5
+ false
+ Provides weather info.
+ local_weather_16jDU000000Gmm5
+ en_US
+
+ check_weather_179DU000000Gn0s
+
+ local_weather
+ Local Weather
+ Topic
+
+ Local Info Agent
+ Atlas__ConcurrentMultiAgentOrchestration
+
+`
+ );
+
+ const result = await getPluginsAndFunctions(name, cs);
+ expect(result.genAiFunctions).to.deep.equal(['check_weather_179DU000000Gn0s']);
+ expect(result.genAiPlugins).to.deep.equal({
+ // eslint-disable-next-line camelcase
+ local_weather_16jDU000000Gmm5: 'local_weather.genAiPlugin-meta.xml',
+ });
+ });
+
it('should not fail when theres no actions', async () => {
const name = 'myAgent';
const cs = new ComponentSet([