@memberjunction/ai-agents 2.117.0 → 2.119.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.
package/README.md CHANGED
@@ -54,10 +54,12 @@ Deterministic workflow agent type that:
54
54
  - Support for Action, Sub-Agent, and Prompt loop bodies
55
55
 
56
56
  ### AgentRunner
57
- Simple orchestrator that:
57
+ Orchestrator that provides multiple execution modes:
58
58
  - Loads agent metadata from database
59
59
  - Instantiates correct agent class using ClassFactory
60
60
  - Executes agents with provided context
61
+ - **RunAgent**: Core execution method for direct agent invocation
62
+ - **RunAgentInConversation**: Integrated execution with conversation and artifact management
61
63
 
62
64
  ### PayloadManager
63
65
  Advanced payload access control for hierarchical agent execution:
@@ -107,6 +109,181 @@ const followUpResult = await runner.RunAgent({
107
109
  });
108
110
  ```
109
111
 
112
+ ### RunAgentInConversation - Integrated Conversation & Artifact Management
113
+
114
+ The `RunAgentInConversation` method provides a complete workflow for executing agents within a conversation context, automatically handling conversation creation, artifact generation, and linking:
115
+
116
+ ```typescript
117
+ import { AgentRunner } from '@memberjunction/ai-agents';
118
+ import { UserInfo } from '@memberjunction/core';
119
+
120
+ const runner = new AgentRunner();
121
+
122
+ // Execute agent with automatic conversation and artifact management
123
+ const result = await runner.RunAgentInConversation({
124
+ agent: agentEntity,
125
+ conversationMessages: messages,
126
+ contextUser: user
127
+ }, {
128
+ // Optional: Use existing conversation
129
+ conversationId: 'existing-conversation-id',
130
+
131
+ // Optional: Use existing conversation detail (skips creation)
132
+ conversationDetailId: 'existing-detail-id',
133
+
134
+ // Required if conversationDetailId not provided
135
+ userMessage: 'Analyze the sales data for Q4',
136
+
137
+ // Optional: Control artifact creation (default: true)
138
+ createArtifacts: true,
139
+
140
+ // Optional: Source artifact for versioning (continuity/refinement)
141
+ sourceArtifactId: 'base-artifact-id',
142
+
143
+ // Optional: Custom conversation name
144
+ conversationName: 'Q4 Sales Analysis'
145
+ });
146
+
147
+ // Result includes everything you need
148
+ console.log('Agent result:', result.agentResult);
149
+ console.log('Conversation ID:', result.conversationId);
150
+ console.log('Detail ID:', result.conversationDetailId);
151
+ if (result.artifactInfo) {
152
+ console.log('Artifact created:', result.artifactInfo.artifactId);
153
+ console.log('Version:', result.artifactInfo.versionNumber);
154
+ }
155
+ ```
156
+
157
+ #### What RunAgentInConversation Does
158
+
159
+ The method provides a complete workflow:
160
+
161
+ 1. **Conversation Management**
162
+ - Creates new conversation if not provided
163
+ - Uses existing conversation if `conversationId` provided
164
+ - Skips creation entirely if `conversationDetailId` provided
165
+
166
+ 2. **Conversation Detail Creation**
167
+ - Creates conversation detail record for user message
168
+ - Automatically handles message ordering via `__mj_CreatedAt`
169
+ - Skipped if `conversationDetailId` already provided
170
+
171
+ 3. **Agent Execution**
172
+ - Runs agent with conversation context
173
+ - Links agent run to conversation detail
174
+ - Passes through all execution parameters (callbacks, data, context, etc.)
175
+
176
+ 4. **Artifact Processing** (if `createArtifacts !== false`)
177
+ - Creates artifacts from agent payload
178
+ - Handles intelligent versioning:
179
+ - Uses `sourceArtifactId` if provided (explicit continuity)
180
+ - Otherwise checks for previous artifacts on this conversation detail
181
+ - Creates new artifact version or entirely new artifact as appropriate
182
+ - Respects agent's `ArtifactCreationMode` configuration
183
+ - Links artifacts to conversation details via junction table
184
+ - Extracts artifact names from payload attributes
185
+
186
+ #### Artifact Versioning Logic
187
+
188
+ The method implements smart artifact versioning:
189
+
190
+ ```typescript
191
+ // Priority 1: Explicit source artifact (agent continuity/refinement)
192
+ {
193
+ sourceArtifactId: 'artifact-to-refine'
194
+ // Creates version 2, 3, 4, etc. of the specified artifact
195
+ }
196
+
197
+ // Priority 2: Previous artifact on this conversation detail (fallback)
198
+ // Automatically finds last artifact linked to the conversation detail
199
+ // Creates next version of that artifact
200
+
201
+ // Priority 3: No previous artifact
202
+ // Creates entirely new artifact with version 1
203
+ ```
204
+
205
+ #### Respecting Agent Configuration
206
+
207
+ The method honors agent-level settings:
208
+
209
+ - **`ArtifactCreationMode === 'Never'`**: Skips artifact creation entirely
210
+ - **`ArtifactCreationMode === 'System Only'`**: Creates artifact with `Visibility='System Only'`
211
+ - **`DefaultArtifactTypeID`**: Uses agent's preferred artifact type (defaults to JSON type)
212
+
213
+ #### Use Cases
214
+
215
+ **GraphQL Resolvers** - Simplify agent execution endpoints:
216
+ ```typescript
217
+ // Before: Manually manage conversations, artifacts, notifications
218
+ // After: One method call handles everything
219
+ const result = await runner.RunAgentInConversation({...}, {
220
+ conversationDetailId: args.conversationDetailId,
221
+ createArtifacts: args.createArtifacts,
222
+ sourceArtifactId: args.sourceArtifactId
223
+ });
224
+ ```
225
+
226
+ **Interactive Chat Interfaces** - Maintain conversation context:
227
+ ```typescript
228
+ // First message - creates conversation
229
+ const firstResult = await runner.RunAgentInConversation({...}, {
230
+ userMessage: 'Analyze sales data',
231
+ createArtifacts: true
232
+ });
233
+
234
+ // Follow-up message - uses existing conversation
235
+ const followUp = await runner.RunAgentInConversation({...}, {
236
+ conversationId: firstResult.conversationId,
237
+ userMessage: 'Show me the trends',
238
+ createArtifacts: true
239
+ });
240
+ ```
241
+
242
+ **Agent Refinement Workflows** - Iterate on artifacts:
243
+ ```typescript
244
+ // Initial generation
245
+ const initial = await runner.RunAgentInConversation({...}, {
246
+ userMessage: 'Create a report',
247
+ createArtifacts: true
248
+ });
249
+
250
+ // Refinement - creates version 2 of same artifact
251
+ const refined = await runner.RunAgentInConversation({...}, {
252
+ userMessage: 'Make it more concise',
253
+ createArtifacts: true,
254
+ sourceArtifactId: initial.artifactInfo.artifactId
255
+ });
256
+ ```
257
+
258
+ #### Benefits
259
+
260
+ - **Single Responsibility**: One method handles entire workflow
261
+ - **Flexible**: Works with new or existing conversations
262
+ - **Intelligent**: Smart artifact versioning without manual tracking
263
+ - **Clean Code**: Moves business logic out of transport layers (GraphQL, REST)
264
+ - **Type Safe**: Full TypeScript typing for results
265
+ - **Auditable**: All artifacts linked to conversation details
266
+
267
+ #### Helper Methods
268
+
269
+ `RunAgentInConversation` uses these public helper methods (available for custom workflows):
270
+
271
+ ```typescript
272
+ // Get maximum version number for an artifact
273
+ const maxVersion = await runner.GetMaxVersionForArtifact(artifactId, user);
274
+
275
+ // Find previous artifact for a conversation detail
276
+ const previousArtifact = await runner.FindPreviousArtifactForMessage(detailId, user);
277
+
278
+ // Process agent artifacts manually
279
+ const artifactInfo = await runner.ProcessAgentArtifacts(
280
+ agentResult,
281
+ conversationDetailId,
282
+ sourceArtifactId,
283
+ user
284
+ );
285
+ ```
286
+
110
287
  ## Iterative Operations (v2.112+)
111
288
 
112
289
  Both Flow and Loop agents support native ForEach and While iterations for efficient batch processing and retry logic.
@@ -1040,6 +1217,206 @@ return {
1040
1217
  };
1041
1218
  ```
