Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 13 additions & 11 deletions agent/robot/DESIGN.md
Original file line number Diff line number Diff line change
Expand Up @@ -351,6 +351,7 @@ P2 Agent reads Goals markdown and breaks into executable tasks:
```go
type Task struct {
ID string // unique task ID
Description string // human-readable task description (for UI display)
Messages []context.Message // original input (text, images, files, audio)
GoalRef string // reference to goal (e.g., "Goal 1")
Source TaskSource // auto | human | event
Expand Down Expand Up @@ -698,17 +699,18 @@ Save to KB:

```go
type Config struct {
Triggers *Triggers `json:"triggers,omitempty"`
Clock *Clock `json:"clock,omitempty"`
Identity *Identity `json:"identity"`
Quota *Quota `json:"quota"`
KB *KB `json:"kb,omitempty"` // shared KB (same as assistant)
DB *DB `json:"db,omitempty"` // shared DB (same as assistant)
Learn *Learn `json:"learn,omitempty"` // learning for private KB
Resources *Resources `json:"resources"`
Delivery *DeliveryPreferences `json:"delivery,omitempty"`
Events []Event `json:"events,omitempty"`
Executor *Executor `json:"executor,omitempty"` // executor mode settings
Triggers *Triggers `json:"triggers,omitempty"`
Clock *Clock `json:"clock,omitempty"`
Identity *Identity `json:"identity"`
Quota *Quota `json:"quota"`
KB *KB `json:"kb,omitempty"` // shared KB (same as assistant)
DB *DB `json:"db,omitempty"` // shared DB (same as assistant)
Learn *Learn `json:"learn,omitempty"` // learning for private KB
Resources *Resources `json:"resources"`
Delivery *DeliveryPreferences `json:"delivery,omitempty"`
Events []Event `json:"events,omitempty"`
Executor *Executor `json:"executor,omitempty"` // executor mode settings
DefaultLocale string `json:"default_locale,omitempty"` // default language for clock/event triggers ("en-US", "zh-CN")
}
```

Expand Down
35 changes: 21 additions & 14 deletions agent/robot/TECHNICAL.md
Original file line number Diff line number Diff line change
Expand Up @@ -938,16 +938,17 @@ import "time"

// Config - robot_config in __yao.member
type Config struct {
Triggers *Triggers `json:"triggers,omitempty"`
Clock *Clock `json:"clock,omitempty"`
Identity *Identity `json:"identity"`
Quota *Quota `json:"quota,omitempty"`
KB *KB `json:"kb,omitempty"` // shared knowledge base (same as assistant)
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 *DeliveryPreferences `json:"delivery,omitempty"` // see section 6.2
Events []Event `json:"events,omitempty"`
Triggers *Triggers `json:"triggers,omitempty"`
Clock *Clock `json:"clock,omitempty"`
Identity *Identity `json:"identity"`
Quota *Quota `json:"quota,omitempty"`
KB *KB `json:"kb,omitempty"` // shared knowledge base (same as assistant)
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 *DeliveryPreferences `json:"delivery,omitempty"` // see section 6.2
Events []Event `json:"events,omitempty"`
DefaultLocale string `json:"default_locale,omitempty"` // Default language for clock/event triggers (e.g., "en-US", "zh-CN")
}

// Validate validates the config
Expand Down Expand Up @@ -1238,6 +1239,10 @@ type Execution struct {
Phase Phase `json:"phase"`
Error string `json:"error,omitempty"`

// UI display fields (updated by executor at each phase)
// These provide human-readable status for frontend display
Name string `json:"name,omitempty"` // Execution title (updated when goals complete)
CurrentTaskName string `json:"current_task_name,omitempty"` // Current task description (updated during run phase)

// Trigger input (stored for traceability)
Input *TriggerInput `json:"input,omitempty"` // original trigger input
Expand All @@ -1263,6 +1268,7 @@ type TriggerInput struct {
Action InterventionAction `json:"action,omitempty"` // task.add, goal.adjust, etc.
Messages []context.Message `json:"messages,omitempty"` // user's input (text, images, files)
UserID string `json:"user_id,omitempty"` // who triggered
Locale string `json:"locale,omitempty"` // language for UI display (e.g., "en-US", "zh-CN")

// For event trigger
Source EventSource `json:"source,omitempty"` // webhook | database
Expand Down Expand Up @@ -1308,10 +1314,11 @@ type DeliveryTarget struct {

// Task - planned task (structured, for execution)
type Task struct {
ID string `json:"id"`
Messages []context.Message `json:"messages"` // original input (text, images, files)
GoalRef string `json:"goal_ref,omitempty"` // reference to goal (e.g., "Goal 1")
Source TaskSource `json:"source"` // auto | human | event
ID string `json:"id"`
Description string `json:"description,omitempty"` // human-readable task description (for UI display)
Messages []context.Message `json:"messages"` // original input (text, images, files)
GoalRef string `json:"goal_ref,omitempty"` // reference to goal (e.g., "Goal 1")
Source TaskSource `json:"source"` // auto | human | event

// Executor
ExecutorType ExecutorType `json:"executor_type"`
Expand Down
44 changes: 35 additions & 9 deletions agent/robot/api/robot.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ const memberModel = "__yao.member"
// robotStore is the shared robot store instance
var robotStore = store.NewRobotStore()

// executionStore is the shared execution store instance
var executionStore = store.NewExecutionStore()

// GetRobot returns a robot by member ID
// Returns the robot from cache if available, otherwise loads from database
func GetRobot(ctx *types.Context, memberID string) (*types.Robot, error) {
Expand Down Expand Up @@ -62,9 +65,11 @@ func ListRobots(ctx *types.Context, query *ListQuery) (*ListResult, error) {
return listRobotsFromDB(query)
}

// If only teamID specified (no other filters), use cache for faster lookup
// Note: Cache only contains autonomous_mode=true robots, so this is safe
if query.TeamID != "" && query.Status == "" && query.Keywords == "" && query.ClockMode == "" {
// If only teamID specified AND explicitly filtering for autonomous_mode=true, use cache
// Cache only contains autonomous_mode=true robots
// When autonomous_mode is not specified or false, must query database to include all robots
if query.TeamID != "" && query.Status == "" && query.Keywords == "" && query.ClockMode == "" &&
query.AutonomousMode != nil && *query.AutonomousMode == true {
robots := mgr.Cache().List(query.TeamID)
return paginateRobots(robots, query), nil
}
Expand Down Expand Up @@ -93,7 +98,6 @@ func GetRobotStatus(ctx *types.Context, memberID string) (*RobotState, error) {
DisplayName: robot.DisplayName,
Bio: robot.Bio,
Status: robot.Status,
Running: robot.RunningCount(),
MaxRunning: 2, // default
}

Expand All @@ -107,11 +111,33 @@ func GetRobotStatus(ctx *types.Context, memberID string) (*RobotState, error) {
state.MaxRunning = robot.Config.Quota.GetMax()
}

// Get running execution IDs
executions := robot.GetExecutions()
state.RunningIDs = make([]string, 0, len(executions))
for _, exec := range executions {
state.RunningIDs = append(state.RunningIDs, exec.ID)
// Get running execution IDs from ExecutionStore (more reliable than in-memory)
// This ensures we get accurate status even when robot is loaded from database
runningExecs, err := executionStore.List(context.Background(), &store.ListOptions{
MemberID: memberID,
Status: types.ExecRunning,
Limit: 100,
})
if err == nil && len(runningExecs) > 0 {
state.Running = len(runningExecs)
state.RunningIDs = make([]string, 0, len(runningExecs))
for _, exec := range runningExecs {
state.RunningIDs = append(state.RunningIDs, exec.ExecutionID)
}
// Update status based on running count
state.Status = types.RobotWorking
} else {
// No running executions from store, check in-memory
executions := robot.GetExecutions()
state.Running = len(executions)
state.RunningIDs = make([]string, 0, len(executions))
for _, exec := range executions {
state.RunningIDs = append(state.RunningIDs, exec.ID)
}
// If there are running executions in memory, update status
if state.Running > 0 {
state.Status = types.RobotWorking
}
}

// Set last run time
Expand Down
3 changes: 3 additions & 0 deletions agent/robot/api/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,9 @@ type TriggerRequest struct {

// Executor mode (optional, overrides robot config)
ExecutorMode types.ExecutorMode `json:"executor_mode,omitempty"`

// i18n support
Locale string `json:"locale,omitempty"` // Locale for UI messages (e.g., "en", "zh")
}

// InsertPosition - where to insert task in queue
Expand Down
8 changes: 8 additions & 0 deletions agent/robot/executor/standard/delivery.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ func (e *Executor) RunDelivery(ctx *robottypes.Context, exec *robottypes.Executi
return fmt.Errorf("robot not found in execution")
}

// Update UI field with i18n
locale := getEffectiveLocale(robot, exec.Input)
e.updateUIFields(ctx, exec, "", getLocalizedMessage(locale, "generating_delivery"))

// Get agent ID for delivery phase
agentID := "__yao.delivery" // default
if robot.Config != nil && robot.Config.Resources != nil {
Expand Down Expand Up @@ -108,6 +112,10 @@ func (e *Executor) routeToDeliveryCenter(ctx *robottypes.Context, exec *robottyp
return nil
}

// Update UI field to show delivery is in progress
locale := getEffectiveLocale(robot, exec.Input)
e.updateUIFields(ctx, exec, "", getLocalizedMessage(locale, "sending_delivery"))

// Create Delivery Center and execute
center := NewDeliveryCenter()
results, err := center.Deliver(ctx, exec.Delivery.Content, &robottypes.DeliveryContext{
Expand Down
Loading