@candoa/workflows
Version:
Type-safe workflow SDK for Candoa with Copilot-friendly syntax. Define chat workflows with triggers, actions, and type safety.
530 lines (417 loc) ⢠13.6 kB
Markdown
# @candoa/workflows
Self-contained, type-safe workflow SDK for Candoa with Copilot-friendly syntax. Define chat workflows with triggers, actions, and full TypeScript support - no external dependencies required.
## š Features
- **Copilot-friendly syntax** - Optimized for GitHub Copilot autocompletion
- **Type-safe** - Full TypeScript support with parameter validation
- **Builder pattern** - Fluent API with method chaining
- **Template variables** - Dynamic value substitution with `{{variableName}}`
- **Pre-built actions** - Common workflow actions ready to use
- **Self-contained** - No external dependencies, all types included
- **Database integration** - Seamless integration with Candoa CRM
- **Sync command** - Deploy workflows to database with `pnpm run sync-workflows`
## š¦ Installation
```bash
npm install @candoa/workflows
# or
pnpm add @candoa/workflows
```
## š„ Quick Start
```typescript
import { workflow, collectFirstName, saveToContact } from '@candoa/workflows'
export default workflow('welcome-new-user', {
description: "Collects the user's first name when they start a chat",
})
.on('chat.started')
.use(
collectFirstName({ prompt: "Hi! What's your first name?" }),
saveToContact({ field: 'firstName', value: '{{firstName}}' }),
)
```
## š§ Core Concepts
### Workflow Definition
A workflow consists of:
1. **Name** - Unique identifier for the workflow
2. **Description** - Optional documentation
3. **Trigger** - Event that starts the workflow
4. **Actions** - Sequence of operations to execute
### Template Variables
Use template variables to pass data between actions:
```typescript
workflow('collect-contact-info')
.on('chat.started')
.use(
collectFirstName({ prompt: "What's your first name?" }),
collectEmail({ prompt: "What's your email address?" }),
saveToContact({ field: 'firstName', value: '{{firstName}}' }),
saveToContact({ field: 'email', value: '{{email}}' }),
sendMessage({
message: "Thanks {{firstName}}! I've saved your information.",
}),
)
```
## šÆ Triggers
Available trigger types with full TypeScript autocompletion:
- `chat.started` - When a new chat session begins
- `chat.ended` - When a chat session ends
- `message.received` - When user sends a message
- `intent.detected` - When AI detects specific intent
- `sentiment.negative` - When negative sentiment is detected
- `payment.failed` - When a payment fails
- `user.signup` - When a user signs up
- `custom.event` - For custom events
## ā” Actions
### Data Collection
```typescript
// Collect user information
collectFirstName({ prompt: "What's your first name?" })
collectLastName({ prompt: "What's your last name?" })
collectEmail({ prompt: "What's your email address?" })
// Save to contact record
saveToContact({ field: 'firstName', value: '{{firstName}}' })
saveToContact({ field: 'email', value: '{{email}}' })
```
### Communication
```typescript
// Send messages
sendMessage({ message: 'Hello {{firstName}}!' })
sendMessage({ message: 'Please wait...', delay: 2000 })
// Send emails
sendEmail({
to: '{{email}}',
subject: 'Welcome to our platform!',
body: 'Hi {{firstName}}, welcome aboard!',
})
```
### Workflow Control
```typescript
// Wait for a period
wait({ duration: 5, unit: 'seconds' })
// Conditional logic
conditional({
condition: "{{sentiment}} == 'negative'",
trueAction: handoffToHuman({ reason: 'Negative sentiment detected' }),
falseAction: sendMessage({ message: 'How else can I help you?' }),
})
// Hand off to human agent
handoffToHuman({
reason: 'Complex issue requiring human assistance',
})
```
### Task Management
```typescript
// Create tasks
createTask({
title: 'Follow up with {{firstName}}',
description: 'User expressed interest in premium features',
})
// Add tags
addTags({
tags: ['new-user', 'interested'],
target: 'contact',
})
```
## šļø Database Integration
### Workflow Storage
Workflows are stored in your `workflows` table:
```sql
CREATE TABLE "workflow" (
"id" text PRIMARY KEY,
"projectId" text NOT NULL REFERENCES "project"("id"),
"name" text NOT NULL,
"workflowData" json NOT NULL, -- SDK data stored here
"createdAt" timestamp DEFAULT now(),
"updatedAt" timestamp DEFAULT now()
);
```
### Manual Database Integration
```typescript
import {
prepareWorkflowForDatabase,
validateWorkflowCompatibility,
} from '@candoa/workflows'
// 1. Define your workflow
const welcomeWorkflow = workflow('welcome-new-user', {
description: "Collects the user's first name when they start a chat",
})
.on('chat.started')
.use(
collectFirstName({ prompt: "Hi! What's your first name?" }),
saveToContact({ field: 'firstName', value: '{{firstName}}' }),
)
// 2. Validate workflow
const validation = validateWorkflowCompatibility(welcomeWorkflow)
if (!validation.isValid) {
console.error('Validation errors:', validation.errors)
return
}
// 3. Prepare for database
const dbData = prepareWorkflowForDatabase(welcomeWorkflow, projectId)
// 4. Insert into database
await db.insert(workflows).values({
id: crypto.randomUUID(),
name: dbData.name,
projectId: dbData.projectId,
workflowData: dbData.workflowData,
})
```
## š Workflow Sync System
### Quick Sync
Create workflows in files and sync them to your database:
```bash
# Sync all workflows to your project
pnpm run sync-workflows --project-id=your-project-uuid
# Preview changes without applying them
pnpm run sync-workflows --project-id=your-project-uuid --dry-run
# Force overwrite existing workflows
pnpm run sync-workflows --project-id=your-project-uuid --force
```
### Directory Structure
The sync script discovers workflows in these locations:
```
packages/workflows/
āāā examples/ # š Example workflows (use as templates)
ā āāā welcome.ts
ā āāā candoa-integration.ts
ā āāā advanced-workflows.ts
āāā scripts/
āāā sync-workflows.ts # š§ Sync script
```
### Workflow File Format
Export workflows from your files:
```typescript
// workflows/my-workflows.ts
import { workflow, sendMessage, collectFirstName } from '@candoa/workflows'
// ā
Default export
export default workflow('welcome', {
description: 'Welcome new users',
})
.on('chat.started')
.use(
sendMessage({ message: 'Welcome!' }),
collectFirstName({ prompt: "What's your name?" }),
)
// ā
Named exports
export const supportFlow = workflow('support-flow', {
description: 'Handle support requests',
})
.on('intent.detected')
.use(sendMessage({ message: 'I can help with that!' }))
```
### Sync Output
```
š Candoa Workflow Sync
========================
š Project ID: 0e8b8d5e-cade-4bf5-9e9f-c9e55a5d5ca0
š Found 1 workflow file:
- /examples/welcome.ts
š Processing: /examples/welcome.ts
š Syncing workflow: new-user-onboarding
ā
Created: new-user-onboarding
š Syncing workflow: frustration-escalation
ā
Created: frustration-escalation
š Sync Summary
===============
ā
Created: 2
š Updated: 0
āļø Skipped: 1
ā Errors: 0
š Successfully synced 2 workflows!
```
## šØ Integration with Candoa Systems
### Contact Management
Works with your existing contact schema:
```typescript
const contactWorkflow = workflow('contact-creation', {
description: 'Create comprehensive contact records',
})
.on('chat.started')
.use(
collectFirstName({ prompt: "What's your first name?" }),
collectEmail({ prompt: "What's your email?" }),
saveToContact({ field: 'firstName', value: '{{firstName}}' }),
saveToContact({ field: 'email', value: '{{email}}' }),
addTags({ tags: ['new-contact'], target: 'contact' }),
)
```
### Automated Handoff Integration
Enhances your existing handoff system:
```typescript
const smartHandoff = workflow('smart-handoff', {
description: 'Intelligent handoff with data collection',
})
.on('sentiment.negative')
.use(
sendMessage({
message: 'Let me connect you with a human agent.',
}),
collectFirstName({ prompt: "What's your name?" }),
saveToContact({ field: 'firstName', value: '{{firstName}}' }),
handoffToHuman({
reason: 'Negative sentiment with contact data',
}),
)
```
### Execution Context
Build context from your database:
```typescript
import { buildWorkflowContext } from '@candoa/workflows'
const context = buildWorkflowContext({
conversationId: conversation.id,
contactData: {
id: contact.id,
firstName: contact.name?.split(' ')[0],
email: contact.email,
phoneNumber: contact.phoneNumber,
},
messageHistory: messages.map((msg) => ({
role: msg.role,
content: msg.content,
createdAt: msg.createdAt,
})),
triggerData: {
sentiment: 'negative',
intent: 'billing_question',
},
})
```
## š Advanced Examples
### Customer Onboarding Flow
```typescript
export const customerOnboarding = workflow('customer-onboarding', {
description: 'Complete customer onboarding process',
})
.on('user.signup')
.use(
sendMessage({
message: "Welcome! Let's get you set up in just a few steps.",
}),
collectFirstName({ prompt: "First, what's your first name?" }),
collectEmail({ prompt: "What's your email address?" }),
saveToContact({ field: 'firstName', value: '{{firstName}}' }),
saveToContact({ field: 'email', value: '{{email}}' }),
sendMessage({ message: 'Perfect! Welcome aboard {{firstName}} š' }),
addTags({ tags: ['onboarded'], target: 'contact' }),
createTask({
title: 'Send welcome materials to {{firstName}}',
assignee: 'onboarding-team',
}),
)
```
### Support Escalation
```typescript
export const supportEscalation = workflow('support-escalation', {
description: 'Escalate negative sentiment to human agents',
})
.on('sentiment.negative')
.use(
sendMessage({
message:
'I sense you might be frustrated. Let me get you connected with a human agent.',
}),
handoffToHuman({
reason: 'Negative sentiment detected',
}),
createTask({
title: 'Urgent: Frustrated customer needs assistance',
}),
addTags({ tags: ['escalated', 'urgent'], target: 'conversation' }),
)
```
### Payment Recovery
```typescript
export const paymentRecovery = workflow('payment-recovery', {
description: 'Handle failed payment with automated retry',
})
.on('payment.failed')
.use(
sendMessage({
message:
'It looks like there was an issue with your payment. Let me help you resolve this.',
}),
wait({ duration: 1, unit: 'hours' }),
sendEmail({
to: '{{email}}',
subject: 'Payment Issue - Action Required',
body: 'Hi {{firstName}}, we had trouble processing your payment. Please update your payment method.',
}),
createTask({
title: 'Follow up on failed payment for {{firstName}}',
description: 'Payment failed, email sent, needs follow-up',
}),
)
```
## š Analytics & Monitoring
### Workflow Performance Tracking
```typescript
import { extractWorkflowMetrics, updateWorkflowStats } from '@candoa/workflows'
// After workflow execution
const metrics = extractWorkflowMetrics(workflowDef, {
success: true,
executionTime: 1250, // milliseconds
actionsCompleted: 5,
})
// Update workflow stats in database
const updatedWorkflowData = updateWorkflowStats(
existingWorkflow.workflowData,
true, // was successful
)
await db
.update(workflows)
.set({ workflowData: updatedWorkflowData })
.where(eq(workflows.id, workflowId))
```
## š ļø Best Practices
### Organization
- Keep production workflows in `workflows/` directory
- Use `examples/` for testing and documentation
- Group related workflows in the same file
- Use descriptive workflow names and descriptions
### Naming Conventions
- Use kebab-case for workflow names: `new-user-onboarding`
- Include purpose in name: `support-ticket-creation`
- Avoid conflicts with existing workflows
### Development Workflow
1. **Develop** workflows locally using the SDK
2. **Test** with `--dry-run` flag first
3. **Sync** to staging environment
4. **Validate** in CRM interface
5. **Deploy** to production
### Version Control
- Commit workflow files to your repository
- Tag releases for workflow deployments
- Document changes in workflow descriptions
- Use branches for workflow development
## š Troubleshooting
### Common Sync Issues
**Project ID required:**
```bash
# ā Missing project ID
pnpm run sync-workflows
# ā
Provide project ID
pnpm run sync-workflows --project-id=your-uuid
```
**Workflow already exists:**
```bash
# ā Workflow exists, won't overwrite
pnpm run sync-workflows --project-id=your-uuid
# ā
Use force flag to overwrite
pnpm run sync-workflows --project-id=your-uuid --force
```
**Validation errors:**
- Check workflow structure matches SDK format
- Ensure all required fields are present
- Verify action parameters are valid
- Review error details in sync output
### Getting Help
- Review workflow examples in `examples/` directory
- Check sync output for detailed error messages
- Use `--dry-run` to preview changes without applying
- Validate workflows before syncing
## šØ IDE Support
This SDK is optimized for modern IDEs and GitHub Copilot:
- **Auto-completion** for trigger types, action parameters, and template variables
- **Type checking** for all parameters and return types
- **IntelliSense** documentation for all functions
- **Error highlighting** for invalid configurations
## š License
MIT License - see LICENSE file for details