Update tasks

Updating the status of tasks in Lattice using the Lattice SDK

In Lattice, a task status represents each stage of the task lifecycle. When an agent receives a task, it calls the following API to update a task in Lattice:

  • UpdateStatus: Used to report real-time status updates to Lattice as the agent makes progress through a task.

Before you begin

  • To publish taskable entities, and subscribe to tasks, set up your Lattice environment.
  • Familiarize yourself with entities and different entity types.

Start performing a task

After an agent has identified a task to perform and begins to make progress towards its objective, it communicates back to Lattice using the UpdateTaskStatus to cycle through various states of a task lifecycle.

To update the status of a task, do the following:

1

Listen for tasks

Use the ListenAsAgent operation. In this example, the agent listens for assigned tasks, then updates the task status, incrementing the StatusVersion of the task.

Replace AGENT_ID with the ID of the agent you want to task. If you are developing on Sandboxes, replace this with the following simulated asset: Demo-Sim-Asset1:

1package main
2
3import (
4 "context"
5 "fmt"
6 "net/http"
7 "os"
8 "time"
9
10 Lattice "github.com/anduril/lattice-sdk-go/v2"
11 "github.com/anduril/lattice-sdk-go/v2/client"
12 "github.com/anduril/lattice-sdk-go/v2/option"
13)
14
15func main() {
16 // Get environment variables
17 latticeEndpoint := os.Getenv("LATTICE_ENDPOINT")
18 environmentToken := os.Getenv("ENVIRONMENT_TOKEN")
19 sandboxesToken := os.Getenv("SANDBOXES_TOKEN")
20
21 // Check required environment variables
22 if latticeEndpoint == "" || environmentToken == "" || sandboxesToken == "" {
23 fmt.Println("Missing required environment variables")
24 os.Exit(1)
25 }
26
27 // Initialize headers for sandbox authorization
28 headers := http.Header{}
29 headers.Add("Anduril-Sandbox-Authorization", fmt.Sprintf("Bearer %s", sandboxesToken))
30 // Create the client
31 LatticeClient := client.NewClient(
32 option.WithToken(environmentToken),
33 option.WithBaseURL(fmt.Sprintf("https://%s", latticeEndpoint)),
34 option.WithHTTPHeader(headers),
35 )
36
37 // Set the entity ID to listen for tasks
38 entityId := "<ENTITY_ID>"
39 fmt.Printf("Listening for tasks for entity %s...\n", entityId)
40
41 // Create context for the request
42 ctx := context.Background()
43
44 // Main loop to continuously listen for tasks
45 for {
46 try := func() bool {
47 // Listen for tasks using the listenAsAgent API
48 agentSelector := Lattice.AgentListener{
49 AgentSelector: &Lattice.EntityIDsSelector{
50 EntityIDs: []string{entityId},
51 },
52 }
53 response, err := LatticeClient.Tasks.ListenAsAgent(ctx, &agentSelector)
54
55 // Handle errors
56 if err != nil {
57 fmt.Printf("Error listening for tasks: %v\n", err)
58 return false
59 }
60
61 // Process the request based on its type
62 if response.ExecuteRequest != nil && response.ExecuteRequest.Task != nil &&
63 response.ExecuteRequest.Task.Version != nil && response.ExecuteRequest.Task.Version.TaskID != nil {
64 // Extract task-specific information
65 taskId := *response.ExecuteRequest.Task.Version.TaskID
66 taskStatusVersion := *response.ExecuteRequest.Task.Version.StatusVersion
67
68 fmt.Printf("Starting task %s, version %d\n", taskId, taskStatusVersion)
69
70 // Update task status to STATUS_EXECUTING
71 result, err := startTask(ctx, LatticeClient, taskId, taskStatusVersion, entityId)
72 if err != nil {
73 fmt.Printf("Error starting task: %v\n", err)
74 return false
75 }
76
77 fmt.Printf("Started task with status version: %d\n", *result.StatusVersion)
78
79 } else if response.CompleteRequest != nil && response.CompleteRequest.TaskID != nil {
80 fmt.Printf("Completing task: %s\n", *response.CompleteRequest.TaskID)
81 } else if response.CancelRequest != nil && response.CancelRequest.TaskID != nil {
82 fmt.Printf("Cancelling task: %s\n", *response.CancelRequest.TaskID)
83 }
84
85 return true
86 }()
87
88 if !try {
89 // If there was an error, wait a bit before retrying
90 time.Sleep(1 * time.Second)
91 }
92 }
93}
94
95// startTask updates the task status to STATUS_EXECUTING
96func startTask(ctx context.Context, client *client.Client, taskId string, taskStatusVersion int, agentEntityId string) (*Lattice.TaskVersion, error) {
97 // Increment status version for the update
98 taskStatusVersion++
99
100 // Create system principal with the agent entity ID
101 principal := Lattice.Principal{
102 System: &Lattice.System{
103 EntityID: &agentEntityId,
104 },
105 }
106
107 taskStatus := Lattice.TaskStatus{
108 Status: Lattice.TaskStatusStatusStatusExecuting.Ptr(),
109 }
110
111 // Create task status update request
112 taskStatusUpdate := Lattice.TaskStatusUpdate{
113 StatusVersion: &taskStatusVersion,
114 NewStatus: &taskStatus,
115 Author: &principal,
116 }
117
118 // Call the UpdateTaskStatus API
119 task, err := client.Tasks.UpdateTaskStatus(ctx, taskId, &taskStatusUpdate)
120 if err != nil {
121 return nil, fmt.Errorf("error updating task status: %w", err)
122 }
123
124 return task.Version, nil
125}
2

