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/CHANGELOG.md +337 -347
- package/README.md +111 -0
- package/dist/api.d.ts +7 -1
- package/dist/api.js +36 -0
- package/dist/auth.js +4 -0
- package/dist/cli/commands/help.js +10 -0
- package/dist/cli/commands/sandbox.d.ts +14 -0
- package/dist/cli/commands/sandbox.js +306 -0
- package/dist/cli.js +9 -0
- package/dist/sandbox/chat.d.ts +40 -0
- package/dist/sandbox/chat.js +280 -0
- package/dist/types.d.ts +81 -0
- package/package.json +6 -3
- package/src/api.ts +55 -1
- package/src/auth.ts +7 -2
- package/src/cli/commands/help.ts +10 -0
- package/src/cli/commands/sandbox.ts +365 -0
- package/src/cli.ts +11 -0
- package/src/sandbox/chat.ts +339 -0
- package/src/types.ts +98 -0
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
|