Update DESIGN.md, TECHNICAL.md, and TODO.md for Enhanced Delivery Preferences
- Revised DESIGN.md to incorporate support for multiple delivery targets across email, webhook, and process channels, enhancing the flexibility of the delivery system. - Expanded TECHNICAL.md with detailed descriptions of the new DeliveryPreferences structure, including EmailPreference, WebhookPreference, and ProcessPreference, to clarify their configurations and usage. - Updated TODO.md to reflect the integration of multiple targets in delivery preferences, ensuring comprehensive tracking of the ongoing enhancements in the delivery architecture. - Enhanced documentation to outline the new delivery channels and their configurations, improving clarity and usability for developers and users.
This commit is contained in:
parent
9e14d8b7af
commit
b64f4c8930
3 changed files with 264 additions and 152 deletions
|
|
@ -262,7 +262,7 @@ Human/Event: P1 → P2 → P3 → P4 → P5
|
|||
| P1 | Goal Gen | Report + history | Goals | Always |
|
||||
| P2 | Task Plan | Goals + tools | Tasks | Always |
|
||||
| P3 | Run + Valid | Tasks + Experts | TaskResults | Always |
|
||||
| P4 | Delivery | All results | Email/Webhook | Always |
|
||||
| P4 | Delivery | All results | Email/Webhook/Process | Always |
|
||||
| P5 | Learning | Summary | KB entries | Always |
|
||||
|
||||
### 4.2 P0: Inspiration (Clock only)
|
||||
|
|
@ -514,7 +514,7 @@ P4 generates delivery content and pushes to Delivery Center. **Agent only genera
|
|||
│ Role: │
|
||||
│ 1. Read Robot/User delivery preferences │
|
||||
│ 2. Decide which channels to use │
|
||||
│ 3. Execute delivery (email, webhook) │
|
||||
│ 3. Execute delivery (email, webhook, process) │
|
||||
│ 4. Future: auto-notify based on user subscriptions │
|
||||
│ │
|
||||
│ (Current: internal, future: yao/delivery) │
|
||||
|
|
@ -571,11 +571,12 @@ Attachments use the standard `yao/attachment` wrapper format:
|
|||
|
||||
**Delivery Channels (Delivery Center decides):**
|
||||
|
||||
| Channel | Description | When |
|
||||
|---------|-------------|------|
|
||||
| `email` | Send via yao/messenger | If configured in preferences |
|
||||
| `webhook` | POST to URL (Slack, 飞书, etc.) | If configured, every execution |
|
||||
| `notify` | In-app push notification | Based on user subscriptions (future) |
|
||||
| Channel | Description | Multiple Targets |
|
||||
|---------|-------------|------------------|
|
||||
| `email` | Send via yao/messenger | ✅ Multiple recipients/emails |
|
||||
| `webhook` | POST to external URL | ✅ Multiple URLs |
|
||||
| `process` | Yao Process call | ✅ Multiple processes |
|
||||
| `notify` | In-app notification | Future (auto by subscriptions) |
|
||||
|
||||
**Delivery Agent:**
|
||||
|
||||
|
|
@ -625,12 +626,13 @@ type DeliveryResult struct {
|
|||
Error string `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
// ChannelResult - result for a single channel
|
||||
// ChannelResult - result for a single delivery target
|
||||
type ChannelResult struct {
|
||||
Type DeliveryType `json:"type"`
|
||||
Type DeliveryType `json:"type"` // email | webhook | process | notify
|
||||
Target string `json:"target,omitempty"` // Target identifier
|
||||
Success bool `json:"success"`
|
||||
Recipients []string `json:"recipients,omitempty"` // For email
|
||||
Details interface{} `json:"details,omitempty"`
|
||||
Details interface{} `json:"details,omitempty"` // Channel-specific response
|
||||
Error string `json:"error,omitempty"`
|
||||
SentAt *time.Time `json:"sent_at,omitempty"`
|
||||
}
|
||||
|
|
@ -638,22 +640,45 @@ type ChannelResult struct {
|
|||
|
||||
**Config (Delivery Preferences):**
|
||||
|
||||
Robot config defines delivery **preferences** (Delivery Center reads and executes):
|
||||
Robot config defines delivery **preferences** (Delivery Center reads and executes).
|
||||
Each channel supports **multiple targets**:
|
||||
|
||||
```yaml
|
||||
delivery:
|
||||
preferences:
|
||||
email:
|
||||
enabled: true
|
||||
to: ["manager@company.com"]
|
||||
cc: ["team@company.com"]
|
||||
targets: # Multiple email targets
|
||||
- to: ["manager@company.com"]
|
||||
cc: ["team@company.com"]
|
||||
- to: ["ceo@company.com"]
|
||||
subject_template: "Executive Summary"
|
||||
|
||||
webhook:
|
||||
enabled: true
|
||||
url: "https://slack.com/webhook/reports"
|
||||
# Every execution pushes to webhook automatically
|
||||
targets: # Multiple webhook URLs
|
||||
- url: "https://slack.com/webhook/sales"
|
||||
- url: "https://feishu.cn/webhook/reports"
|
||||
headers: {"X-Custom": "value"}
|
||||
|
||||
process:
|
||||
enabled: true
|
||||
targets: # Multiple Yao Process calls
|
||||
- name: "orders.UpdateStatus"
|
||||
args: ["completed"]
|
||||
- name: "audit.LogDelivery"
|
||||
|
||||
# Note: notify handled by Delivery Center based on user subscriptions (future)
|
||||
```
|
||||
|
||||
**Use Cases:**
|
||||
|
||||
| Scenario | Channels | Description |
|
||||
|----------|----------|-------------|
|
||||
| Event callback | `process` | DB change → Robot → Update data via Process |
|
||||
| Multi-channel notify | `email` + `webhook` | Send to multiple emails and Slack/飞书 |
|
||||
| Data pipeline | `process` | Robot result → Save to DB → Update dashboard |
|
||||
|
||||
### 4.7 P5: Learn
|
||||
|
||||
Save to KB:
|
||||
|
|
@ -680,7 +705,7 @@ type Config struct {
|
|||
DB *DB `json:"db,omitempty"` // shared DB (same as assistant)
|
||||
Learn *Learn `json:"learn,omitempty"` // learning for private KB
|
||||
Resources *Resources `json:"resources"`
|
||||
Delivery *Delivery `json:"delivery"`
|
||||
Delivery *DeliveryPreferences `json:"delivery,omitempty"`
|
||||
Events []Event `json:"events,omitempty"`
|
||||
Executor *Executor `json:"executor,omitempty"` // executor mode settings
|
||||
}
|
||||
|
|
@ -721,7 +746,8 @@ type DeliveryType string
|
|||
|
||||
const (
|
||||
DeliveryEmail DeliveryType = "email" // Email via yao/messenger
|
||||
DeliveryWebhook DeliveryType = "webhook" // POST to URL
|
||||
DeliveryWebhook DeliveryType = "webhook" // POST to external URL
|
||||
DeliveryProcess DeliveryType = "process" // Yao Process call
|
||||
DeliveryNotify DeliveryType = "notify" // In-app notification (future)
|
||||
)
|
||||
|
||||
|
|
@ -815,10 +841,44 @@ type MCP struct {
|
|||
Tools []string `json:"tools,omitempty"` // empty = all
|
||||
}
|
||||
|
||||
// Delivery
|
||||
type Delivery struct {
|
||||
Type DeliveryType `json:"type"`
|
||||
Opts map[string]interface{} `json:"opts"`
|
||||
// DeliveryPreferences - Robot delivery preferences (read by Delivery Center)
|
||||
// Each channel supports multiple targets
|
||||
type DeliveryPreferences struct {
|
||||
Email *EmailPreference `json:"email,omitempty"`
|
||||
Webhook *WebhookPreference `json:"webhook,omitempty"`
|
||||
Process *ProcessPreference `json:"process,omitempty"`
|
||||
// notify is handled automatically based on user subscriptions
|
||||
}
|
||||
|
||||
type EmailPreference struct {
|
||||
Enabled bool `json:"enabled"`
|
||||
Targets []EmailTarget `json:"targets"`
|
||||
}
|
||||
|
||||
type EmailTarget struct {
|
||||
To []string `json:"to"`
|
||||
CC []string `json:"cc,omitempty"`
|
||||
SubjectTemplate string `json:"subject_template,omitempty"`
|
||||
}
|
||||
|
||||
type WebhookPreference struct {
|
||||
Enabled bool `json:"enabled"`
|
||||
Targets []WebhookTarget `json:"targets"`
|
||||
}
|
||||
|
||||
type WebhookTarget struct {
|
||||
URL string `json:"url"`
|
||||
Headers map[string]string `json:"headers,omitempty"`
|
||||
}
|
||||
|
||||
type ProcessPreference struct {
|
||||
Enabled bool `json:"enabled"`
|
||||
Targets []ProcessTarget `json:"targets"`
|
||||
}
|
||||
|
||||
type ProcessTarget struct {
|
||||
Name string `json:"name"` // Process name, e.g., "orders.UpdateStatus"
|
||||
Args []any `json:"args,omitempty"`
|
||||
}
|
||||
|
||||
// ExecutorMode - executor mode enum
|
||||
|
|
|
|||
|
|
@ -855,7 +855,8 @@ type DeliveryType string
|
|||
|
||||
const (
|
||||
DeliveryEmail DeliveryType = "email" // Email via yao/messenger
|
||||
DeliveryWebhook DeliveryType = "webhook" // POST to URL
|
||||
DeliveryWebhook DeliveryType = "webhook" // POST to external URL
|
||||
DeliveryProcess DeliveryType = "process" // Yao Process call
|
||||
DeliveryNotify DeliveryType = "notify" // In-app notification (future)
|
||||
)
|
||||
|
||||
|
|
@ -953,7 +954,7 @@ type Config struct {
|
|||
DB *DB `json:"db,omitempty"` // shared database (same as assistant)
|
||||
Learn *Learn `json:"learn,omitempty"` // learning config for private KB
|
||||
Resources *Resources `json:"resources,omitempty"`
|
||||
Delivery *Delivery `json:"delivery,omitempty"`
|
||||
Delivery *DeliveryPreferences `json:"delivery,omitempty"` // see section 6.2
|
||||
Events []Event `json:"events,omitempty"`
|
||||
}
|
||||
|
||||
|
|
@ -1133,11 +1134,7 @@ type MCPConfig struct {
|
|||
Tools []string `json:"tools,omitempty"` // empty = all
|
||||
}
|
||||
|
||||
// Delivery - output delivery
|
||||
type Delivery struct {
|
||||
Type DeliveryType `json:"type"`
|
||||
Opts map[string]interface{} `json:"opts,omitempty"`
|
||||
}
|
||||
// Note: Delivery preferences moved to DeliveryPreferences (see section 6.2)
|
||||
|
||||
// Event - event trigger config
|
||||
type Event struct {
|
||||
|
|
@ -1435,22 +1432,46 @@ type DeliveryContext struct {
|
|||
}
|
||||
|
||||
// DeliveryPreferences - Robot/User delivery preferences (read by Delivery Center)
|
||||
// Each channel supports multiple targets
|
||||
type DeliveryPreferences struct {
|
||||
Email *EmailPreference `json:"email,omitempty"`
|
||||
Webhook *WebhookPreference `json:"webhook,omitempty"`
|
||||
Process *ProcessPreference `json:"process,omitempty"`
|
||||
// notify is handled automatically based on user subscriptions
|
||||
}
|
||||
|
||||
// EmailPreference - multiple email targets
|
||||
type EmailPreference struct {
|
||||
Enabled bool `json:"enabled"`
|
||||
To []string `json:"to"`
|
||||
CC []string `json:"cc,omitempty"`
|
||||
Enabled bool `json:"enabled"`
|
||||
Targets []EmailTarget `json:"targets"`
|
||||
}
|
||||
|
||||
type EmailTarget struct {
|
||||
To []string `json:"to"`
|
||||
CC []string `json:"cc,omitempty"`
|
||||
SubjectTemplate string `json:"subject_template,omitempty"` // Optional, default: content.Summary
|
||||
}
|
||||
|
||||
// WebhookPreference - multiple webhook targets
|
||||
type WebhookPreference struct {
|
||||
Enabled bool `json:"enabled"`
|
||||
URL string `json:"url"`
|
||||
// If enabled, every execution pushes automatically
|
||||
Enabled bool `json:"enabled"`
|
||||
Targets []WebhookTarget `json:"targets"`
|
||||
}
|
||||
|
||||
type WebhookTarget struct {
|
||||
URL string `json:"url"`
|
||||
Headers map[string]string `json:"headers,omitempty"`
|
||||
}
|
||||
|
||||
// ProcessPreference - multiple Yao Process targets
|
||||
type ProcessPreference struct {
|
||||
Enabled bool `json:"enabled"`
|
||||
Targets []ProcessTarget `json:"targets"`
|
||||
}
|
||||
|
||||
type ProcessTarget struct {
|
||||
Name string `json:"name"` // Process name, e.g., "orders.UpdateStatus"
|
||||
Args []any `json:"args,omitempty"` // Additional args (DeliveryContent passed as first arg)
|
||||
}
|
||||
|
||||
// DeliveryResult - P4 delivery output (returned by Delivery Center)
|
||||
|
|
@ -1462,9 +1483,10 @@ type DeliveryResult struct {
|
|||
Error string `json:"error,omitempty"` // Overall error if any
|
||||
}
|
||||
|
||||
// ChannelResult - result for a single delivery channel
|
||||
// ChannelResult - result for a single delivery target
|
||||
type ChannelResult struct {
|
||||
Type DeliveryType `json:"type"` // email | webhook | notify
|
||||
Type DeliveryType `json:"type"` // email | webhook | process | notify
|
||||
Target string `json:"target,omitempty"` // Target identifier (email, URL, process name)
|
||||
Success bool `json:"success"`
|
||||
Recipients []string `json:"recipients,omitempty"` // Who received (for email)
|
||||
Details interface{} `json:"details,omitempty"` // Channel-specific response
|
||||
|
|
@ -2040,25 +2062,29 @@ type DeliveryContext struct {
|
|||
|
||||
**Channel Decision by Delivery Center:**
|
||||
|
||||
Delivery Center reads Robot/User preferences and decides channels:
|
||||
Delivery Center reads Robot/User preferences and executes delivery to all enabled targets:
|
||||
|
||||
```go
|
||||
// DeliveryPreferences - from Robot config
|
||||
// DeliveryPreferences - from Robot config (each channel supports multiple targets)
|
||||
type DeliveryPreferences struct {
|
||||
Email *EmailPreference `json:"email,omitempty"`
|
||||
Webhook *WebhookPreference `json:"webhook,omitempty"`
|
||||
Process *ProcessPreference `json:"process,omitempty"`
|
||||
}
|
||||
|
||||
type EmailPreference struct {
|
||||
Enabled bool `json:"enabled"`
|
||||
To []string `json:"to"`
|
||||
CC []string `json:"cc,omitempty"`
|
||||
Enabled bool `json:"enabled"`
|
||||
Targets []EmailTarget `json:"targets"` // Multiple email targets
|
||||
}
|
||||
|
||||
type WebhookPreference struct {
|
||||
Enabled bool `json:"enabled"`
|
||||
URL string `json:"url"`
|
||||
// If enabled, every execution pushes automatically
|
||||
Enabled bool `json:"enabled"`
|
||||
Targets []WebhookTarget `json:"targets"` // Multiple webhook URLs
|
||||
}
|
||||
|
||||
type ProcessPreference struct {
|
||||
Enabled bool `json:"enabled"`
|
||||
Targets []ProcessTarget `json:"targets"` // Multiple Yao Process calls
|
||||
}
|
||||
```
|
||||
|
||||
|
|
@ -2139,57 +2165,59 @@ The agent focuses on content generation:
|
|||
|
||||
### 6.5 Delivery Center
|
||||
|
||||
The Delivery Center receives `DeliveryRequest`, **decides channels based on preferences**, and executes delivery.
|
||||
The Delivery Center receives `DeliveryRequest`, reads preferences, and executes delivery to **all enabled targets**.
|
||||
|
||||
**Current implementation:** Internal to P4 (in `executor/delivery.go`)
|
||||
**Future:** Can be extracted to standalone `yao/delivery` package
|
||||
|
||||
```go
|
||||
// DeliveryCenter - handles channel decision and delivery execution
|
||||
// DeliveryCenter - handles delivery execution to multiple targets
|
||||
type DeliveryCenter struct {
|
||||
handlers map[DeliveryType]ChannelHandler
|
||||
}
|
||||
|
||||
// ChannelHandler - interface for channel implementations
|
||||
type ChannelHandler interface {
|
||||
Deliver(ctx context.Context, content *DeliveryContent, opts map[string]interface{}) (*ChannelResult, error)
|
||||
messenger *messenger.Manager
|
||||
}
|
||||
|
||||
// Deliver - main entry point
|
||||
func (dc *DeliveryCenter) Deliver(ctx context.Context, req *DeliveryRequest) *DeliveryResult {
|
||||
requestID := generateID()
|
||||
|
||||
// 1. Get Robot/User delivery preferences
|
||||
prefs := dc.getDeliveryPreferences(ctx, req.Context.RobotID)
|
||||
|
||||
// 2. Decide channels based on preferences
|
||||
channels := dc.decideChannels(prefs)
|
||||
|
||||
// 3. Execute delivery to each channel
|
||||
var results []ChannelResult
|
||||
allSuccess := true
|
||||
|
||||
for _, ch := range channels {
|
||||
handler, ok := dc.handlers[ch.Type]
|
||||
if !ok {
|
||||
results = append(results, ChannelResult{
|
||||
Type: ch.Type,
|
||||
Success: false,
|
||||
Error: fmt.Sprintf("unsupported channel: %s", ch.Type),
|
||||
})
|
||||
allSuccess = false
|
||||
continue
|
||||
// Email - send to all targets
|
||||
if prefs.Email != nil && prefs.Email.Enabled {
|
||||
for _, target := range prefs.Email.Targets {
|
||||
result := dc.sendEmail(ctx, req.Content, target)
|
||||
results = append(results, result)
|
||||
if !result.Success {
|
||||
allSuccess = false
|
||||
}
|
||||
}
|
||||
|
||||
result, err := handler.Deliver(ctx, req.Content, ch.Options)
|
||||
if err != nil {
|
||||
result = &ChannelResult{Type: ch.Type, Success: false, Error: err.Error()}
|
||||
allSuccess = false
|
||||
}
|
||||
results = append(results, *result)
|
||||
}
|
||||
|
||||
// 4. Future: check user subscriptions and send notifications
|
||||
// Webhook - POST to all targets
|
||||
if prefs.Webhook != nil && prefs.Webhook.Enabled {
|
||||
for _, target := range prefs.Webhook.Targets {
|
||||
result := dc.postWebhook(ctx, req.Content, target)
|
||||
results = append(results, result)
|
||||
if !result.Success {
|
||||
allSuccess = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Process - call all targets
|
||||
if prefs.Process != nil && prefs.Process.Enabled {
|
||||
for _, target := range prefs.Process.Targets {
|
||||
result := dc.callProcess(ctx, req.Content, target)
|
||||
results = append(results, result)
|
||||
if !result.Success {
|
||||
allSuccess = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Future: auto-notify based on user subscriptions
|
||||
// dc.sendNotifications(ctx, req)
|
||||
|
||||
return &DeliveryResult{
|
||||
|
|
@ -2199,40 +2227,15 @@ func (dc *DeliveryCenter) Deliver(ctx context.Context, req *DeliveryRequest) *De
|
|||
Results: results,
|
||||
}
|
||||
}
|
||||
|
||||
// decideChannels - decide which channels to use based on preferences
|
||||
func (dc *DeliveryCenter) decideChannels(prefs *DeliveryPreferences) []channelWithOpts {
|
||||
var channels []channelWithOpts
|
||||
|
||||
if prefs.Email != nil && prefs.Email.Enabled {
|
||||
channels = append(channels, channelWithOpts{
|
||||
Type: DeliveryEmail,
|
||||
Options: map[string]interface{}{"to": prefs.Email.To, "cc": prefs.Email.CC},
|
||||
})
|
||||
}
|
||||
|
||||
if prefs.Webhook != nil && prefs.Webhook.Enabled {
|
||||
channels = append(channels, channelWithOpts{
|
||||
Type: DeliveryWebhook,
|
||||
Options: map[string]interface{}{"url": prefs.Webhook.URL},
|
||||
})
|
||||
}
|
||||
|
||||
return channels
|
||||
}
|
||||
```
|
||||
|
||||
### 6.6 Channel Handlers
|
||||
|
||||
Each delivery channel has a dedicated handler implementing `ChannelHandler`:
|
||||
Each delivery channel is handled by dedicated methods in DeliveryCenter:
|
||||
|
||||
```go
|
||||
// EmailHandler - uses yao/messenger
|
||||
type EmailHandler struct {
|
||||
messenger *messenger.Manager
|
||||
}
|
||||
|
||||
func (h *EmailHandler) Deliver(ctx context.Context, content *DeliveryContent, opts map[string]interface{}) (*ChannelResult, error) {
|
||||
// sendEmail - send to a single email target
|
||||
func (dc *DeliveryCenter) sendEmail(ctx context.Context, content *DeliveryContent, target EmailTarget) ChannelResult {
|
||||
// Convert attachments to messenger format
|
||||
var attachments []messenger.Attachment
|
||||
for _, att := range content.Attachments {
|
||||
|
|
@ -2248,50 +2251,87 @@ func (h *EmailHandler) Deliver(ctx context.Context, content *DeliveryContent, op
|
|||
})
|
||||
}
|
||||
|
||||
to := opts["to"].([]string)
|
||||
err := h.messenger.Send(ctx, &messenger.Message{
|
||||
To: to,
|
||||
Subject: content.Summary, // Use summary as subject
|
||||
subject := content.Summary
|
||||
if target.SubjectTemplate != "" {
|
||||
subject = target.SubjectTemplate
|
||||
}
|
||||
|
||||
err := dc.messenger.Send(ctx, &messenger.Message{
|
||||
To: target.To,
|
||||
CC: target.CC,
|
||||
Subject: subject,
|
||||
Body: content.Body,
|
||||
Attachments: attachments,
|
||||
})
|
||||
|
||||
now := time.Now()
|
||||
return &ChannelResult{
|
||||
return ChannelResult{
|
||||
Type: DeliveryEmail,
|
||||
Target: strings.Join(target.To, ","),
|
||||
Success: err == nil,
|
||||
Recipients: to,
|
||||
Recipients: target.To,
|
||||
SentAt: &now,
|
||||
}, err
|
||||
Error: errStr(err),
|
||||
}
|
||||
}
|
||||
|
||||
// WebhookHandler - POST JSON to URL
|
||||
type WebhookHandler struct{}
|
||||
|
||||
func (h *WebhookHandler) Deliver(ctx context.Context, content *DeliveryContent, opts map[string]interface{}) (*ChannelResult, error) {
|
||||
// postWebhook - POST to a single webhook target
|
||||
func (dc *DeliveryCenter) postWebhook(ctx context.Context, content *DeliveryContent, target WebhookTarget) ChannelResult {
|
||||
payload, _ := json.Marshal(content)
|
||||
req, _ := http.NewRequestWithContext(ctx, "POST", opts["url"].(string), bytes.NewReader(payload))
|
||||
req, _ := http.NewRequestWithContext(ctx, "POST", target.URL, bytes.NewReader(payload))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
// Add custom headers
|
||||
for k, v := range target.Headers {
|
||||
req.Header.Set(k, v)
|
||||
}
|
||||
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
now := time.Now()
|
||||
|
||||
if err != nil {
|
||||
return &ChannelResult{Type: DeliveryWebhook, Success: false}, err
|
||||
return ChannelResult{
|
||||
Type: DeliveryWebhook,
|
||||
Target: target.URL,
|
||||
Success: false,
|
||||
Error: err.Error(),
|
||||
SentAt: &now,
|
||||
}
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode >= 400 {
|
||||
return &ChannelResult{Type: DeliveryWebhook, Success: false}, fmt.Errorf("webhook failed: %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
return &ChannelResult{
|
||||
success := resp.StatusCode < 400
|
||||
return ChannelResult{
|
||||
Type: DeliveryWebhook,
|
||||
Success: true,
|
||||
Target: target.URL,
|
||||
Success: success,
|
||||
Details: map[string]interface{}{"status_code": resp.StatusCode},
|
||||
Error: ternary(!success, fmt.Sprintf("HTTP %d", resp.StatusCode), ""),
|
||||
SentAt: &now,
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
// callProcess - call a single Yao Process target
|
||||
func (dc *DeliveryCenter) callProcess(ctx context.Context, content *DeliveryContent, target ProcessTarget) ChannelResult {
|
||||
// DeliveryContent as first arg, then additional args
|
||||
args := append([]interface{}{content}, target.Args...)
|
||||
|
||||
proc := process.Of(target.Name, args...)
|
||||
result, err := proc.Execute()
|
||||
|
||||
now := time.Now()
|
||||
return ChannelResult{
|
||||
Type: DeliveryProcess,
|
||||
Target: target.Name,
|
||||
Success: err == nil,
|
||||
Details: map[string]interface{}{
|
||||
"process": target.Name,
|
||||
"result": result,
|
||||
},
|
||||
Error: errStr(err),
|
||||
SentAt: &now,
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Note on Notifications:**
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@
|
|||
- [x] `RobotStatus` - robot status (idle, working, paused, error, maintenance)
|
||||
- [x] `InterventionAction` - human actions (task.add, goal.adjust, etc.)
|
||||
- [x] `Priority` - priority levels (high, normal, low)
|
||||
- [x] `DeliveryType` - delivery types (email, webhook, notify)
|
||||
- [x] `DeliveryType` - delivery types (email, webhook, process, notify)
|
||||
- [x] `DedupResult` - dedup results (skip, merge, proceed)
|
||||
- [x] `EventSource` - event sources (webhook, database)
|
||||
- [x] `LearningType` - learning types (execution, feedback, insight)
|
||||
|
|
@ -916,16 +916,21 @@ Supported channels:
|
|||
|
||||
### 10.3 Type Updates (Prerequisite)
|
||||
|
||||
- [ ] Update `types/enums.go` - Remove `DeliveryFile` from `DeliveryType`
|
||||
- [ ] Update `types/enums.go` - Update `DeliveryType` enum
|
||||
- [ ] Remove `DeliveryFile`
|
||||
- [ ] Add `DeliveryProcess`
|
||||
- [ ] Update `types/robot.go` - Delivery types for new architecture
|
||||
- [ ] `DeliveryResult` - update to new structure (RequestID, Content, Results[])
|
||||
- [ ] Add `DeliveryContent` struct
|
||||
- [ ] Add `DeliveryAttachment` struct
|
||||
- [ ] Add `DeliveryRequest` struct
|
||||
- [ ] Add `DeliveryContext` struct
|
||||
- [ ] Add `DeliveryPreferences` struct
|
||||
- [ ] Add `ChannelResult` struct
|
||||
- [ ] Update `types/enums_test.go` - Remove `DeliveryFile` test
|
||||
- [ ] Add `DeliveryPreferences` struct (with Email, Webhook, Process)
|
||||
- [ ] Add `EmailPreference`, `EmailTarget` structs
|
||||
- [ ] Add `WebhookPreference`, `WebhookTarget` structs
|
||||
- [ ] Add `ProcessPreference`, `ProcessTarget` structs
|
||||
- [ ] Add `ChannelResult` struct (with Target field)
|
||||
- [ ] Update `types/enums_test.go` - Update DeliveryType tests
|
||||
- [ ] Update `types/robot_test.go` - Update delivery result tests
|
||||
|
||||
### 10.4 Delivery Agent Setup
|
||||
|
|
@ -980,12 +985,13 @@ type DeliveryContext struct {
|
|||
- Parse: `attachment.Parse(value)` → `(uploader, fileID, isWrapper)`
|
||||
- Read: `attachment.Base64(ctx, value)` → base64 content
|
||||
|
||||
**Delivery Channels (Delivery Center decides):**
|
||||
| Channel | Description |
|
||||
|---------|-------------|
|
||||
| `email` | Send via yao/messenger (if configured in preferences) |
|
||||
| `webhook` | POST JSON to URL (if configured, every execution) |
|
||||
| `notify` | In-app notification based on user subscriptions (future) |
|
||||
**Delivery Channels (each supports multiple targets):**
|
||||
| Channel | Description | Multiple Targets |
|
||||
|---------|-------------|------------------|
|
||||
| `email` | Send via yao/messenger | ✅ Multiple recipients |
|
||||
| `webhook` | POST to external URL | ✅ Multiple URLs |
|
||||
| `process` | Yao Process call | ✅ Multiple processes |
|
||||
| `notify` | In-app notification | Future (auto by subscriptions) |
|
||||
|
||||
### 10.6 Implementation
|
||||
|
||||
|
|
@ -999,30 +1005,36 @@ type DeliveryContext struct {
|
|||
**Delivery Center (executor/delivery.go, future: yao/delivery):**
|
||||
- [ ] `DeliveryCenter.Deliver(ctx, request)` - main entry
|
||||
- [ ] Read Robot/User delivery preferences
|
||||
- [ ] Decide which channels to use based on preferences
|
||||
- [ ] Call appropriate handler for each channel
|
||||
- [ ] Iterate through all enabled targets for each channel
|
||||
- [ ] Aggregate ChannelResults into DeliveryResult
|
||||
- [ ] `ChannelHandler` interface
|
||||
- [ ] `Deliver(ctx, content, opts) (*ChannelResult, error)`
|
||||
|
||||
**Channel Handlers:**
|
||||
- [ ] `EmailHandler` - uses yao/messenger
|
||||
**Channel Handlers (each supports multiple targets):**
|
||||
- [ ] `sendEmail()` - uses yao/messenger
|
||||
- [ ] Convert DeliveryAttachment to messenger.Attachment
|
||||
- [ ] Use Summary as email subject
|
||||
- [ ] Support to, cc from preferences
|
||||
- [ ] `WebhookHandler` - POST JSON
|
||||
- [ ] Support multiple EmailTarget
|
||||
- [ ] Support custom subject_template per target
|
||||
- [ ] `postWebhook()` - POST JSON
|
||||
- [ ] POST DeliveryContent as JSON payload
|
||||
- [ ] If enabled, every execution pushes automatically
|
||||
- [ ] Support multiple WebhookTarget
|
||||
- [ ] Support custom headers per target
|
||||
- [ ] `callProcess()` - Yao Process call
|
||||
- [ ] DeliveryContent as first arg
|
||||
- [ ] Support multiple ProcessTarget
|
||||
- [ ] Support additional args per target
|
||||
|
||||
### 10.7 Tests
|
||||
|
||||
- [ ] `executor/delivery_test.go` - P4 delivery
|
||||
- [ ] Test: Delivery Agent generates content (only content)
|
||||
- [ ] Test: DeliveryCenter reads preferences and decides channels
|
||||
- [ ] Test: DeliveryCenter dispatches to multiple channels
|
||||
- [ ] Test: EmailHandler with attachments
|
||||
- [ ] Test: WebhookHandler POST JSON
|
||||
- [ ] Test: Partial success (some channels fail)
|
||||
- [ ] Test: DeliveryCenter reads preferences
|
||||
- [ ] Test: Multiple email targets
|
||||
- [ ] Test: Multiple webhook targets
|
||||
- [ ] Test: Multiple process targets
|
||||
- [ ] Test: Mixed channels (email + webhook + process)
|
||||
- [ ] Test: sendEmail with attachments
|
||||
- [ ] Test: postWebhook with custom headers
|
||||
- [ ] Test: callProcess with args
|
||||
- [ ] Test: Partial success (some targets fail)
|
||||
- [ ] Test: DeliveryResult aggregation
|
||||
|
||||
---
|
||||
|
|
@ -1239,7 +1251,7 @@ func TestWithLLM(t *testing.T) {
|
|||
| 7. P1 Goals | ✅ | Goal Generation Agent integration |
|
||||
| 8. P2 Tasks | ✅ | Task Planning Agent integration |
|
||||
| 9. P3 Run | ✅ | Task execution + validation + yao/assert + multi-turn conversation |
|
||||
| 10. P4 Delivery | ⬜ | Output delivery (email/webhook, notify future) |
|
||||
| 10. P4 Delivery | ⬜ | Output delivery (email/webhook/process, notify future) |
|
||||
| 11. API & Integration | ⬜ | Complete API, end-to-end tests (main flow: P0→P1→P2→P3→P4) |
|
||||
| 12. Advanced | ⬜ | P5 Learning, dedup, plan queue, Sandbox mode |
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue