Skip to content

Commit 60c98f7

Browse files
committed
Merge branch 'main' into ew/agent-preview
2 parents bdd3ede + 19db6e6 commit 60c98f7

7 files changed

+164
-102
lines changed

CHANGELOG.md

+45
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,48 @@
1+
## [0.12.6](https://github.com/forcedotcom/agents/compare/0.12.5...0.12.6) (2025-02-25)
2+
3+
4+
### Bug Fixes
5+
6+
* order aiEvalDefs in server-order ([56cde25](https://github.com/forcedotcom/agents/commit/56cde25b2f1850fa223560d1059890d20215af83))
7+
8+
9+
10+
## [0.12.5](https://github.com/forcedotcom/agents/compare/0.12.4...0.12.5) (2025-02-23)
11+
12+
13+
### Bug Fixes
14+
15+
* **deps:** bump @salesforce/source-deploy-retrieve ([7993e19](https://github.com/forcedotcom/agents/commit/7993e19818bfcafdf546a7c10e54d5aec5d1e92d))
16+
17+
18+
19+
## [0.12.4](https://github.com/forcedotcom/agents/compare/0.12.3...0.12.4) (2025-02-22)
20+
21+
22+
### Bug Fixes
23+
24+
* **deps:** bump fast-xml-parser from 4.5.1 to 4.5.3 ([8922c90](https://github.com/forcedotcom/agents/commit/8922c908cdff50eafe55a8084fb4a792bc301429))
25+
26+
27+
28+
## [0.12.3](https://github.com/forcedotcom/agents/compare/0.12.2...0.12.3) (2025-02-22)
29+
30+
31+
### Bug Fixes
32+
33+
* **deps:** bump @salesforce/core from 8.8.2 to 8.8.3 ([20ac335](https://github.com/forcedotcom/agents/commit/20ac3354a688563c5a072879cce38ca542d91d81))
34+
35+
36+
37+
## [0.12.2](https://github.com/forcedotcom/agents/compare/0.12.1...0.12.2) (2025-02-19)
38+
39+
40+
### Bug Fixes
41+
42+
* decode html entities in ai-assist API responses ([a0cebae](https://github.com/forcedotcom/agents/commit/a0cebae214f8ffdbf4053762e9d17ce0e1d7b1a1))
43+
44+
45+
146
## [0.12.1](https://github.com/forcedotcom/agents/compare/0.12.0...0.12.1) (2025-02-17)
247

348
### Bug Fixes

package.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "@salesforce/agents",
33
"description": "Client side APIs for working with Salesforce agents",
4-
"version": "0.12.1",
4+
"version": "0.12.6",
55
"license": "BSD-3-Clause",
66
"author": "Salesforce",
77
"main": "lib/index",
@@ -25,7 +25,7 @@
2525
"ts-morph": "^24.0.0",
2626
"ts-node": "^10.9.2",
2727
"ts-patch": "^3.3.0",
28-
"typedoc": "^0.27.7",
28+
"typedoc": "^0.27.8",
2929
"typescript": "^5.7.3"
3030
},
3131
"engines": {

src/agent.ts

+14-5
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import {
1818
type DraftAgentTopicsResponse,
1919
} from './types.js';
2020
import { MaybeMock } from './maybe-mock';
21+
import { decodeHtmlEntities } from './utils';
2122

2223
Messages.importMessagesDirectory(__dirname);
2324
const messages = Messages.loadMessages('@salesforce/agents', 'agents');
@@ -68,7 +69,9 @@ export class Agent {
6869
`Previewing agent creation using config: ${inspect(config)} in project: ${this.project.getPath()}`
6970
);
7071
await Lifecycle.getInstance().emit(AgentCreateLifecycleStages.Previewing, {});
71-
return this.maybeMock.request<AgentCreateResponse>('POST', url, config);
72+
73+
const response = await this.maybeMock.request<AgentCreateResponse>('POST', url, config);
74+
return decodeResponse(response);
7275
}
7376

7477
if (!config.agentSettings?.agentName) {
@@ -127,7 +130,7 @@ export class Agent {
127130
}
128131
}
129132

130-
return response;
133+
return decodeResponse(response);
131134
}
132135

133136
/**
@@ -164,13 +167,15 @@ export class Agent {
164167
}
165168

166169
const response = await this.maybeMock.request<DraftAgentTopicsResponse>('POST', url, body);
170+
const htmlDecodedResponse = decodeResponse<DraftAgentTopicsResponse>(response);
167171

168-
if (response.isSuccess && response.topicDrafts) {
169-
return { ...config, topics: response.topicDrafts };
172+
if (htmlDecodedResponse.isSuccess) {
173+
return { ...config, topics: htmlDecodedResponse.topicDrafts };
170174
} else {
171175
throw SfError.create({
172176
name: 'AgentJobSpecCreateError',
173-
message: response.errorMessage ?? 'unknown',
177+
message: htmlDecodedResponse.errorMessage ?? 'unknown',
178+
data: htmlDecodedResponse,
174179
});
175180
}
176181
}
@@ -202,3 +207,7 @@ export const generateAgentApiName = (agentName: string): string => {
202207
logger.debug(`Generated Agent API name: [${apiName}] from Agent name: [${agentName}]`);
203208
return apiName;
204209
};
210+
211+
// Decodes all HTML entities in ai-assist API responses.
212+
const decodeResponse = <T extends object>(response: T): T =>
213+
JSON.parse(decodeHtmlEntities(JSON.stringify(response))) as T;

src/agentTester.ts

+10-51
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { ComponentSetBuilder, DeployResult, FileProperties, RequestStatus } from
1313
import { parse, stringify } from 'yaml';
1414
import { XMLBuilder, XMLParser } from 'fast-xml-parser';
1515
import { MaybeMock } from './maybe-mock';
16+
import { decodeHtmlEntities } from './utils';
1617

1718
export type TestStatus = 'NEW' | 'IN_PROGRESS' | 'COMPLETED' | 'ERROR' | 'TERMINATED';
1819

@@ -275,6 +276,7 @@ export class AgentTester {
275276
const builder = new XMLBuilder({
276277
format: true,
277278
attributeNamePrefix: '$',
279+
indentBy: ' ',
278280
ignoreAttributes: false,
279281
});
280282

@@ -283,28 +285,28 @@ export class AgentTester {
283285
$xmlns: 'http://soap.sforce.com/2006/04/metadata',
284286
...(parsed.description && { description: parsed.description }),
285287
name: parsed.name,
286-
subjectType: parsed.subjectType,
287288
subjectName: parsed.subjectName,
289+
subjectType: parsed.subjectType,
288290
...(parsed.subjectVersion && { subjectVersion: parsed.subjectVersion }),
289291
testCase: parsed.testCases.map((tc) => ({
290-
number: parsed.testCases.indexOf(tc) + 1,
291-
inputs: {
292-
utterance: tc.utterance,
293-
},
294292
expectation: [
295293
{
296-
name: 'topic_sequence_match',
297294
expectedValue: tc.expectedTopic,
295+
name: 'topic_sequence_match',
298296
},
299297
{
300-
name: 'action_sequence_match',
301298
expectedValue: `[${(tc.expectedActions ?? []).map((v) => `"${v}"`).join(',')}]`,
299+
name: 'action_sequence_match',
302300
},
303301
{
304-
name: 'bot_response_rating',
305302
expectedValue: tc.expectedOutcome,
303+
name: 'bot_response_rating',
306304
},
307305
],
306+
inputs: {
307+
utterance: tc.utterance,
308+
},
309+
number: parsed.testCases.indexOf(tc) + 1,
308310
})),
309311
},
310312
}) as string;
@@ -392,49 +394,6 @@ export function normalizeResults(results: AgentTestResultsResponse): AgentTestRe
392394
};
393395
}
394396

395-
/**
396-
* Clean a string by replacing HTML entities with their respective characters.
397-
*
398-
* @param str - The string to clean.
399-
* @returns The cleaned string with all HTML entities replaced with their respective characters.
400-
*/
401-
function decodeHtmlEntities(str: string = ''): string {
402-
const entities: { [key: string]: string } = {
403-
'&quot;': '"',
404-
'&apos;': "'",
405-
'&amp;': '&',
406-
'&lt;': '<',
407-
'&gt;': '>',
408-
'&#39;': "'",
409-
'&deg;': '°',
410-
'&nbsp;': ' ',
411-
'&ndash;': '–',
412-
'&mdash;': '—',
413-
'&rsquo;': '’',
414-
'&lsquo;': '‘',
415-
'&ldquo;': '“',
416-
'&rdquo;': '”',
417-
'&hellip;': '…',
418-
'&trade;': '™',
419-
'&copy;': '©',
420-
'&reg;': '®',
421-
'&euro;': '€',
422-
'&pound;': '£',
423-
'&yen;': '¥',
424-
'&cent;': '¢',
425-
'&times;': '×',
426-
'&divide;': '÷',
427-
'&plusmn;': '±',
428-
'&micro;': 'µ',
429-
'&para;': '¶',
430-
'&sect;': '§',
431-
'&bull;': '•',
432-
'&middot;': '·',
433-
};
434-
435-
return str.replace(/&[a-zA-Z0-9#]+;/g, (entity) => entities[entity] || entity);
436-
}
437-
438397
async function jsonFormat(results: AgentTestResultsResponse): Promise<string> {
439398
return Promise.resolve(JSON.stringify(results, null, 2));
440399
}

src/utils.ts

+49
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/*
2+
* Copyright (c) 2025, salesforce.com, inc.
3+
* All rights reserved.
4+
* Licensed under the BSD 3-Clause license.
5+
* For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
6+
*/
7+
8+
/**
9+
* Clean a string by replacing HTML entities with their respective characters.
10+
*
11+
* @param str - The string to clean.
12+
* @returns The cleaned string with all HTML entities replaced with their respective characters.
13+
*/
14+
export const decodeHtmlEntities = (str: string = ''): string => {
15+
const entities: { [key: string]: string } = {
16+
'&quot;': '"',
17+
'&apos;': "'",
18+
'&amp;': '&',
19+
'&lt;': '<',
20+
'&gt;': '>',
21+
'&#39;': "'",
22+
'&deg;': '°',
23+
'&nbsp;': ' ',
24+
'&ndash;': '–',
25+
'&mdash;': '—',
26+
'&rsquo;': '’',
27+
'&lsquo;': '‘',
28+
'&ldquo;': '“',
29+
'&rdquo;': '”',
30+
'&hellip;': '…',
31+
'&trade;': '™',
32+
'&copy;': '©',
33+
'&reg;': '®',
34+
'&euro;': '€',
35+
'&pound;': '£',
36+
'&yen;': '¥',
37+
'&cent;': '¢',
38+
'&times;': '×',
39+
'&divide;': '÷',
40+
'&plusmn;': '±',
41+
'&micro;': 'µ',
42+
'&para;': '¶',
43+
'&sect;': '§',
44+
'&bull;': '•',
45+
'&middot;': '·',
46+
};
47+
48+
return str.replace(/&[a-zA-Z0-9#]+;/g, (entity) => entities[entity] || entity);
49+
};

test/agentTester.test.ts

+40-40
Original file line numberDiff line numberDiff line change
@@ -129,46 +129,46 @@ testCases:
129129

130130
expect(contents).to.equal(`<?xml version="1.0" encoding="UTF-8"?>
131131
<AiEvaluationDefinition xmlns="http://soap.sforce.com/2006/04/metadata">
132-
<description>Test</description>
133-
<name>Test</name>
134-
<subjectType>AGENT</subjectType>
135-
<subjectName>MyAgent</subjectName>
136-
<testCase>
137-
<number>1</number>
138-
<inputs>
139-
<utterance>List contact names associated with Acme account</utterance>
140-
</inputs>
141-
<expectation>
142-
<name>topic_sequence_match</name>
143-
<expectedValue>GeneralCRM</expectedValue>
144-
</expectation>
145-
<expectation>
146-
<name>action_sequence_match</name>
147-
<expectedValue>[&quot;IdentifyRecordByName&quot;,&quot;QueryRecords&quot;]</expectedValue>
148-
</expectation>
149-
<expectation>
150-
<name>bot_response_rating</name>
151-
<expectedValue>contacts available name available with Acme are listed</expectedValue>
152-
</expectation>
153-
</testCase>
154-
<testCase>
155-
<number>2</number>
156-
<inputs>
157-
<utterance>List contact emails associated with Acme account</utterance>
158-
</inputs>
159-
<expectation>
160-
<name>topic_sequence_match</name>
161-
<expectedValue>GeneralCRM</expectedValue>
162-
</expectation>
163-
<expectation>
164-
<name>action_sequence_match</name>
165-
<expectedValue>[&quot;IdentifyRecordByName&quot;,&quot;QueryRecords&quot;]</expectedValue>
166-
</expectation>
167-
<expectation>
168-
<name>bot_response_rating</name>
169-
<expectedValue>contacts available emails available with Acme are listed</expectedValue>
170-
</expectation>
171-
</testCase>
132+
<description>Test</description>
133+
<name>Test</name>
134+
<subjectName>MyAgent</subjectName>
135+
<subjectType>AGENT</subjectType>
136+
<testCase>
137+
<expectation>
138+
<expectedValue>GeneralCRM</expectedValue>
139+
<name>topic_sequence_match</name>
140+
</expectation>
141+
<expectation>
142+
<expectedValue>[&quot;IdentifyRecordByName&quot;,&quot;QueryRecords&quot;]</expectedValue>
143+
<name>action_sequence_match</name>
144+
</expectation>
145+
<expectation>
146+
<expectedValue>contacts available name available with Acme are listed</expectedValue>
147+
<name>bot_response_rating</name>
148+
</expectation>
149+
<inputs>
150+
<utterance>List contact names associated with Acme account</utterance>
151+
</inputs>
152+
<number>1</number>
153+
</testCase>
154+
<testCase>
155+
<expectation>
156+
<expectedValue>GeneralCRM</expectedValue>
157+
<name>topic_sequence_match</name>
158+
</expectation>
159+
<expectation>
160+
<expectedValue>[&quot;IdentifyRecordByName&quot;,&quot;QueryRecords&quot;]</expectedValue>
161+
<name>action_sequence_match</name>
162+
</expectation>
163+
<expectation>
164+
<expectedValue>contacts available emails available with Acme are listed</expectedValue>
165+
<name>bot_response_rating</name>
166+
</expectation>
167+
<inputs>
168+
<utterance>List contact emails associated with Acme account</utterance>
169+
</inputs>
170+
<number>2</number>
171+
</testCase>
172172
</AiEvaluationDefinition>
173173
`);
174174
});

yarn.lock

+4-4
Original file line numberDiff line numberDiff line change
@@ -5953,10 +5953,10 @@ typedoc@^0.26.5:
59535953
shiki "^1.16.2"
59545954
yaml "^2.5.1"
59555955

5956-
typedoc@^0.27.7:
5957-
version "0.27.7"
5958-
resolved "https://registry.yarnpkg.com/typedoc/-/typedoc-0.27.7.tgz#09047ffb5c845f45765de26c68b77260867fe967"
5959-
integrity sha512-K/JaUPX18+61W3VXek1cWC5gwmuLvYTOXJzBvD9W7jFvbPnefRnCHQCEPw7MSNrP/Hj7JJrhZtDDLKdcYm6ucg==
5956+
typedoc@^0.27.8:
5957+
version "0.27.8"
5958+
resolved "https://registry.yarnpkg.com/typedoc/-/typedoc-0.27.8.tgz#9d4c4aa3c6889090d8fa4776367e353697d3e706"
5959+
integrity sha512-q0/2TUunNEDmWkn23ULKGXieK8cgGuAmBUXC/HcZ/rgzMI9Yr4Nq3in1K1vT1NZ9zx6M78yTk3kmIPbwJgK5KA==
59605960
dependencies:
59615961
"@gerrit0/mini-shiki" "^1.24.0"
59625962
lunr "^2.3.9"

0 commit comments

Comments
 (0)