newo 1.7.3 → 1.9.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/CHANGELOG.md CHANGED
@@ -5,6 +5,67 @@ 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.0] - 2025-09-16
9
+
10
+ ### Added
11
+ - **User Conversations Pull Functionality**: Complete conversation history extraction
12
+ - New `newo conversations` command to download user conversations and personas
13
+ - Multi-customer conversation support with `--customer <idn>` flag
14
+ - Chat History API integration (`/api/v1/chat/history`) with fallback to conversations acts API
15
+ - Automatic phone number extraction from persona actors
16
+ - Comprehensive pagination handling for large conversation datasets
17
+ - Clean YAML output format in `newo_customers/{customerIdn}/conversations.yaml`
18
+
19
+ ### Enhanced
20
+ - **Conversation Data Processing**: Optimized structure and chronological ordering
21
+ - Acts sorted by datetime ascending (chronological conversation flow)
22
+ - Personas sorted by most recent activity (descending)
23
+ - Redundant fields removed (`is_agent`, `session_id: unknown`, etc.)
24
+ - Clean persona structure: `id` → `name` → `phone` → `act_count` → `acts`
25
+ - Proper datetime extraction from chat history API responses
26
+
27
+ ### Technical
28
+ - **New API Functions**: Type-safe conversation API integration
29
+ - `listUserPersonas()` - Get all user personas with pagination
30
+ - `getChatHistory()` - Get conversation history for user actors
31
+ - `getConversationActs()` - Fallback for accounts with proper permissions
32
+ - `pullConversations()` - Complete conversation sync orchestration
33
+ - **NPM Scripts**: Added convenient conversation commands
34
+ - `npm run conversations` - Build and pull conversations
35
+ - `npm run conversations:all` - Legacy alias for compatibility
36
+
37
+ ### Performance
38
+ - **Concurrent Processing**: Efficient conversation data extraction
39
+ - Parallel API calls with concurrency limiting (p-limit)
40
+ - Graceful error handling with persona-level fault tolerance
41
+ - No artificial limits on personas or acts (loads all available data)
42
+ - Multi-customer support with authentication reuse
43
+
44
+ ## [1.8.0] - 2025-09-15
45
+
46
+ ### Added
47
+ - **Complete Metadata Change Tracking**: Comprehensive metadata.yaml file synchronization
48
+ - All metadata.yaml files now tracked with hash-based change detection
49
+ - Status command shows detailed metadata changes (title, runner_type, model)
50
+ - Push command automatically updates skills when metadata changes
51
+ - flows.yaml automatically regenerated when metadata changes detected
52
+ - Preserves flows.yaml format consistency with backup/comparison system
53
+
54
+ ### Enhanced
55
+ - **Comprehensive File Synchronization**: All NEWO workspace files fully tracked
56
+ - Skills: .guidance and .jinja script files with hash tracking ✓
57
+ - Metadata: metadata.yaml files with skill updates + flows.yaml regeneration ✓
58
+ - Attributes: attributes.yaml with diff-based sync for 233 customer attributes ✓
59
+ - Flows: flows.yaml with automatic regeneration and format preservation ✓
60
+ - Multi-customer: All file types synchronized across multiple customer workspaces ✓
61
+
62
+ ### Technical
63
+ - **flows.yaml Regeneration**: Automatic regeneration pipeline when metadata changes
64
+ - Creates backup before regeneration for format comparison
65
+ - Re-fetches project data to ensure accuracy
66
+ - Updates hash tracking for regenerated flows.yaml
67
+ - Maintains consistent YAML format structure
68
+
8
69
  ## [1.7.3] - 2025-09-15
9
70
 
10
71
  ### Added
package/dist/api.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { type AxiosInstance } from 'axios';
2
- import type { ProjectMeta, Agent, Skill, FlowEvent, FlowState, AkbImportArticle, CustomerProfile, CustomerAttribute, CustomerAttributesResponse } from './types.js';
2
+ import type { ProjectMeta, Agent, Skill, FlowEvent, FlowState, AkbImportArticle, CustomerProfile, CustomerAttribute, CustomerAttributesResponse, UserPersonaResponse, UserPersona, ConversationActsResponse, ConversationActsParams, ChatHistoryParams, ChatHistoryResponse } from './types.js';
3
3
  export declare function makeClient(verbose?: boolean, token?: string): Promise<AxiosInstance>;
4
4
  export declare function listProjects(client: AxiosInstance): Promise<ProjectMeta[]>;
5
5
  export declare function listAgents(client: AxiosInstance, projectId: string): Promise<Agent[]>;
