newo 3.0.0 → 3.1.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
@@ -20,6 +20,7 @@ Sync NEWO "Project → Agent → Flow → Skills" structure to local files with:
20
20
  - 🧠 **AI skill formats** - Support for `.guidance` (AI prompts) and `.jinja` (NSL templates)
21
21
  - 📊 **Knowledge base import** - Bulk import AKB articles from structured text files
22
22
  - 💬 **Conversation history** - Extract and sync user conversations and personas
23
+ - 🧪 **Sandbox testing** - Interactive agent testing with conversation continuation (NEW v3.1.0)
23
24
  - 🔧 **CI/CD ready** - GitHub Actions integration for automated deployments
24
25
 
25
26
  ---
@@ -142,6 +143,7 @@ NEWO_REFRESH_URL=custom_refresh_endpoint # Custom refresh endpoint
142
143
  | `newo pull` | Download projects + attributes + metadata | • Real-time progress tracking (966+ skills)<br>• IDN-based file naming<br>• Automatic attributes.yaml generation<br>• `--force` for silent overwrite |
143
144
  | `newo push` | Upload local changes to NEWO | • Smart file validation<br>• Multiple file detection<br>• Hash-based change detection<br>• Safe error handling |
144
145
  | `newo status` | Show modified files with details | • Multiple file warnings<br>• Detailed change analysis<br>• Clean state validation<br>• Per-customer status |
146
+ | `newo sandbox` | Test agents in sandbox chat mode | • Single-command mode for automation<br>• Multi-turn conversation support<br>• Debug info for agent development<br>• Conversation continuation |
145
147
  | `newo conversations` | Pull conversation history | • User personas and chat history<br>• YAML format output<br>• Pagination support |
146
148
  | `newo list-customers` | List configured customers | • Shows default customer<br>• Multi-customer discovery |
147
149
  | `newo import-akb` | Import knowledge base articles | • Structured text parsing<br>• Bulk article import<br>• Validation and error reporting |
@@ -476,6 +478,107 @@ Add these secrets to your repository:
476
478
  node ./dist/cli.js push
