Skip to content

Commit 2eba3a2

Browse files
committed
Added prompt and app tool mapping from getTemplateContext() for agents
1 parent d0c2c32 commit 2eba3a2

5 files changed

Lines changed: 98 additions & 147 deletions

File tree

ai.go

Lines changed: 36 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7654,21 +7654,23 @@ func ReduceAgentResponseData(rawResponse []byte, dataFilter string, fieldsNeeded
76547654

76557655
// createNextActions = false => start of agent to find initial decisions
76567656
// createNextActions = true => mid-agent to decide next steps
7657-
func getTemplateContext(ctx context.Context, template string, execution WorkflowExecution) (string, string, error) {
7657+
func getTemplateContext(ctx context.Context, template string, execution WorkflowExecution) (string, string, []string, error) {
7658+
// FIXME: Handle dynamic templates here too, based on user input
7659+
76587660
switch template {
76597661
case "workflow-edit":
76607662
return buildWorkflowEditContext(ctx, execution)
76617663
case "computer-use":
76627664
return buildComputerUseContext(ctx, execution)
76637665
default:
7664-
return "", "", nil
7666+
return "", "", []string{}, nil
76657667
}
76667668
}
76677669

7668-
func buildComputerUseContext(ctx context.Context, execution WorkflowExecution) (string, string, error) {
7670+
func buildComputerUseContext(ctx context.Context, execution WorkflowExecution) (string, string, []string, error) {
76697671
systemRule := `You are a computer use agent that can execute commands to control a computer. Your goal is to use the Terminal, Keyboard, Mouse and Screenshots to perform the task the user intends in the best possible way. Make assumptions for what they most likely want to perform, and continue it is done.
76707672

7671-
Use the 'post_control_mouse_and_keyboard' function for keyboard & mouse control if it is available. You can chain together escaped JSON commands in the the "actions" array using the operations detailed below.
7673+
Use the 'post_control_mouse_and_keyboard' function for keyboard & mouse control if it is available. You can chain together escaped JSON commands in the the "actions" array using the operations detailed below. If an action takes more than 30 seconds, it will return an execution_id and authorization key to be used for polling results. When polling, always add a 30 second delay.
76727674

76737675
Params for each keyboard & mouse operation:
76747676
1. keyboard.press: {\"op\":\"keyboard.press\",\"params\":{\"key\":75}}
@@ -7691,13 +7693,18 @@ When sending body to the post_control_mouse_and_keyboard function, the following
76917693
}
76927694
'''
76937695

7694-
Prioritise terminal commands before screenshots. IF an action takes more than 30 seconds, it will return an execution_id and authorization key to be used for polling results. ALWAYS validate if the action was successful by checking the output of the or a before and after screenshot. If it was not successful, you must try again with a different approach.
7696+
ALWAYS validate if the action was successful by checking the output of the operations before AND after with screenshots or terminal input/output. If it was not successful, you must try again with a different approach.
76957697
`
76967698

7697-
return systemRule, "", nil
7699+
templateContext := ""
7700+
requiredApps := []string{
7701+
"app:48a954b9440b3913b8a2620e57b94a75:shuffle_host_monitors",
7702+
}
7703+
7704+
return systemRule, templateContext, requiredApps, nil
76987705
}
76997706

7700-
func buildWorkflowEditContext(ctx context.Context, execution WorkflowExecution) (string, string, error) {
7707+
func buildWorkflowEditContext(ctx context.Context, execution WorkflowExecution) (string, string, []string, error) {
77017708
targetWorkflowId := execution.ExecutionArgument
77027709

77037710
user := User{
@@ -7711,7 +7718,7 @@ func buildWorkflowEditContext(ctx context.Context, execution WorkflowExecution)
77117718
appsJson, err := json.Marshal(appSummaries)
77127719
if err != nil {
77137720
log.Printf("[WARNING] buildWorkflowEditContext: failed marshaling app summaries for org %s: %s", execution.ExecutionOrg, err)
7714-
return "", "", fmt.Errorf("failed to marshal app summaries: %w", err)
7721+
return "", "", []string{}, fmt.Errorf("failed to marshal app summaries: %w", err)
77157722
}
77167723

77177724
// What we tell the agent about its workflow_id
@@ -7910,7 +7917,12 @@ CRITICAL RULES FOR THE AGENT
79107917
%s
79117918
<End of Available Apps>`, workflowIdLine, string(appsJson))
79127919

7913-
return systemRule, templateContext, nil
7920+
requiredApps := []string{
7921+
"app:7db43ccd25261967b095cfbd467a75cc:shuffle_apps",
7922+
"app:de4ef2287bd41b9d5563e39989643ee6:shuffle_workflows_builder",
7923+
}
7924+
7925+
return systemRule, templateContext, requiredApps, nil
79147926
}
79157927

79167928
func getWorkflowEditPromptRemovals() []string {
@@ -8856,8 +8868,9 @@ data_filter:
88568868
// If a template is set, get secondary system rules + extra context.
88578869
templateSystemRule := ""
88588870
templateContext := ""
8871+
requiredApps := []string{}
88598872
if len(template) > 0 {
8860-
templateSystemRule, templateContext, err = getTemplateContext(ctx, template, execution)
8873+
templateSystemRule, templateContext, requiredApps, err = getTemplateContext(ctx, template, execution)
88618874
if err != nil {
88628875
log.Printf("[ERROR] Failed to get template context: %v", err)
88638876
}
@@ -8875,6 +8888,18 @@ data_filter:
88758888
agentReasoningEffort = foundReasoning
88768889
}
88778890

8891+
added := false
8892+
for _, requiredApp := range requiredApps {
8893+
if !strings.Contains(allowedActionString, requiredApp) {
8894+
added = true
8895+
allowedActionString += "," + requiredApp
8896+
}
8897+
}
8898+
8899+
if added {
8900+
allowedActionString = strings.TrimPrefix(allowedActionString, ",")
8901+
}
8902+
88788903
agentOutput := AgentOutput{
88798904
Status: "RUNNING",
88808905
Input: userMessage,
@@ -8950,7 +8975,7 @@ data_filter:
89508975
if len(templateContext) > 0 {
89518976
completionRequest.Messages = append(completionRequest.Messages, openai.ChatCompletionMessage{
89528977
//Role: openai.ChatMessageRoleUser,
8953-
Role: openai.ChatMessageRoleSystem,
8978+
Role: openai.ChatMessageRoleUser,
89548979
Content: "SKILL CONTEXT:\n" + templateContext,
89558980
})
89568981
}

db-connector.go

Lines changed: 51 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2446,6 +2446,19 @@ func GetEnvironment(ctx context.Context, id, orgId string) (*Environment, error)
24462446
cacheData := []byte(cache.([]uint8))
24472447
err = json.Unmarshal(cacheData, &env)
24482448
if err == nil {
2449+
2450+
timenow := time.Now().Unix()
2451+
if env.SensorGroup {
2452+
for sensorIndex, _ := range env.SensorHosts {
2453+
sensor := env.SensorHosts[sensorIndex]
2454+
2455+
env.SensorHosts[sensorIndex].Active = false
2456+
if sensor.Checkin > 0 && timenow-sensor.Checkin < 300 {
2457+
env.SensorHosts[sensorIndex].Active = true
2458+
}
2459+
}
2460+
}
2461+
24492462
return env, nil
24502463
}
24512464
} else {
@@ -2566,16 +2579,26 @@ func GetEnvironment(ctx context.Context, id, orgId string) (*Environment, error)
25662579
} else {
25672580
key := datastore.NameKey(nameKey, strings.ToLower(id), nil)
25682581
if err := project.Dbclient.Get(ctx, key, env); err != nil {
2582+
log.Printf("[ERROR] Problem in environment loading of %s", id)
25692583
if strings.Contains(err.Error(), `cannot load field`) {
2570-
log.Printf("[INFO] Error in environment loading of %s", id)
25712584
err = nil
25722585
} else {
25732586
return env, err
25742587
}
25752588
}
25762589
}
25772590

2578-
//log.Printf("[DEBUG] Got hit: %s", env)
2591+
timenow := time.Now().Unix()
2592+
if env.SensorGroup {
2593+
for sensorIndex, _ := range env.SensorHosts {
2594+
sensor := env.SensorHosts[sensorIndex]
2595+
2596+
env.SensorHosts[sensorIndex].Active = false
2597+
if sensor.Checkin > 0 && timenow-sensor.Checkin < 300 {
2598+
env.SensorHosts[sensorIndex].Active = true
2599+
}
2600+
}
2601+
}
25792602

25802603
if project.CacheDb {
25812604
//log.Printf("[DEBUG] Setting cache for workflow %s", cacheKey)
@@ -6414,6 +6437,20 @@ func GetEnvironments(ctx context.Context, orgId string) ([]Environment, error) {
64146437
// log.Printf("[DEBUG] Got %d environments from cache for orgId '%s'", len(environments), orgId)
64156438
//}
64166439

6440+
timenow := time.Now().Unix()
6441+
for envIndex, env := range environments {
6442+
if env.SensorGroup {
6443+
for sensorIndex, _ := range env.SensorHosts {
6444+
sensor := env.SensorHosts[sensorIndex]
6445+
6446+
environments[envIndex].SensorHosts[sensorIndex].Active = false
6447+
if sensor.Checkin > 0 && timenow-sensor.Checkin < 300 {
6448+
environments[envIndex].SensorHosts[sensorIndex].Active = true
6449+
}
6450+
}
6451+
}
6452+
}
6453+
64176454
return environments, nil
64186455
}
64196456
} else {
@@ -6584,6 +6621,18 @@ func GetEnvironments(ctx context.Context, orgId string) ([]Environment, error) {
65846621
// Fixing environment return search problems
65856622
timenow := time.Now().Unix()
65866623
for envIndex, env := range environments {
6624+
6625+
if env.SensorGroup {
6626+
for sensorIndex, _ := range env.SensorHosts {
6627+
sensor := env.SensorHosts[sensorIndex]
6628+
6629+
environments[envIndex].SensorHosts[sensorIndex].Active = false
6630+
if sensor.Checkin > 0 && timenow-sensor.Checkin < 300 {
6631+
environments[envIndex].SensorHosts[sensorIndex].Active = true
6632+
}
6633+
}
6634+
}
6635+
65876636
if env.Name == "Cloud" {
65886637
environments[envIndex].Type = "cloud"
65896638
environments[envIndex].RunType = "cloud"

executions.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,14 @@ func Fixexecution(ctx context.Context, workflowExecution WorkflowExecution) (Wor
7676

7777
// Special cleanup for agents
7878
if innerresult.Action.AppName == "AI Agent" || innerresult.Action.AppName == "Shuffle Agent" {
79+
if workflowExecution.Status == "FINISHED" || workflowExecution.Status == "ABORTED" {
80+
if workflowExecution.Status == "FINISHED" {
81+
log.Printf("[DEBUG][%s] Fixexecution: Agent execution is finished, skipping agent result %s", workflowExecution.ExecutionId, innerresult.Action.ID)
82+
}
83+
84+
break
85+
}
86+
7987
actionCacheId := fmt.Sprintf("%s_%s_result", workflowExecution.ExecutionId, innerresult.Action.ID)
8088
if cachedData, cacheErr := GetCache(ctx, actionCacheId); cacheErr == nil {
8189
cachedBytes := []byte(cachedData.([]uint8))

shared.go

Lines changed: 0 additions & 133 deletions
Original file line numberDiff line numberDiff line change
@@ -36767,139 +36767,6 @@ func GetWorkflowMinimal(resp http.ResponseWriter, request *http.Request) {
3676736767
resp.Write(responseData)
3676836768
}
3676936769

36770-
func AgentWorkflowEditor(resp http.ResponseWriter, request *http.Request) {
36771-
cors := HandleCors(resp, request)
36772-
if cors {
36773-
return
36774-
}
36775-
36776-
ctx := GetContext(request)
36777-
user, userErr := HandleApiAuthentication(resp, request)
36778-
if userErr != nil {
36779-
log.Printf("[AUDIT] Api authentication failed in AgentWorkflowEditor: %s", userErr)
36780-
resp.WriteHeader(401)
36781-
resp.Write([]byte(`{"success": false}`))
36782-
return
36783-
}
36784-
36785-
body, err := ioutil.ReadAll(request.Body)
36786-
if err != nil {
36787-
log.Printf("[WARNING] Failed reading body in AgentWorkflowEditor: %s", err)
36788-
resp.WriteHeader(400)
36789-
resp.Write([]byte(`{"success": false}`))
36790-
return
36791-
}
36792-
36793-
var req MCPRequest
36794-
err = json.Unmarshal(body, &req)
36795-
36796-
if err != nil || len(strings.TrimSpace(req.Params.Input.Text)) == 0 {
36797-
log.Printf("[WARNING] Bad body in AgentWorkflowEditor. Error: %v, Body: %s", err, string(body))
36798-
resp.WriteHeader(400)
36799-
resp.Write([]byte(`{"success": false, "reason": "input field is required"}`))
36800-
return
36801-
}
36802-
36803-
36804-
// All context building (workflow state, app actions, rules) is handled inside
36805-
// buildWorkflowEditContext which is called by getTemplateContext inside HandleAiAgentExecutionStart
36806-
toolApps := "app:7db43ccd25261967b095cfbd467a75cc:shuffle_apps,app:de4ef2287bd41b9d5563e39989643ee6:shuffle_workflows_builder"
36807-
36808-
action := Action{
36809-
ID: uuid.NewV4().String(),
36810-
Name: "agent",
36811-
AppName: "AI Agent",
36812-
AppID: "shuffle_agent",
36813-
AppVersion: "1.0.0",
36814-
Environment: "cloud",
36815-
Parameters: []WorkflowAppActionParameter{
36816-
{
36817-
Name: "input",
36818-
Value: req.Params.Input.Text,
36819-
},
36820-
{
36821-
Name: "action",
36822-
Value: toolApps,
36823-
},
36824-
{
36825-
Name: "template",
36826-
Value: "workflow-edit",
36827-
},
36828-
{
36829-
Name: "execution_mode",
36830-
Value: "direct",
36831-
},
36832-
},
36833-
}
36834-
36835-
workflowId := uuid.NewV4().String()
36836-
action.SourceWorkflow = workflowId
36837-
36838-
exec := WorkflowExecution{
36839-
Workflow: Workflow{
36840-
ID: workflowId,
36841-
Actions: []Action{
36842-
action,
36843-
},
36844-
OrgId: user.ActiveOrg.Id,
36845-
Owner: user.Username,
36846-
UpdatedBy: user.Username,
36847-
Start: action.ID,
36848-
},
36849-
Type: "AGENT",
36850-
Start: action.ID,
36851-
Status: "EXECUTING",
36852-
WorkflowId: workflowId,
36853-
ExecutionId: workflowId,
36854-
ExecutionOrg: user.ActiveOrg.Id,
36855-
StartedAt: int64(time.Now().Unix()),
36856-
Authorization: uuid.NewV4().String(),
36857-
// Store the target workflow_id here so buildWorkflowEditContext can fetch it
36858-
ExecutionArgument: req.Params.Input.WorkflowId,
36859-
}
36860-
36861-
SetWorkflowExecution(ctx, exec, true)
36862-
36863-
log.Printf("[INFO] AgentWorkflowEditor: calling HandleAiAgentExecutionStart for user %s (%s), target_workflow_id=%s, execution_id=%s", user.Username, user.Id, req.Params.Input.WorkflowId, exec.ExecutionId)
36864-
36865-
returnAction, err := HandleAiAgentExecutionStart(exec, action, false, "AgentWorkflowEditor")
36866-
if err != nil {
36867-
log.Printf("[ERROR] HandleAiAgentExecutionStart failed in AgentWorkflowEditor: %s", err)
36868-
resp.WriteHeader(500)
36869-
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "%s"}`, err.Error())))
36870-
return
36871-
}
36872-
36873-
// Fetch the updated execution to return
36874-
newExec, err := GetWorkflowExecution(ctx, exec.ExecutionId)
36875-
if err != nil {
36876-
log.Printf("[ERROR] Failed to get workflow execution after agent start in AgentWorkflowEditor: %s", err)
36877-
resp.WriteHeader(500)
36878-
resp.Write([]byte(`{"success": false}`))
36879-
return
36880-
}
36881-
36882-
_ = returnAction
36883-
36884-
respObj := agentResponse{
36885-
Success: true,
36886-
ExecutionId: newExec.ExecutionId,
36887-
Authorization: newExec.Authorization,
36888-
}
36889-
36890-
responseData, err := json.Marshal(respObj)
36891-
if err != nil {
36892-
log.Printf("[ERROR] Failed marshalling execution response in AgentWorkflowEditor: %s", err)
36893-
resp.WriteHeader(500)
36894-
resp.Write([]byte(`{"success": false}`))
36895-
return
36896-
}
36897-
36898-
resp.Header().Set("Content-Type", "application/json")
36899-
resp.WriteHeader(200)
36900-
resp.Write(responseData)
36901-
}
36902-
3690336770
func generateNodeID() string {
3690436771
return uuid.NewV4().String()
3690536772
}

structs.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5447,6 +5447,7 @@ type Parsed struct {
54475447
type SensorDetails struct {
54485448
SensorMode bool `json:"sensor_mode,omitempty" datastore:"sensor_mode"`
54495449
Checkin int64 `json:"checkin" datastore:"checkin"`
5450+
Active bool `json:"active,omitempty" datastore:"active"`
54505451
Uuid string `json:"uuid" datastore:"uuid"`
54515452

54525453
User string `json:"user,omitempty" datastore:"user"`
@@ -5470,7 +5471,8 @@ type SensorDetails struct {
54705471

54715472
// Related to Orborus Agent Mode. Used locally.
54725473
type SensorMode struct {
5473-
Enabled bool `json:"enabled" datastore:"enabled"`
5474+
Enabled bool `json:"enabled" datastore:"enabled"`
5475+
Hostname string `json:"hostname" datastore:"hostname"`
54745476

54755477
// Compliance
54765478
ProcessListEnabled string `json:"process_list_enabled" datastore:"process_list_enabled"`

0 commit comments

Comments
 (0)