@@ -13,4 +13,12 @@ export declare function importAkbArticle(client: AxiosInstance, articleData: Akb
13
13
  export declare function getCustomerProfile(client: AxiosInstance): Promise<CustomerProfile>;
14
14
  export declare function getCustomerAttributes(client: AxiosInstance, includeHidden?: boolean): Promise<CustomerAttributesResponse>;
15
15
  export declare function updateCustomerAttribute(client: AxiosInstance, attribute: CustomerAttribute): Promise<void>;
16
+ export declare function listUserPersonas(client: AxiosInstance, page?: number, per?: number): Promise<UserPersonaResponse>;
17
+ export declare function getUserPersona(client: AxiosInstance, personaId: string): Promise<UserPersona>;
18
+ export declare function getAccount(client: AxiosInstance): Promise<{
19
+ id: string;
20
+ [key: string]: any;
21
+ }>;
22
+ export declare function getConversationActs(client: AxiosInstance, params: ConversationActsParams): Promise<ConversationActsResponse>;
23
+ export declare function getChatHistory(client: AxiosInstance, params: ChatHistoryParams): Promise<ChatHistoryResponse>;
16
24
  //# sourceMappingURL=api.d.ts.map
package/dist/api.js CHANGED
@@ -121,4 +121,54 @@ export async function updateCustomerAttribute(client, attribute) {
121
121
  value_type: attribute.value_type
122
122
  });
123
123
  }
124
+ // Conversation API Functions
125
+ export async function listUserPersonas(client, page = 1, per = 50) {
126
+ const response = await client.get('/api/v1/bff/conversations/user-personas', {
127
+ params: { page, per }
128
+ });
129
+ return response.data;
130
+ }
131
+ export async function getUserPersona(client, personaId) {
132
+ const response = await client.get(`/api/v1/bff/conversations/user-personas/${personaId}`);
133
+ return response.data;
134
+ }
135
+ export async function getAccount(client) {
136
+ const response = await client.get('/api/v1/account');
137
+ return response.data;
138
+ }
139
+ export async function getConversationActs(client, params) {
140
+ const queryParams = {
141
+ user_persona_id: params.user_persona_id,
142
+ page: params.page || 1,
143
+ per: params.per || 50
144
+ };
145
+ // Only add optional parameters if explicitly provided
146
+ if (params.turn_type)
147
+ queryParams.turn_type = params.turn_type;
148
+ if (params.connectors)
149
+ queryParams.connectors = params.connectors;
150
+ if (params.from_date)
151
+ queryParams.from_date = params.from_date;
152
+ if (params.to_date)
153
+ queryParams.to_date = params.to_date;
154
+ const response = await client.get('/api/v1/bff/conversations/acts', {
155
+ params: queryParams
156
+ });
157
+ return response.data;
158
+ }
159
+ export async function getChatHistory(client, params) {
160
+ const queryParams = {
161
+ user_actor_id: params.user_actor_id,
162
+ page: params.page || 1,
163
+ per: params.per || 50
164
+ };
165
+ // Only add agent_actor_id if provided
166
+ if (params.agent_actor_id) {
167
+ queryParams.agent_actor_id = params.agent_actor_id;
168
+ }
169
+ const response = await client.get('/api/v1/chat/history', {
170
+ params: queryParams
171
+ });
172
+ return response.data;
173
+ }
124
174
  //# sourceMappingURL=api.js.map
package/dist/cli.js CHANGED
@@ -2,7 +2,7 @@
2
2
  import minimist from 'minimist';
3
3
  import dotenv from 'dotenv';
4
4
  import { makeClient, getProjectMeta, importAkbArticle } from './api.js';
5
- import { pullAll, pushChanged, status, saveCustomerAttributes } from './sync.js';
5
+ import { pullAll, pushChanged, status, saveCustomerAttributes, pullConversations } from './sync.js';
6
6
  import { parseAkbFile, prepareArticlesForImport } from './akb.js';
7
7
  import { initializeEnvironment, ENV, EnvValidationError } from './env.js';
8
8
  import { parseCustomerConfigAsync, listCustomers, getCustomer, getDefaultCustomer, tryGetDefaultCustomer, getAllCustomers, validateCustomerConfig } from './customerAsync.js';
@@ -160,12 +160,14 @@ Usage:
160
160
  newo pull [--customer <idn>] # download projects -> ./newo_customers/<idn>/projects/
161
161
  newo push [--customer <idn>] # upload modified *.guidance/*.jinja back to NEWO
162
162
  newo status [--customer <idn>] # show modified files
163
+ newo conversations [--customer <idn>] [--all] # download user conversations -> ./newo_customers/<idn>/conversations.yaml
163
164
  newo list-customers # list available customers
164
165
  newo meta [--customer <idn>] # get project metadata (debug)
165
166
  newo import-akb <file> <persona_id> [--customer <idn>] # import AKB articles from file
166
167
 
167
168
  Flags:
168
169
  --customer <idn> # specify customer (if not set, uses default or interactive selection)
170
+ --all # include all available data (for conversations: all personas and acts)
169
171
  --verbose, -v # enable detailed logging
170
172
 
171
173
  Environment Variables:
@@ -343,6 +345,53 @@ File Structure:
343
345
  }
344
346
  return;
345
347
  }