477
479
  ```
478
480
 
481
+ ## Sandbox Testing (NEW v3.1.0)
482
+
483
+ Test your NEWO agents in real-time with sandbox chat mode. Perfect for development, debugging, and automated testing workflows.
484
+
485
+ ### Features
486
+ - **Single-command mode** - Send a message and get a response (ideal for automation)
487
+ - **Multi-turn conversations** - Continue chats with conversation context preserved
488
+ - **Debug information** - View flow execution, skill invocation, and session tracking
489
+ - **Unique sessions** - Each test creates a fresh persona for isolation
490
+
491
+ ### Usage
492
+
493
+ **Start a new conversation:**
494
+ ```bash
495
+ newo sandbox "Hello, I want to order a pizza"
496
+ ```
497
+
498
+ **Continue an existing conversation:**
499
+ ```bash
500
+ newo sandbox --actor <chat-id> "I want 2 large pepperoni pizzas"
501
+ ```
502
+
503
+ **With verbose debugging:**
504
+ ```bash
505
+ newo sandbox "Test message" --verbose
506
+ ```
507
+
508
+ ### Example: Multi-Turn Conversation
509
+
510
+ ```bash
511
+ # Turn 1: Start conversation
512
+ $ newo sandbox "I want to order delivery"
513
+
514
+ 📋 Chat Session Created:
515
+ Chat ID (actor_id): abc123...
516
+ Persona ID: xyz789...
517
+ Connector: convo_agent_sandbox
518
+ External ID: 2f99f7
519
+
520
+ 📤 You: I want to order delivery
521
+
522
+ 🤖 Agent:
523
+ Awesome! We can definitely get a delivery order started for you! What's your zip code, please?
524
+
525
+ 📊 Debug Summary:
526
+ Flow: CAMainFlow
527
+ Skill: _userMessageFastReplySkill
528
+ Session: 816c769a-8e1c-43e7-b22d-766c7bf63c33
529
+ Acts Processed: 1 (1 agent, 0 system)
530
+
531
+ 💡 To continue this conversation:
532
+ npx newo sandbox --actor abc123... "your next message"
533
+
534
+
535
+ # Turn 2: Continue conversation
536
+ $ newo sandbox --actor abc123... "90210"
537
+
538
+ 📤 You: 90210
539
+
540
+ 🤖 Agent:
541
+ Perfect! Now, could you please provide your delivery address?
542
+
543
+ 📊 Debug Summary:
544
+ Flow: CAMainFlow
545
+ Skill: CollectAddressSkill
546
+ Session: 816c769a-8e1c-43e7-b22d-766c7bf63c33
547
+ Acts Processed: 1 (1 agent, 0 user)
548
+ ```
549
+
550
+ ### Debug Information
551
+
552
+ **Standard Mode** shows:
553
+ - Flow execution path
554
+ - Skill invocation
555
+ - Session ID
556
+ - Act counts (agent vs. system messages)
557
+
558
+ **Verbose Mode** (`--verbose`) shows:
559
+ - All API requests and responses
560
+ - Complete act structure with arguments
561
+ - Runtime context IDs
562
+ - Detailed polling progress
563
+
564
+ ### Automated Testing Integration
565
+
566
+ Perfect for CI/CD workflows:
567
+
568
+ ```bash
569
+ # Test agent responses
570
+ RESPONSE=$(newo sandbox "test query" | grep "Agent:" | cut -d: -f2-)
571
+
572
+ # Validate response contains expected content
573
+ echo "$RESPONSE" | grep -q "expected text" && echo "✓ Test passed"
574
+
575
+ # Multi-turn testing
576
+ ACTOR_ID=$(newo sandbox "start conversation" | grep "Chat ID" | awk '{print $NF}')
577
+ newo sandbox --actor "$ACTOR_ID" "follow up message"
578
+ ```
579
+
580
+ ---
581
+
479
582
  ## AKB Import
480
583
 
481
584
  Import knowledge base articles from structured text files into NEWO personas:
@@ -1097,6 +1200,14 @@ NEWO CLI integrates with these NEWO platform endpoints:
1097
1200
  - `GET /api/v1/bff/customer/attributes?include_hidden=true` - Get customer attributes
1098
1201
  - `PUT /api/v1/customer/attributes/{attributeId}` - Update customer attribute
1099
1202
 
1203
+ ### Sandbox Testing (NEW v3.1.0)
1204
+ - `GET /api/v1/integrations` - List available integrations
1205
+ - `GET /api/v1/integrations/{id}/connectors` - List integration connectors
1206
+ - `POST /api/v1/customer/personas` - Create user persona for chat
1207
+ - `POST /api/v1/customer/personas/{id}/actors` - Create actor (chat session)
1208
+ - `POST /api/v1/chat/user/{actorId}` - Send chat message
1209
+ - `GET /api/v1/chat/history` - Poll for agent responses
1210
+
1100
1211
  ### Knowledge Base
1101
1212
  - `POST /api/v1/akb/append-manual` - Import AKB articles to persona
1102
1213
 
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, UserPersonaResponse, UserPersona, ChatHistoryParams, ChatHistoryResponse, CreateAgentRequest, CreateAgentResponse, CreateFlowRequest, CreateFlowResponse, CreateSkillRequest, CreateSkillResponse, CreateFlowEventRequest, CreateFlowEventResponse, CreateFlowStateRequest, CreateFlowStateResponse, CreateSkillParameterRequest, CreateSkillParameterResponse, CreateCustomerAttributeRequest, CreateCustomerAttributeResponse, CreatePersonaRequest, CreatePersonaResponse, CreateProjectRequest, CreateProjectResponse, PublishFlowRequest, PublishFlowResponse } from './types.js';
2
+ import type { ProjectMeta, Agent, Skill, FlowEvent, FlowState, AkbImportArticle, CustomerProfile, CustomerAttribute, CustomerAttributesResponse, UserPersonaResponse, UserPersona, ChatHistoryParams, ChatHistoryResponse, CreateAgentRequest, CreateAgentResponse, CreateFlowRequest, CreateFlowResponse, CreateSkillRequest, CreateSkillResponse, CreateFlowEventRequest, CreateFlowEventResponse, CreateFlowStateRequest, CreateFlowStateResponse, CreateSkillParameterRequest, CreateSkillParameterResponse, CreateCustomerAttributeRequest, CreateCustomerAttributeResponse, CreatePersonaRequest, CreatePersonaResponse, CreateProjectRequest, CreateProjectResponse, PublishFlowRequest, PublishFlowResponse, Integration, Connector, CreateSandboxPersonaRequest, CreateSandboxPersonaResponse, CreateActorRequest, CreateActorResponse, SendChatMessageRequest, ConversationActsParams, ConversationActsResponse } 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[]>;
@@ -36,4 +36,10 @@ export declare function createProject(client: AxiosInstance, projectData: Create
36
36
  export declare function deleteProject(client: AxiosInstance, projectId: string): Promise<void>;
37
37
  export declare function createPersona(client: AxiosInstance, personaData: CreatePersonaRequest): Promise<CreatePersonaResponse>;
38
38
  export declare function publishFlow(client: AxiosInstance, flowId: string, publishData: PublishFlowRequest): Promise<PublishFlowResponse>;
39
+ export declare function listIntegrations(client: AxiosInstance): Promise<Integration[]>;
40
+ export declare function listConnectors(client: AxiosInstance, integrationId: string): Promise<Connector[]>;
41
+ export declare function createSandboxPersona(client: AxiosInstance, personaData: CreateSandboxPersonaRequest): Promise<CreateSandboxPersonaResponse>;
42
+ export declare function createActor(client: AxiosInstance, personaId: string, actorData: CreateActorRequest): Promise<CreateActorResponse>;
43
+ export declare function sendChatMessage(client: AxiosInstance, actorId: string, messageData: SendChatMessageRequest): Promise<void>;
44
+ export declare function getConversationActs(client: AxiosInstance, params: ConversationActsParams): Promise<ConversationActsResponse>;
39
45
  //# sourceMappingURL=api.d.ts.map
package/dist/api.js CHANGED
@@ -229,4 +229,40 @@ export async function publishFlow(client, flowId, publishData) {
229
229
  const response = await client.post(`/api/v1/designer/flows/${flowId}/publish`, publishData);
230
230
  return response.data;
231
231
  }
232
+ // Sandbox Chat API Functions
233
+ export async function listIntegrations(client) {
234
+ const response = await client.get('/api/v1/integrations');
235
+ return response.data;
236
+ }
237
+ export async function listConnectors(client, integrationId) {
238
+ const response = await client.get(`/api/v1/integrations/${integrationId}/connectors`);
239
+ return response.data;
240
+ }
241
+ export async function createSandboxPersona(client, personaData) {
242
+ const response = await client.post('/api/v1/customer/personas', personaData);
243
+ return response.data;
244
+ }
245
+ export async function createActor(client, personaId, actorData) {
246
+ const response = await client.post(`/api/v1/customer/personas/${personaId}/actors`, actorData);
247
+ return response.data;
248
+ }
249
+ export async function sendChatMessage(client, actorId, messageData) {
250
+ await client.post(`/api/v1/chat/user/${actorId}`, messageData);
251
+ }
252
+ export async function getConversationActs(client, params) {
253
+ const queryParams = {
254
+ user_persona_id: params.user_persona_id,
255
+ user_actor_id: params.user_actor_id,
256
+ per: params.per || 100,
257
+ page: params.page || 1
258
+ };
259
+ // Only add agent_persona_id if provided
260
+ if (params.agent_persona_id) {
261
+ queryParams.agent_persona_id = params.agent_persona_id;
262
+ }
263
+ const response = await client.get('/api/v1/bff/conversations/acts', {
264
+ params: queryParams
265
+ });
266
+ return response.data;
267
+ }
232
268
  //# sourceMappingURL=api.js.map
package/dist/auth.js CHANGED
@@ -48,6 +48,10 @@ function validateUrl(url, name) {
48
48
  }
49
49
  // Enhanced logging function
50
50
  function logAuthEvent(level, message, meta) {
51
+ // Skip all logging if in quiet mode
52
+ if (process.env.NEWO_QUIET_MODE === 'true') {
53
+ return;
54
+ }
51
55
  const timestamp = new Date().toISOString();
52
56
  const logEntry = {
53
57
  timestamp,
@@ -10,6 +10,8 @@ Core Commands:
10
10
  newo push [--customer <idn>] [--no-publish] # upload modified *.guidance/*.jinja + attributes back to NEWO, publish flows by default
11
11
  newo status [--customer <idn>] # show modified files that would be pushed
12
12
  newo conversations [--customer <idn>] [--all] # download user conversations -> ./newo_customers/<idn>/conversations.yaml
13
+ newo sandbox "<message>" [--customer <idn>] # test agent in sandbox - single message mode (NEW v3.1.0)
14
+ newo sandbox --actor <id> "message" # continue existing sandbox conversation with chat ID
13
15
  newo pull-attributes [--customer <idn>] # download customer attributes -> ./newo_customers/<idn>/attributes.yaml
14
16
  newo list-customers # list available customers and their configuration
15
17
  newo meta [--customer <idn>] # get project metadata (debug command)
@@ -45,6 +47,8 @@ Flags:
45
47
  --all # include all available data (for conversations: all personas and acts)
46
48
  --force, -f # force overwrite without prompting (for pull command)
47
49
  --verbose, -v # enable detailed logging and progress information
50
+ --quiet, -q # minimal output for automation (sandbox only)
51
+ --actor <id> # continue existing sandbox chat with actor/chat ID
48
52
  --confirm # confirm destructive operations without prompting
49
53
  --no-publish # skip automatic flow publishing during push operations
50
54
 
@@ -107,6 +111,12 @@ Usage Examples:
107
111
  # Import AKB articles:
108
112
  newo import-akb articles.txt da4550db-2b95-4500-91ff-fb4b60fe7be9
109
113
 
114
+ # Sandbox testing (NEW v3.1.0):
115
+ newo sandbox "Hello, I want to order pizza" # Start new conversation
116
+ newo sandbox --actor abc123... "I want 2 large pizzas" # Continue conversation
117
+ newo sandbox "Test query" --verbose # With debug info
118
+ newo sandbox "Test query" --quiet # For automation/scripts
119
+
110
120
  File Structure:
111
121
  newo_customers/
112
122
  ├── <customer-idn>/
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Sandbox Chat Command Handler
3
+ * Supports both single-command and interactive modes
4
+ */
5
+ import type { MultiCustomerConfig, CliArgs } from '../../types.js';
6
+ /**
7
+ * Handle sandbox command
8
+ * Usage:
9
+ * npx newo sandbox "Hello" --customer <idn> # Single message mode
10
+ * npx newo sandbox --actor <actor_id> "Follow up" # Continue existing chat
11
+ * npx newo sandbox --interactive # Interactive mode (TBD)
12
+ */
13
+ export declare function handleSandboxCommand(customerConfig: MultiCustomerConfig, args: CliArgs, verbose: boolean): Promise<void>;
14
+ //# sourceMappingURL=sandbox.d.ts.map
@@ -0,0 +1,306 @@
1
+ /**
2
+ * Sandbox Chat Command Handler
3
+ * Supports both single-command and interactive modes
4
+ */
5
+ import { makeClient } from '../../api.js';
6
+ import { getValidAccessToken } from '../../auth.js';
7
+ import { selectSingleCustomer } from '../customer-selection.js';
8
+ import { getChatHistory } from '../../api.js';
9
+ import { findSandboxConnector, createChatSession, sendMessage, pollForResponse, extractAgentMessages, formatDebugInfo } from '../../sandbox/chat.js';
10
+ /**
11
+ * Handle sandbox command
12
+ * Usage:
13
+ * npx newo sandbox "Hello" --customer <idn> # Single message mode
14
+ * npx newo sandbox --actor <actor_id> "Follow up" # Continue existing chat
15
+ * npx newo sandbox --interactive # Interactive mode (TBD)
16
+ */
17
+ export async function handleSandboxCommand(customerConfig, args, verbose) {
18
+ const quiet = Boolean(args.quiet || args.q);
19
+ // Save original console functions
20
+ const originalConsoleLog = console.log;
21
+ const originalConsoleError = console.error;
22
+ const originalConsoleWarn = console.warn;
23
+ // In quiet mode, set environment variable to suppress auth logging AND suppress console
24
+ if (quiet) {
25
+ process.env.NEWO_QUIET_MODE = 'true';
26
+ console.log = () => { };
27
+ console.error = () => { };
28
+ console.warn = () => { };
29
+ }
30
+ try {
31
+ // Select customer
32
+ const customerArg = args.customer;
33
+ const result = selectSingleCustomer(customerConfig, customerArg);
34
+ if (!result.selectedCustomer) {
35
+ if (!quiet) {
36
+ console.error = originalConsoleError;
37
+ console.error('❌ No customer selected');
38
+ }
39
+ process.exit(1);
40
+ }
41
+ // Get access token and create client (quiet mode already suppressing logs)
42
+ const token = await getValidAccessToken(result.selectedCustomer);
43
+ const client = await makeClient(quiet ? false : verbose, token);
44
+ // Restore console for our own output
45
+ if (quiet) {
46
+ console.log = originalConsoleLog;
47
+ console.error = originalConsoleError;
48
+ console.warn = originalConsoleWarn;
49
+ }
50
+ // Check for interactive mode
51
+ const interactive = args.interactive || args.i;
52
+ if (interactive) {
53
+ if (!quiet) {
54
+ console.log('❌ Interactive mode not yet implemented');
55
+ console.log(' Use single-command mode: npx newo sandbox "your message"');
56
+ }
57
+ process.exit(1);
58
+ }
59
+ // Check if continuing existing chat
60
+ const actorId = args.actor;
61
+ // Extract message from arguments (position depends on whether --actor is used)
62
+ const messageArg = args._[1];
63
+ if (!messageArg) {
64
+ if (!quiet) {
65
+ console.log('❌ Message is required');
66
+ console.log('Usage: npx newo sandbox "your message" [--actor <id>]');
67
+ console.log(' or: npx newo sandbox --actor <id> "your message"');
68
+ }
69
+ process.exit(1);
70
+ }
71
+ // Convert to string (minimist may parse numbers)
72
+ const message = String(messageArg);
73
+ if (message.trim() === '') {
74
+ if (!quiet)
75
+ console.log('❌ Message cannot be empty');
76
+ process.exit(1);
77
+ }
78
+ if (actorId) {
79
+ // Continue existing chat
80
+ await continueExistingChat(client, actorId, message, verbose, quiet, originalConsoleLog, originalConsoleError, originalConsoleWarn);
81
+ }
82
+ else {
83
+ // Start new chat
84
+ await startNewChat(client, message, verbose, quiet, originalConsoleLog, originalConsoleError, originalConsoleWarn);
85
+ }
86
+ }
87
+ catch (error) {
88
+ // Restore console for error reporting
89
+ if (quiet) {
90
+ console.error = originalConsoleError;
91
+ console.log = originalConsoleLog;
92
+ console.warn = originalConsoleWarn;
93
+ }
94
+ console.error('❌ Sandbox chat error:', error.message);
95
+ if (verbose && error.response?.data) {
96
+ console.error(' Response data:', JSON.stringify(error.response.data, null, 2));
97
+ }
98
+ process.exit(1);
99
+ }
100
+ finally {
101
+ // Always restore console functions and clear quiet mode flag
102
+ if (quiet) {
103
+ console.log = originalConsoleLog;
104
+ console.error = originalConsoleError;
105
+ console.warn = originalConsoleWarn;
106
+ delete process.env.NEWO_QUIET_MODE;
107
+ }
108
+ }
109
+ }
110
+ /**
111
+ * Start a new sandbox chat and send a message
112
+ */
113
+ async function startNewChat(client, message, verbose, quiet = false, originalConsoleLog = console.log, _originalConsoleError = console.error, _originalConsoleWarn = console.warn) {
114
+ if (!quiet)
115
+ console.log('🔧 Starting new sandbox chat...\n');
116
+ // Find sandbox connector
117
+ const connector = await findSandboxConnector(client, quiet ? false : verbose);
118
+ if (!connector) {
119
+ if (!quiet) {
120
+ console.error('❌ No running sandbox connector found');
121
+ console.error(' Please ensure you have a sandbox connector configured in your NEWO project');
122
+ }
123
+ process.exit(1);
124
+ }
125
+ // Create chat session
126
+ const session = await createChatSession(client, connector, quiet ? false : verbose);
127
+ if (!quiet) {
128
+ console.log(`\n📋 Chat Session Created:`);
129
+ console.log(` Chat ID (actor_id): ${session.user_actor_id}`);
130
+ console.log(` Persona ID: ${session.user_persona_id}`);
131
+ console.log(` Connector: ${session.connector_idn}`);
132
+ console.log(` External ID: ${session.external_id}\n`);
133
+ console.log(`📤 You: ${message}\n`);
134
+ }
135
+ else {
136
+ // In quiet mode, output Chat ID FIRST to stdout
137
+ originalConsoleLog(`CHAT_ID:${session.user_actor_id}`);
138
+ originalConsoleLog(`You: ${message}`);
139
+ }
140
+ const sentAt = await sendMessage(client, session, message, quiet ? false : verbose);
141
+ // Poll for response
142
+ const { acts, agentPersonaId } = await pollForResponse(client, session, sentAt, quiet ? false : verbose);
143
+ if (acts.length === 0) {
144
+ if (!quiet) {
145
+ console.log('⏱️ No response received within timeout period');
146
+ console.log(` You can continue this chat with: npx newo sandbox --actor ${session.user_actor_id} "your message"`);
147
+ }
148
+ return;
149
+ }
150
+ // Update session with agent_persona_id
151
+ session.agent_persona_id = agentPersonaId;
152
+ // Extract agent messages - show only the MOST RECENT one
153
+ const agentMessages = extractAgentMessages(acts);
154
+ if (agentMessages.length > 0) {
155
+ // Show only the latest agent message (messages are in reverse chronological order from API)
156
+ const latestAgentMessage = agentMessages[0];
157
+ if (latestAgentMessage) {
158
+ if (quiet) {
159
+ // Quiet mode: ONLY message content
160
+ originalConsoleLog(`Agent: ${latestAgentMessage.source_text || latestAgentMessage.original_text}`);
161
+ }
162
+ else {
163
+ // Normal mode: full output
164
+ console.log('🤖 Agent:');
165
+ console.log(` ${latestAgentMessage.source_text || latestAgentMessage.original_text}`);
166
+ console.log('');
167
+ if (verbose && agentMessages.length > 1) {
168
+ console.log(`ℹ️ Note: Received ${agentMessages.length} agent messages, showing latest only\n`);
169
+ }
170
+ }
171
+ }
172
+ }
173
+ // In quiet mode, skip all debug output and continuation info completely
174
+ if (quiet) {
175
+ return; // Exit early, showing only messages
176
+ }
177
+ // Display debug information (skip in quiet mode)
178
+ if (!quiet) {
179
+ if (verbose) {
180
+ console.log('\n📊 Debug Information:');
181
+ console.log(formatDebugInfo(acts));
182
+ console.log('');
183
+ }
184
+ else {
185
+ // Show condensed debug info for single-command mode
186
+ console.log('📊 Debug Summary:');
187
+ const agentActs = acts.filter(a => a.is_agent);
188
+ if (agentActs.length > 0) {
189
+ const lastAct = agentActs[agentActs.length - 1];
190
+ if (lastAct) {
191
+ console.log(` Flow: ${lastAct.flow_idn || 'N/A'}`);
192
+ console.log(` Skill: ${lastAct.skill_idn || 'N/A'}`);
193
+ console.log(` Session: ${lastAct.session_id}`);
194
+ }
195
+ console.log(` Acts Processed: ${acts.length} (${agentActs.length} agent, ${acts.length - agentActs.length} system)`);
196
+ }
197
+ console.log('');
198
+ }
199
+ // Show continuation info
200
+ console.log(`💡 To continue this conversation:`);
201
+ console.log(` npx newo sandbox --actor ${session.user_actor_id} "your next message"`);
202
+ console.log('');
203
+ }
204
+ }
205
+ /**
206
+ * Continue an existing sandbox chat
207
+ */
208
+ async function continueExistingChat(client, actorId, message, verbose, quiet = false, originalConsoleLog = console.log, _originalConsoleError = console.error, _originalConsoleWarn = console.warn) {
209
+ if (!quiet) {
210
+ console.log(`💬 Continuing chat...`);
211
+ console.log(` Chat ID: ${actorId}\n`);
212
+ }
213
+ // First, get current chat history to find the last message ID
214
+ const historyResponse = await getChatHistory(client, {
215
+ user_actor_id: actorId,
216
+ page: 1,
217
+ per: 100
218
+ });
219
+ // Get the last message ID
220
+ let lastMessageId = null;
221
+ if (historyResponse.items && historyResponse.items.length > 0) {
222
+ const lastItem = historyResponse.items[0];
223
+ if (lastItem && 'id' in lastItem) {
224
+ lastMessageId = lastItem.id;
225
+ }
226
+ }
227
+ if (verbose && lastMessageId && !quiet) {
228
+ console.log(`📌 Last message ID: ${lastMessageId}`);
229
+ }
230
+ // Create a temporary session for the existing chat
231
+ const session = {
232
+ user_actor_id: actorId,
233
+ user_persona_id: 'unknown', // Not needed for continuation
234
+ agent_persona_id: null,
235
+ connector_idn: 'sandbox',
236
+ session_id: null,
237
+ external_id: 'continuation'
238
+ };
239
+ // Send message (use original console in quiet mode)
240
+ if (quiet) {
241
+ originalConsoleLog(`You: ${message}`);
242
+ }
243
+ else {
244
+ console.log(`📤 You: ${message}\n`);
245
+ }
246
+ const sentAt = await sendMessage(client, session, message, quiet ? false : verbose);
247
+ // Poll for response using timestamp-based filtering
248
+ const { acts } = await pollForResponse(client, session, sentAt, quiet ? false : verbose);
249
+ if (acts.length === 0) {
250
+ if (!quiet) {
251
+ console.log('⏱️ No response received within timeout period');
252
+ console.log(` You can continue this chat with: npx newo sandbox --actor ${actorId} "your message"`);
253
+ }
254
+ return;
255
+ }
256
+ // Extract agent messages - show only the MOST RECENT one
257
+ const agentMessages = extractAgentMessages(acts);
258
+ if (agentMessages.length > 0) {
259
+ // Show only the latest agent message (messages are in reverse chronological order from API)
260
+ const latestAgentMessage = agentMessages[0];
261
+ if (latestAgentMessage) {
262
+ if (quiet) {
263
+ // Quiet mode: ONLY message content
264
+ originalConsoleLog(`Agent: ${latestAgentMessage.source_text || latestAgentMessage.original_text}`);
265
+ return; // Exit immediately, no debug output
266
+ }
267
+ else {
268
+ // Normal mode: full output
269
+ console.log('🤖 Agent:');
270
+ console.log(` ${latestAgentMessage.source_text || latestAgentMessage.original_text}`);
271
+ console.log('');
272
+ if (verbose && agentMessages.length > 1) {
273
+ console.log(`ℹ️ Note: Received ${agentMessages.length} agent messages, showing latest only\n`);
274
+ }
275
+ }
276
+ }
277
+ }
278
+ // Display debug information (skip in quiet mode)
279
+ if (!quiet) {
280
+ if (verbose) {
281
+ console.log('\n📊 Debug Information:');
282
+ console.log(formatDebugInfo(acts));
283
+ console.log('');
284
+ }
285
+ else {
286
+ // Show condensed debug info
287
+ console.log('📊 Debug Summary:');
288
+ const agentActs = acts.filter(a => a.is_agent);
289
+ if (agentActs.length > 0) {
290
+ const lastAct = agentActs[agentActs.length - 1];
291
+ if (lastAct) {
292
+ console.log(` Flow: ${lastAct.flow_idn || 'N/A'}`);
293
+ console.log(` Skill: ${lastAct.skill_idn || 'N/A'}`);
294
+ console.log(` Session: ${lastAct.session_id}`);
295
+ }
296
+ console.log(` Acts Processed: ${acts.length} (${agentActs.length} agent, ${acts.length - agentActs.length} user)`);
297
+ }
298
+ console.log('');
299
+ }
300
+ // Show continuation info
301
+ console.log(`💡 To continue this conversation:`);
302
+ console.log(` npx newo sandbox --actor ${actorId} "your next message"`);
303
+ console.log('');
304
+ }
305
+ }
306
+ //# sourceMappingURL=sandbox.js.map
package/dist/cli.js CHANGED
@@ -28,6 +28,7 @@ import { handleCreateStateCommand } from './cli/commands/create-state.js';
28
28
  import { handleCreateParameterCommand } from './cli/commands/create-parameter.js';
29
29
  import { handleCreatePersonaCommand } from './cli/commands/create-persona.js';
30
30
  import { handleCreateAttributeCommand } from './cli/commands/create-attribute.js';
31
+ import { handleSandboxCommand } from './cli/commands/sandbox.js';
31
32
  dotenv.config();
32
33
  async function main() {
33
34
  try {
@@ -41,6 +42,11 @@ async function main() {
41
42
  const args = minimist(process.argv.slice(2));
42
43
  const cmd = args._[0];
43
44
  const verbose = Boolean(args.verbose || args.v);
45
+ const quiet = Boolean(args.quiet || args.q);
46
+ // Set quiet mode flag EARLY to suppress auth logging
47
+ if (quiet) {
48
+ process.env.NEWO_QUIET_MODE = 'true';
49
+ }
44
50
  if (verbose)
45
51
  console.log(`🔍 Command parsed: "${cmd}"`);
46
52
  // Handle help command first
@@ -77,6 +83,9 @@ async function main() {
77
83
  case 'conversations':
78
84
  await handleConversationsCommand(customerConfig, args, verbose);
79
85
  break;
86
+ case 'sandbox':
87
+ await handleSandboxCommand(customerConfig, args, verbose);
88
+ break;
80
89
  case 'meta':
81
90
  await handleMetaCommand(customerConfig, args, verbose);
82
91
  break;
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Sandbox Chat Utility Module
3
+ * Handles chat session management, message sending, and polling for responses
4
+ */
5
+ import type { AxiosInstance } from 'axios';
6
+ import type { SandboxChatSession, Connector, ConversationAct, ChatDebugInfo } from '../types.js';
7
+ /**
8
+ * Find a sandbox connector from the customer's connectors list
9
+ */
10
+ export declare function findSandboxConnector(client: AxiosInstance, verbose?: boolean): Promise<Connector | null>;
11
+ /**
12
+ * Create a new sandbox chat session
13
+ */
14
+ export declare function createChatSession(client: AxiosInstance, connector: Connector, verbose?: boolean): Promise<SandboxChatSession>;
15
+ /**
16
+ * Send a message in the chat session
17
+ * Returns the timestamp when message was sent (for filtering responses)
18
+ */
19
+ export declare function sendMessage(client: AxiosInstance, session: SandboxChatSession, text: string, verbose?: boolean): Promise<Date>;
20
+ /**
21
+ * Poll for new conversation acts (messages and debug info)
22
+ * Continues polling until we get an agent response, not just any new message
23
+ */
24
+ export declare function pollForResponse(client: AxiosInstance, session: SandboxChatSession, messageSentAt?: Date | null, verbose?: boolean): Promise<{
25
+ acts: ConversationAct[];
26
+ agentPersonaId: string | null;
27
+ }>;
28
+ /**
29
+ * Extract agent messages from acts
30
+ */
31
+ export declare function extractAgentMessages(acts: ConversationAct[]): ConversationAct[];
32
+ /**
33
+ * Extract debug information from acts
34
+ */
35
+ export declare function extractDebugInfo(acts: ConversationAct[]): ChatDebugInfo[];
36
+ /**
37
+ * Format debug info for display
38
+ */
39
+ export declare function formatDebugInfo(acts: ConversationAct[]): string;
40
+ //# sourceMappingURL=chat.d.ts.map