newo 1.9.1 → 1.9.2

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/CHANGELOG.md CHANGED
@@ -5,6 +5,21 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [1.9.2] - 2025-09-16
9
+
10
+ ### Fixed
11
+ - **Pagination Safety**: Prevent infinite loops and hanging during conversation processing
12
+ - Added max pages limit (50 pages = 5000 acts per persona) to prevent infinite pagination
13
+ - Improved persona filtering for voice actors to avoid unnecessary API calls
14
+ - Better error handling and verbose logging for debugging conversation processing
15
+ - Graceful handling of personas without voice actors (adds with empty acts array)
16
+
17
+ ### Enhanced
18
+ - **Performance Optimization**: Faster conversation processing with early exits
19
+ - Skip personas without newo_voice actors immediately
20
+ - Clear verbose logging for pagination progress
21
+ - Optimized control flow to prevent hanging during large conversation extraction
22
+
8
23
  ## [1.9.1] - 2025-09-16
9
24
 
10
25
  ### Fixed
package/dist/sync.js CHANGED
@@ -1057,68 +1057,97 @@ export async function pullConversations(client, customer, options = {}, verbose
1057
1057
  let actPage = 1;
1058
1058
  const actsPerPage = 100; // Higher limit for acts
1059
1059
  let hasMoreActs = true;
1060
- while (hasMoreActs) {
1061
- // First try the chat history API as alternative
1060
+ // Get user actor IDs from persona actors first
1061
+ const userActors = persona.actors.filter(actor => actor.integration_idn === 'newo_voice' &&
1062
+ actor.connector_idn === 'newo_voice_connector');
1063
+ if (userActors.length === 0) {
1064
+ if (verbose)
1065
+ console.log(` 👤 ${persona.name}: No voice actors found, skipping`);
1066
+ // No voice actors, can't get chat history - add persona with empty acts
1067
+ processedPersonas.push({
1068
+ id: persona.id,
1069
+ name: persona.name,
1070
+ phone,
1071
+ act_count: persona.act_count,
1072
+ acts: []
1073
+ });
1074
+ if (verbose)
1075
+ console.log(` ✓ Processed ${persona.name}: 0 acts (no voice actors)`);
1076
+ return; // Return from the concurrency function
1077
+ }
1078
+ // Safety mechanism to prevent infinite loops
1079
+ const maxPages = 50; // Limit to 50 pages (5000 acts max per persona)
1080
+ while (hasMoreActs && actPage <= maxPages) {
1062
1081
  try {
1063
- // Get user actor IDs from persona actors
1064
- const userActors = persona.actors.filter(actor => actor.integration_idn === 'newo_voice' &&
1065
- actor.connector_idn === 'newo_voice_connector');
1066
- if (userActors.length > 0) {
1067
- const chatHistoryParams = {
1082
+ const chatHistoryParams = {
1083
+ user_actor_id: userActors[0].id,
1084
+ page: actPage,
1085
+ per: actsPerPage
1086
+ };
1087
+ if (verbose)
1088
+ console.log(` 📄 ${persona.name}: Fetching page ${actPage}...`);
1089
+ const chatResponse = await getChatHistory(client, chatHistoryParams);
1090
+ if (chatResponse.items && chatResponse.items.length > 0) {
1091
+ // Convert chat history format to acts format - create minimal ConversationAct objects
1092
+ const convertedActs = chatResponse.items.map((item) => ({
1093
+ id: item.id || `chat_${Math.random()}`,
1094
+ command_act_id: null,
1095
+ external_event_id: item.external_event_id || 'chat_history',
1096
+ arguments: [],
1097
+ reference_idn: (item.is_agent === true) ? 'agent_message' : 'user_message',
1098
+ runtime_context_id: item.runtime_context_id || 'chat_history',
1099
+ source_text: item.payload?.text || item.message || item.content || item.text || '',
1100
+ original_text: item.payload?.text || item.message || item.content || item.text || '',
1101
+ datetime: item.datetime || item.created_at || item.timestamp || new Date().toISOString(),
1068
1102
  user_actor_id: userActors[0].id,
1069
- page: actPage,
1070
- per: actsPerPage
1071
- };
1072
- const chatResponse = await getChatHistory(client, chatHistoryParams);
1073
- if (chatResponse.items && chatResponse.items.length > 0) {
1074
- // Convert chat history format to acts format - create minimal ConversationAct objects
1075
- const convertedActs = chatResponse.items.map((item) => ({
1076
- id: item.id || `chat_${Math.random()}`,
1077
- command_act_id: null,
1078
- external_event_id: item.external_event_id || 'chat_history',
1079
- arguments: [],
1080
- reference_idn: (item.is_agent === true) ? 'agent_message' : 'user_message',
1081
- runtime_context_id: item.runtime_context_id || 'chat_history',
1082
- source_text: item.payload?.text || item.message || item.content || item.text || '',
1083
- original_text: item.payload?.text || item.message || item.content || item.text || '',
1084
- datetime: item.datetime || item.created_at || item.timestamp || new Date().toISOString(),
1085
- user_actor_id: userActors[0].id,
1086
- agent_actor_id: null,
1087
- user_persona_id: persona.id,
1088
- user_persona_name: persona.name,
1089
- agent_persona_id: item.agent_persona_id || 'unknown',
1090
- external_id: item.external_id || null,
1091
- integration_idn: 'newo_voice',
1092
- connector_idn: 'newo_voice_connector',
1093
- to_integration_idn: null,
1094
- to_connector_idn: null,
1095
- is_agent: Boolean(item.is_agent === true),
1096
- project_idn: null,
1097
- flow_idn: item.flow_idn || 'unknown',
1098
- skill_idn: item.skill_idn || 'unknown',
1099
- session_id: item.session_id || 'unknown',
1100
- recordings: item.recordings || [],
1101
- contact_information: item.contact_information || null
1102
- }));
1103
- allActs.push(...convertedActs);
1104
- if (verbose && convertedActs.length > 0) {
1105
- console.log(` 👤 ${persona.name}: Chat History - ${convertedActs.length} messages (${allActs.length} total)`);
1106
- }
1107
- hasMoreActs = chatResponse.items.length === actsPerPage &&
1108
- chatResponse.metadata?.total !== undefined &&
1109
- allActs.length < chatResponse.metadata.total;
1110
- actPage++;
1111
- continue; // Skip the original acts API call
1103
+ agent_actor_id: null,
1104
+ user_persona_id: persona.id,
1105
+ user_persona_name: persona.name,
1106
+ agent_persona_id: item.agent_persona_id || 'unknown',
1107
+ external_id: item.external_id || null,
1108
+ integration_idn: 'newo_voice',
1109
+ connector_idn: 'newo_voice_connector',
1110
+ to_integration_idn: null,
1111
+ to_connector_idn: null,
1112
+ is_agent: Boolean(item.is_agent === true),
1113
+ project_idn: null,
1114
+ flow_idn: item.flow_idn || 'unknown',
1115
+ skill_idn: item.skill_idn || 'unknown',
1116
+ session_id: item.session_id || 'unknown',
1117
+ recordings: item.recordings || [],
1118
+ contact_information: item.contact_information || null
1119
+ }));
1120
+ allActs.push(...convertedActs);
1121
+ if (verbose && convertedActs.length > 0) {
1122
+ console.log(` 👤 ${persona.name}: Chat History - ${convertedActs.length} messages (${allActs.length} total)`);
1112
1123
  }
1124
+ // Check if we should continue paginating
1125
+ const hasMetadata = chatResponse.metadata?.total !== undefined;
1126
+ const currentTotal = chatResponse.metadata?.total || 0;
1127
+ hasMoreActs = chatResponse.items.length === actsPerPage &&
1128
+ hasMetadata &&
1129
+ allActs.length < currentTotal;
1130
+ actPage++;
1131
+ if (verbose)
1132
+ console.log(` 📊 ${persona.name}: Page ${actPage - 1} done, ${allActs.length}/${currentTotal} total acts`);
1133
+ }
1134
+ else {
1135
+ // No more items
1136
+ hasMoreActs = false;
1137
+ if (verbose)
1138
+ console.log(` 📊 ${persona.name}: No more chat history items`);
1113
1139
  }
1114
1140
  }
1115
1141
  catch (chatError) {
1116
1142
  if (verbose)
1117
1143
  console.log(` ⚠️ Chat history failed for ${persona.name}: ${chatError instanceof Error ? chatError.message : String(chatError)}`);
1118
- // No fallback - only use chat history API
1119
1144
  hasMoreActs = false;
1120
1145
  }
1121
1146
  }
1147
+ if (actPage > maxPages) {
1148
+ if (verbose)
1149
+ console.log(` ⚠️ ${persona.name}: Reached max pages limit (${maxPages}), stopping pagination`);
1150
+ }
1122
1151
  // Sort acts by datetime ascending (chronological order)
1123
1152
  allActs.sort((a, b) => new Date(a.datetime).getTime() - new Date(b.datetime).getTime());
1124
1153
  // Process acts into simplified format - exclude redundant fields
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "newo",
3
- "version": "1.9.1",
3
+ "version": "1.9.2",
4
4
  "description": "NEWO CLI: comprehensive sync for AI Agent skills, customer attributes, conversations, and metadata between NEWO platform and local files. Multi-customer workspaces, conversation history extraction, complete change tracking, Git-first workflows.",
5
5
  "type": "module",
6
6
  "bin": {
package/src/sync.ts CHANGED
@@ -1234,24 +1234,41 @@ export async function pullConversations(
1234
1234
  const actsPerPage = 100; // Higher limit for acts
1235
1235
  let hasMoreActs = true;
1236
1236
 
1237
- while (hasMoreActs) {
1238
- // First try the chat history API as alternative
1237
+ // Get user actor IDs from persona actors first
1238
+ const userActors = persona.actors.filter(actor =>
1239
+ actor.integration_idn === 'newo_voice' &&
1240
+ actor.connector_idn === 'newo_voice_connector'
1241
+ );
1242
+
1243
+ if (userActors.length === 0) {
1244
+ if (verbose) console.log(` 👤 ${persona.name}: No voice actors found, skipping`);
1245
+ // No voice actors, can't get chat history - add persona with empty acts
1246
+ processedPersonas.push({
1247
+ id: persona.id,
1248
+ name: persona.name,
1249
+ phone,
1250
+ act_count: persona.act_count,
1251
+ acts: []
1252
+ });
1253
+ if (verbose) console.log(` ✓ Processed ${persona.name}: 0 acts (no voice actors)`);
1254
+ return; // Return from the concurrency function
1255
+ }
1256
+
1257
+ // Safety mechanism to prevent infinite loops
1258
+ const maxPages = 50; // Limit to 50 pages (5000 acts max per persona)
1259
+
1260
+ while (hasMoreActs && actPage <= maxPages) {
1239
1261
  try {
1240
- // Get user actor IDs from persona actors
1241
- const userActors = persona.actors.filter(actor =>
1242
- actor.integration_idn === 'newo_voice' &&
1243
- actor.connector_idn === 'newo_voice_connector'
1244
- );
1245
-
1246
- if (userActors.length > 0) {
1247
- const chatHistoryParams = {
1248
- user_actor_id: userActors[0]!.id,
1249
- page: actPage,
1250
- per: actsPerPage
1251
- };
1252
-
1253
- const chatResponse = await getChatHistory(client, chatHistoryParams);
1254
- if (chatResponse.items && chatResponse.items.length > 0) {
1262
+ const chatHistoryParams = {
1263
+ user_actor_id: userActors[0]!.id,
1264
+ page: actPage,
1265
+ per: actsPerPage
1266
+ };
1267
+
1268
+ if (verbose) console.log(` 📄 ${persona.name}: Fetching page ${actPage}...`);
1269
+ const chatResponse = await getChatHistory(client, chatHistoryParams);
1270
+
1271
+ if (chatResponse.items && chatResponse.items.length > 0) {
1255
1272
  // Convert chat history format to acts format - create minimal ConversationAct objects
1256
1273
  const convertedActs: ConversationAct[] = chatResponse.items.map((item: any) => ({
1257
1274
  id: item.id || `chat_${Math.random()}`,
@@ -1288,20 +1305,32 @@ export async function pullConversations(
1288
1305
  console.log(` 👤 ${persona.name}: Chat History - ${convertedActs.length} messages (${allActs.length} total)`);
1289
1306
  }
1290
1307
 
1308
+ // Check if we should continue paginating
1309
+ const hasMetadata = chatResponse.metadata?.total !== undefined;
1310
+ const currentTotal = chatResponse.metadata?.total || 0;
1311
+
1291
1312
  hasMoreActs = chatResponse.items.length === actsPerPage &&
1292
- chatResponse.metadata?.total !== undefined &&
1293
- allActs.length < chatResponse.metadata.total;
1313
+ hasMetadata &&
1314
+ allActs.length < currentTotal;
1315
+
1294
1316
  actPage++;
1295
- continue; // Skip the original acts API call
1317
+
1318
+ if (verbose) console.log(` 📊 ${persona.name}: Page ${actPage - 1} done, ${allActs.length}/${currentTotal} total acts`);
1319
+ } else {
1320
+ // No more items
1321
+ hasMoreActs = false;
1322
+ if (verbose) console.log(` 📊 ${persona.name}: No more chat history items`);
1296
1323
  }
1297
- }
1298
1324
  } catch (chatError) {
1299
1325
  if (verbose) console.log(` ⚠️ Chat history failed for ${persona.name}: ${chatError instanceof Error ? chatError.message : String(chatError)}`);
1300
- // No fallback - only use chat history API
1301
1326
  hasMoreActs = false;
1302
1327
  }
1303
1328
  }
1304
1329
 
1330
+ if (actPage > maxPages) {
1331
+ if (verbose) console.log(` ⚠️ ${persona.name}: Reached max pages limit (${maxPages}), stopping pagination`);
1332
+ }
1333
+
1305
1334
  // Sort acts by datetime ascending (chronological order)
1306
1335
  allActs.sort((a, b) => new Date(a.datetime).getTime() - new Date(b.datetime).getTime());
1307
1336