1042
1219
 
1220
+ ### Conversation Message Mapping for Actions and Sub-Agents
1221
+
1222
+ The framework includes a **ConversationMessageResolver** utility that enables flexible conversation message referencing in action input mappings and sub-agent configurations. This is particularly useful for passing conversation context to knowledge base assistants, chatbots, or analysis agents.
1223
+
1224
+ #### Basic Usage with Actions
1225
+
1226
+ **In Flow Agent Step Configuration** (`ActionInputMapping`):
1227
+ ```typescript
1228
+ // Pass full conversation history to an action
1229
+ {
1230
+ "ActionInputMapping": {
1231
+ "ConversationMessages": "conversation.all"
1232
+ }
1233
+ }
1234
+ ```
1235
+
1236
+ **In Loop Agent Response**:
1237
+ ```typescript
1238
+ {
1239
+ "taskComplete": false,
1240
+ "nextStep": {
1241
+ "type": "Actions",
1242
+ "actions": [{
1243
+ "name": "Betty", // Knowledge base assistant
1244
+ "params": {
1245
+ "ConversationMessages": "conversation.all"
1246
+ }
1247
+ }]
1248
+ }
1249
+ }
1250
+ ```
1251
+
1252
+ #### Supported Conversation Patterns
1253
+
1254
+ The `ConversationMessageResolver` supports powerful pattern-based message selection:
1255
+
1256
+ **1. All Messages**:
1257
+ ```typescript
1258
+ "ConversationMessages": "conversation.all"
1259
+ // Returns entire conversation history
1260
+ ```
1261
+
1262
+ **2. Role-Based Selection**:
1263
+ ```typescript
1264
+ // Last N user messages
1265
+ "ConversationMessages": "conversation.user.last[5]"
1266
+
1267
+ // Last N assistant messages
1268
+ "ConversationMessages": "conversation.assistant.last[3]"
1269
+
1270
+ // Last N system messages
1271
+ "ConversationMessages": "conversation.system.last[1]"
1272
+ ```
1273
+
1274
+ **3. All Messages of a Role**:
1275
+ ```typescript
1276
+ // All user messages
1277
+ "ConversationMessages": "conversation.user.all"
1278
+
1279
+ // All assistant messages
1280
+ "ConversationMessages": "conversation.assistant.all"
1281
+ ```
1282
+
1283
+ **4. Single Last Message by Role**:
1284
+ ```typescript
1285
+ // Just the last user message
1286
+ "ConversationMessages": "conversation.user.last"
1287
+
1288
+ // Just the last assistant message
1289
+ "ConversationMessages": "conversation.assistant.last"
1290
+ ```
1291
+
1292
+ #### Use Cases
1293
+
1294
+ **Knowledge Base Assistants** - Pass full conversation history for context-aware responses:
1295
+ ```typescript
1296
+ // In Knowledge Base Research Agent step
1297
+ {
1298
+ "StepType": "Action",
1299
+ "ActionID": "betty-action-id",
1300
+ "ActionInputMapping": {
1301
+ "ConversationMessages": "conversation.all" // Betty sees full context
1302
+ }
1303
+ }
1304
+ ```
1305
+
1306
+ **Sentiment Analysis** - Analyze just user messages:
1307
+ ```typescript
1308
+ {
1309
+ "ActionInputMapping": {
1310
+ "messagesToAnalyze": "conversation.user.last[10]",
1311
+ "includeSentiment": true
1312
+ }
1313
+ }
1314
+ ```
1315
+
1316
+ **Context Summarization** - Summarize recent conversation:
1317
+ ```typescript
1318
+ {
1319
+ "ActionInputMapping": {
1320
+ "recentMessages": "conversation.all", // or "conversation.last[20]" if supported
1321
+ "summarizeAs": "bullet_points"
1322
+ }
1323
+ }
1324
+ ```
1325
+
1326
+ **Follow-up Question Generation** - Based on assistant responses:
1327
+ ```typescript
1328
+ {
1329
+ "ActionInputMapping": {
1330
+ "previousResponses": "conversation.assistant.last[3]",
1331
+ "generateFollowUps": true
1332
+ }
1333
+ }
1334
+ ```
1335
+
1336
+ #### Sub-Agent Usage
1337
+
1338
+ Sub-agents automatically receive the parent's conversation context, but you can control which messages are passed:
1339
+
1340
+ **In Loop Agent** (via agent prompt instructions):
1341
+ ```typescript
1342
+ // The agent's system prompt can instruct:
1343
+ "When invoking sub-agents, you can specify which conversation messages to pass using the ConversationMessages parameter in your action input mappings."
1344
+ ```
1345
+
1346
+ **In Flow Agent** (via SubAgentConfiguration):
1347
+ ```typescript
1348
+ // Configure sub-agent relationships with conversation context
1349
+ {
1350
+ "AgentID": "parent-agent-id",
1351
+ "SubAgentID": "knowledge-base-research-agent-id",
1352
+ "SubAgentOutputMapping": { "*": "knowledgeBaseResearch" },
1353
+ "SubAgentContextPaths": ["*"] // Full context by default
1354
+ }
1355
+ ```
1356
+
1357
+ #### How It Works
1358
+
1359
+ The resolver operates during the parameter mapping phase:
1360
+
1361
+ 1. **Detection**: Identifies `conversation.` prefixed strings in action/sub-agent parameters
1362
+ 2. **Pattern Matching**: Parses the pattern (role, selector, count)
1363
+ 3. **Message Filtering**: Extracts matching messages from conversation history
1364
+ 4. **Type Validation**: Ensures the target parameter expects an array of messages
1365
+ 5. **Injection**: Replaces the pattern with actual message objects
1366
+
1367
+ **Example Resolution**:
1368
+ ```typescript
1369
+ // Input mapping configuration
1370
+ {
1371
+ "ConversationMessages": "conversation.user.last[3]"
1372
+ }
1373
+
1374
+ // Conversation history
1375
+ [
1376
+ { role: 'system', content: 'You are a helpful assistant' },
1377
+ { role: 'user', content: 'What is MemberJunction?' },
1378
+ { role: 'assistant', content: 'MemberJunction is...' },
1379
+ { role: 'user', content: 'How do agents work?' },
1380
+ { role: 'assistant', content: 'Agents work by...' },
1381
+ { role: 'user', content: 'Can you give an example?' }
1382
+ ]
1383
+
1384
+ // Resolved parameter value (last 3 user messages)
1385
+ [
1386
+ { role: 'user', content: 'What is MemberJunction?' },
1387
+ { role: 'user', content: 'How do agents work?' },
1388
+ { role: 'user', content: 'Can you give an example?' }
1389
+ ]
1390
+ ```
1391
+
1392
+ #### Benefits
1393
+
1394
+ - **Context-Aware Actions**: Actions receive relevant conversation history
1395
+ - **Flexible Filtering**: Select exactly which messages are needed
1396
+ - **Declarative Configuration**: No custom code needed in action implementations
1397
+ - **Type Safety**: Resolver validates parameter types at runtime
1398
+ - **Performance**: Only selected messages are passed, reducing token usage
1399
+ - **Composability**: Works seamlessly with Flow and Loop agents
1400
+
1401
+ #### Integration with Betty Knowledge Base Action
1402
+
1403
+ A prime example of this feature is the Betty action for knowledge base queries:
1404
+
1405
+ ```typescript
1406
+ // Betty action accepts ConversationMessages parameter
1407
+ {
1408
+ "name": "Betty",
1409
+ "params": {
1410
+ "ConversationMessages": "conversation.all" // Full conversation context
1411
+ }
1412
+ }
1413
+
1414
+ // Betty uses the conversation history to provide context-aware responses
1415
+ // and can reference earlier questions/answers in its knowledge base queries
1416
+ ```
1417
+
1418
+ This enables knowledge base agents to maintain conversation context across multiple queries, improving response relevance and follow-up question handling.
1419
+
1043
1420
  ## Agent Permissions System