Assign a task using the UI

To assign a task, do the following:

  1. Open your environment’s Lattice UI, and choose an asset from the Assets panel. On Sandboxes, choose Demo-Sim-Asset1:
  2. From the entity pane, choose Task, then select a task, for example, Follow:
    Shows the drop down menu where you can see the available tasks for the entity.
  3. From the Task Details panel on the right hand side, select a target then choose Execute Task:
    Shows the drop down menu where you can see available targets for the task.
3

Verify the task status

If successful, you see the following output in your local development console:

$Starting task <task-id>, version 1.
>Started task with status version: 2.

The agent has successfully updated the tasks status and incremented the task status version.

Task lifecycle

In Lattice tasks move through the following states:

1

STATUS_SENT

The agent receives a task with STATUS_SENT:

1"task": {
2 "version": {
3 "taskId": "my-task",
4 "definitionVersion": 1,
5 "statusVersion": 1
6 },
7 "status": {
8 "status": "STATUS_SENT"
9 }
10}
2

STATUS_MACHINE_RECEIPT

The agent then responds back with status STATUS_MACHINE_RECEIPT, indicating that the task has been received, and incrementing statusVersion accordingly:

1"statusUpdate": {
2 "version": {
3 "taskId": "my-task",
4 "definitionVersion": 1,
5 "statusVersion": 2
6 },
7 "status": {
8 "status": "STATUS_MACHINE_RECEIPT"
9 }
10}
3

STATUS_ACK

When the agent is ready to acknowledge the task, it does so using STATUS_ACK, and again increments statusVersion:

1"statusUpdate": {
2 "version": {
3 "taskId": "my-task",
4 "definitionVersion": 1,
5 "statusVersion": 3
6 },
7 "status": {
8 "status": "STATUS_ACK"
9 }
10}
4

STATUS_WILCO

The agent confirms it intends to execute the task using STATUS_WILCO:

1"statusUpdate": {
2 "version": {
3 "taskId": "my-task",
4 "definitionVersion": 1,
5 "statusVersion": 4
6 },
7 "status": {
8 "status": "STATUS_WILCO"
9 }
10}
5

STATUS_EXECUTING

As the agent begins to actively execute the task, it indicates this by reporting STATUS_EXECUTING back to Lattice:

1"statusUpdate": {
2 "version": {
3 "taskId": "my-task",
4 "definitionVersion": 1,
5 "statusVersion": 5
6 },
7 "status": {
8 "status": "STATUS_EXECUTING"
9 }
10}
6

STATUS_DONE_OK

Finally, when the agent reaches a terminal state and completes the task successfully, it reports STATUS_DONE_OK. This might result from operator-initiated requests for task completion:

1"statusUpdate": {
2 "version": {
3 "taskId": "my-task",
4 "definitionVersion": 1,
5 "statusVersion": 6
6 },
7 "status": {
8 "status": "STATUS_DONE_OK"
9 }
10}
7

STATUS_DONE_NOT_OK

If the agent reaches a terminal state but does not complete the task successfully, it reports STATUS_DONE_NOT_OK. This might result from operator-initiated requests for task completion or cancellation. The agent should include a descriptive TaskError when reporting STATUS_DONE_NOT_OK:

1"statusUpdate": {
2 "version": {
3 "taskId": "my-task",
4 "definitionVersion": 1,
5 "statusVersion": 7
6 },
7 "status": {
8 "status": "DONE_NOT_OK",
9 "taskError": {
10 "code": "ERROR_CODE_FAILED",
11 "message": "The asset failed task execution due to an internal error.",
12 }
13 }
14}

In this example, the message indicates that the agent encountered an internal error during task execution. You can add more descriptive errors to help the operator troubleshoot the issue accordingly.

The STATUS_DONE_OK and STATUS_DONE_NOT_OK statuses are considered terminal states. Once a task’s reaches either state, it’s complete and cannot be updated.

What’s next

  • To learn more about tasks, see Task an asset in in the Lattice SDK sample applications guide.