348
+ if (cmd === 'conversations') {
349
+ // Handle customer selection for conversations command
350
+ if (args.customer) {
351
+ const customer = getCustomer(customerConfig, args.customer);
352
+ if (!customer) {
353
+ console.error(`Unknown customer: ${args.customer}`);
354
+ console.error(`Available customers: ${listCustomers(customerConfig).join(', ')}`);
355
+ process.exit(1);
356
+ }
357
+ selectedCustomer = customer;
358
+ }
359
+ else {
360
+ // Try to get default, fall back to all customers
361
+ selectedCustomer = tryGetDefaultCustomer(customerConfig);
362
+ if (!selectedCustomer) {
363
+ allCustomers = getAllCustomers(customerConfig);
364
+ if (verbose)
365
+ console.log(`💬 No default customer specified, pulling conversations from all ${allCustomers.length} customers`);
366
+ }
367
+ }
368
+ // Parse conversation-specific options - load all data by default
369
+ const conversationOptions = {
370
+ includeAll: true, // Always include all data for conversations
371
+ maxPersonas: undefined, // No limit on personas
372
+ maxActsPerPersona: undefined // No limit on acts per persona
373
+ };
374
+ if (selectedCustomer) {
375
+ // Single customer conversations
376
+ const accessToken = await getValidAccessToken(selectedCustomer);
377
+ const client = await makeClient(verbose, accessToken);
378
+ console.log(`💬 Pulling conversations for customer: ${selectedCustomer.idn} (all data)`);
379
+ await pullConversations(client, selectedCustomer, conversationOptions, verbose);
380
+ console.log(`✅ Conversations saved to newo_customers/${selectedCustomer.idn}/conversations.yaml`);
381
+ }
382
+ else if (allCustomers.length > 0) {
383
+ // Multi-customer conversations
384
+ console.log(`💬 Pulling conversations from ${allCustomers.length} customers (all data)...`);
385
+ for (const customer of allCustomers) {
386
+ console.log(`\n💬 Pulling conversations for customer: ${customer.idn}`);
387
+ const accessToken = await getValidAccessToken(customer);
388
+ const client = await makeClient(verbose, accessToken);
389
+ await pullConversations(client, customer, conversationOptions, verbose);
390
+ }
391
+ console.log(`\n✅ Conversations pull completed for all ${allCustomers.length} customers`);
392
+ }
393
+ return;
394
+ }
346
395
  // For all other commands, require a single selected customer
347
396
  if (args.customer) {
348
397
  const customer = getCustomer(customerConfig, args.customer);
package/dist/fsutil.d.ts CHANGED
@@ -11,6 +11,7 @@ export declare function projectDir(customerIdn: string, projectIdn: string): str
11
11
  export declare function flowsYamlPath(customerIdn: string): string;
12
12
  export declare function customerAttributesPath(customerIdn: string): string;
13
13
  export declare function customerAttributesMapPath(customerIdn: string): string;
14
+ export declare function customerAttributesBackupPath(customerIdn: string): string;
14
15
  export declare function skillPath(customerIdn: string, projectIdn: string, agentIdn: string, flowIdn: string, skillIdn: string, runnerType?: RunnerType): string;
15
16
  export declare function skillFolderPath(customerIdn: string, projectIdn: string, agentIdn: string, flowIdn: string, skillIdn: string): string;
16
17
  export declare function skillScriptPath(customerIdn: string, projectIdn: string, agentIdn: string, flowIdn: string, skillIdn: string, runnerType?: RunnerType): string;
package/dist/fsutil.js CHANGED
@@ -34,6 +34,9 @@ export function customerAttributesPath(customerIdn) {
34
34
  export function customerAttributesMapPath(customerIdn) {
35
35
  return path.join(customerStateDir(customerIdn), 'attributes-map.json');
36
36
  }
37
+ export function customerAttributesBackupPath(customerIdn) {
38
+ return path.join(customerStateDir(customerIdn), 'attributes-backup.yaml');
39
+ }
37
40
  // Legacy skill path - direct file
38
41
  export function skillPath(customerIdn, projectIdn, agentIdn, flowIdn, skillIdn, runnerType = 'guidance') {
39
42
  const extension = runnerType === 'nsl' ? '.jinja' : '.guidance';
package/dist/sync.d.ts CHANGED
@@ -1,8 +1,9 @@
1
1
  import type { AxiosInstance } from 'axios';
2
- import type { ProjectData, CustomerConfig } from './types.js';
2
+ import type { ProjectData, CustomerConfig, ConversationOptions } from './types.js';
3
3
  export declare function saveCustomerAttributes(client: AxiosInstance, customer: CustomerConfig, verbose?: boolean): Promise<void>;
4
4
  export declare function pullSingleProject(client: AxiosInstance, customer: CustomerConfig, projectId: string, projectIdn: string, verbose?: boolean): Promise<ProjectData>;
5
5
  export declare function pullAll(client: AxiosInstance, customer: CustomerConfig, projectId?: string | null, verbose?: boolean): Promise<void>;
6
6
  export declare function pushChanged(client: AxiosInstance, customer: CustomerConfig, verbose?: boolean): Promise<void>;
7
7
  export declare function status(customer: CustomerConfig, verbose?: boolean): Promise<void>;
8
+ export declare function pullConversations(client: AxiosInstance, customer: CustomerConfig, options?: ConversationOptions, verbose?: boolean): Promise<void>;
8
9
  //# sourceMappingURL=sync.d.ts.map