1044
1421
 
1045
1422
  The agent framework includes a comprehensive ACL-based permissions system that controls who can view, run, edit, and delete agents.
@@ -1506,7 +1883,7 @@ Paths are the edges connecting your workflow nodes. They determine the flow:
1506
1883
  }
1507
1884
  ```
1508
1885
 
1509
- **Action Output Mapping** (`ActionOutputMapping`) - Maps action results back to payload:
1886
+ **Action Output Mapping** (`ActionOutputMapping`) - Maps action results back to payload or special fields:
1510
1887
 
1511
1888
  ```typescript
1512
1889
  // In AIAgentStep.ActionOutputMapping
@@ -1514,13 +1891,42 @@ Paths are the edges connecting your workflow nodes. They determine the flow:
1514
1891
  "userId": "payload.customer.id", // Map specific output param
1515
1892
  "orderTotal": "payload.order.total", // Nested path in payload
1516
1893
  "metadata": "payload.action.lastResult", // Arbitrary nesting
1517
- "*": "payload.rawResults.fullData" // Wildcard = entire result
1894
+ "*": "payload.rawResults.fullData", // Wildcard = entire result
1895
+ "responseText": "$message", // Special field - user message
1896
+ "analysisDetails": "$reasoning", // Special field - reasoning
1897
+ "confidenceScore": "$confidence" // Special field - confidence
1518
1898
  }
1519
1899
 
1520
1900
  // Case-insensitive output parameter matching
1521
1901
  // If action returns { UserId: "123" }, it matches "userId" in mapping
1522
1902
  ```
1523
1903
 
1904
+ **Special Fields** (Flow Agents Only):
1905
+
1906
+ Use the `$` prefix to map action outputs to special response fields instead of the payload:
1907
+
1908
+ - **`$message`**: Maps to the user-facing message in the final Success step
1909
+ - **`$reasoning`**: Optional reasoning/explanation shown with the response
1910
+ - **`$confidence`**: Optional confidence score (number) for the response
1911
+
1912
+ **Example - Betty Knowledge Base Agent:**
1913
+ ```typescript
1914
+ // Betty action returns: { BettyResponse: "The answer is...", BettyReferences: [...] }
1915
+ {
1916
+ "BettyResponse": "$message", // Shows directly to user
1917
+ "BettyReferences": "references" // Stored in payload
1918
+ }
1919
+
1920
+ // When flow completes, user sees Betty's response as the message
1921
+ // No LLM processing needed - deterministic, single-step flow
1922
+ ```
1923
+
1924
+ **Special Field Benefits:**
1925
+ - ✅ Eliminates need for LLM to format final response
1926
+ - ✅ Enables deterministic flows with dynamic user messages
1927
+ - ✅ Clearly separates UI content from payload data
1928
+ - ✅ No namespace pollution - can still have `message` in payload
1929
+
1524
1930
  #### 5. Prompt Result Merging
1525
1931
 
1526
1932
  When a Prompt step executes, its JSON response is **deep merged** into the payload:
@@ -1,5 +1,36 @@
1
+ import { UserInfo } from '@memberjunction/core';
1
2
  import { ExecuteAgentResult, ExecuteAgentParams } from '@memberjunction/ai-core-plus';
2
3
  export declare class AgentRunner {
3
4
  RunAgent<C = any, R = any>(params: ExecuteAgentParams<C>): Promise<ExecuteAgentResult<R>>;
5
+ RunAgentInConversation<C = any, R = any>(params: ExecuteAgentParams<C>, options: {
6
+ conversationId?: string;
7
+ conversationDetailId?: string;
8
+ userMessage?: string;
9
+ createArtifacts?: boolean;
10
+ sourceArtifactId?: string;
11
+ conversationName?: string;
12
+ testRunId?: string;
13
+ }): Promise<{
14
+ agentResult: ExecuteAgentResult<R>;
15
+ conversationId: string;
16
+ userMessageDetailId: string;
17
+ agentResponseDetailId?: string;
18
+ artifactInfo?: {
19
+ artifactId: string;
20
+ versionId: string;
21
+ versionNumber: number;
22
+ };
23
+ }>;
24
+ GetMaxVersionForArtifact(artifactId: string, contextUser: UserInfo): Promise<number>;
25
+ FindPreviousArtifactForMessage(conversationDetailId: string, contextUser: UserInfo): Promise<{
26
+ artifactId: string;
27
+ versionNumber: number;
28
+ } | null>;
29
+ ProcessAgentArtifacts<R>(agentResult: ExecuteAgentResult<R>, conversationDetailId: string, sourceArtifactId: string | undefined, contextUser: UserInfo): Promise<{
30
+ artifactId: string;
31
+ versionId: string;
32
+ versionNumber: number;
33
+ } | undefined>;
34
+ private GenerateConversationName;
4
35
  }
5
36
  //# sourceMappingURL=AgentRunner.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"AgentRunner.d.ts","sourceRoot":"","sources":["../src/AgentRunner.ts"],"names":[],"mappings":"AAeA,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,8BAA8B,CAAC;AAsBtF,qBAAa,WAAW;IAgBP,QAAQ,CAAC,CAAC,GAAG,GAAG,EAAE,CAAC,GAAG,GAAG,EAAE,MAAM,EAAE,kBAAkB,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAAC,CAAC,CAAC;CA8CzG"}
1
+ {"version":3,"file":"AgentRunner.d.ts","sourceRoot":"","sources":["../src/AgentRunner.ts"],"names":[],"mappings":"AAYA,OAAO,EAAgF,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAG9H,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,8BAA8B,CAAC;AAuBtF,qBAAa,WAAW;IAgBP,QAAQ,CAAC,CAAC,GAAG,GAAG,EAAE,CAAC,GAAG,GAAG,EAAE,MAAM,EAAE,kBAAkB,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAAC,CAAC,CAAC;IA2EzF,sBAAsB,CAAC,CAAC,GAAG,GAAG,EAAE,CAAC,GAAG,GAAG,EAChD,MAAM,EAAE,kBAAkB,CAAC,CAAC,CAAC,EAC7B,OAAO,EAAE;QAEL,cAAc,CAAC,EAAE,MAAM,CAAC;QAExB,oBAAoB,CAAC,EAAE,MAAM,CAAC;QAE9B,WAAW,CAAC,EAAE,MAAM,CAAC;QAErB,eAAe,CAAC,EAAE,OAAO,CAAC;QAE1B,gBAAgB,CAAC,EAAE,MAAM,CAAC;QAE1B,gBAAgB,CAAC,EAAE,MAAM,CAAC;QAE1B,SAAS,CAAC,EAAE,MAAM,CAAC;KACtB,GACF,OAAO,CAAC;QAEP,WAAW,EAAE,kBAAkB,CAAC,CAAC,CAAC,CAAC;QAEnC,cAAc,EAAE,MAAM,CAAC;QAEvB,mBAAmB,EAAE,MAAM,CAAC;QAE5B,qBAAqB,CAAC,EAAE,MAAM,CAAC;QAE/B,YAAY,CAAC,EAAE;YACX,UAAU,EAAE,MAAM,CAAC;YACnB,SAAS,EAAE,MAAM,CAAC;YAClB,aAAa,EAAE,MAAM,CAAC;SACzB,CAAC;KACL,CAAC;IA8OW,wBAAwB,CAAC,UAAU,EAAE,MAAM,EAAE,WAAW,EAAE,QAAQ,GAAG,OAAO,CAAC,MAAM,CAAC;IAuCpF,8BAA8B,CACvC,oBAAoB,EAAE,MAAM,EAC5B,WAAW,EAAE,QAAQ,GACtB,OAAO,CAAC;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,aAAa,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IAuEnD,qBAAqB,CAAC,CAAC,EAChC,WAAW,EAAE,kBAAkB,CAAC,CAAC,CAAC,EAClC,oBAAoB,EAAE,MAAM,EAC5B,gBAAgB,EAAE,MAAM,GAAG,SAAS,EACpC,WAAW,EAAE,QAAQ,GACtB,OAAO,CAAC;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,aAAa,EAAE,MAAM,CAAA;KAAE,GAAG,SAAS,CAAC;YAgK1E,wBAAwB;CAmDzC"}