> This is the Markdown version of the page. For the complete Lattice Developers documentation index, fetch https://developer.anduril.com/llms.txt. Append .md to any page URL for its clean Markdown.

# Integrate an agent

> Publish a taskable agent, process assigned tasks, and report status using the Lattice SDK

A **taskable agent** is an asset, or a group of assets, that listens for tasks assigned to it in
Lattice, executes them, and reports its progress back. This guide walks through the full agent
workflow: publishing a taskable agent, streaming and parsing assigned tasks, updating task status,
and handling cancellation requests.

An agent calls the following API to listen for tasks assigned to it in Lattice:

* [`StreamAsAgent`](/reference/rest/tasks/stream-as-agent) -- For monitoring tasks routed to the agent using REST.
* [`ListenAsAgent`](/reference/grpc/anduril-taskmanager-v-1/task-manager-api/anduril-taskmanager-v-1-listen-as-agent) -- For monitoring tasks routed to the agent using gRPC.

## Before you begin

* To publish taskable entities, and subscribe to tasks, [set up](/guides/getting-started/set-up) your Lattice environment.
* Familiarize yourself with [entities](/guides/entities/overview) and different entity types.
* Review how to [define a task](/guides/tasks/define-a-task). This guide uses the `Objective`
  task definition from that page as its example.

> **gRPC authentication**
>
> These gRPC examples authenticate with a static environment token attached as request metadata.
> If you are using OAuth 2.0 client credentials instead, [set up the token refresh module](/guides/getting-started/authenticate#client-credentials) before running the examples on this page.

## Publish a taskable agent

An *asset* is an entity under your control, or under the control of another operator or system.
Assets may accept tasks such as search or tracking. An *agent* is an asset,
or a group of assets, that can complete a specific set of defined tasks.

To publish an agent, do the following:

#### Define a \`TaskCatalog\`

The entity model's [`TaskCatalog`](/reference/rest/entities/publish-entity#request.body.taskCatalog)
component defines the tasks that an asset can execute.

For example, if your asset supports the [`Objective`](/guides/tasks/define-a-task) task, publish an asset entity that includes it in its catalog, and listen for and execute that task. The
`taskSpecificationUrl` is the type URL of the task definition you authored and published to the
Schema Registry:

**`taskCatalog`**

```json title="taskCatalog"
  "taskCatalog": {
      // Define the tasks the asset can perform.
      "taskDefinitions": [
          {
              // Set the task specification URL to your Objective definition.
              "taskSpecificationUrl": "type.googleapis.com/org.example.reconnaissance.v1beta.Objective"
          }
      ]
  }
```

#### Publish the agent

Use the [`PublishEntity`](/reference/rest/entities/publish-entity) method to publish a taskable agent:

```go title={"Go (REST)"} startLine={52} maxLines={30}
package main

import (
	"context"
	"fmt"
	"net/http"
	"os"
	"time"

	Lattice "github.com/anduril/lattice-sdk-go/v5"
	"github.com/anduril/lattice-sdk-go/v5/client"
	"github.com/anduril/lattice-sdk-go/v5/option"
	"github.com/google/uuid"
)

func main() {
	// Get environment variables
	latticeEndpoint := os.Getenv("LATTICE_ENDPOINT")
	environmentToken := os.Getenv("ENVIRONMENT_TOKEN")

	// Remove sandboxesToken from the following statements if you are not developing on Sandboxes
	sandboxesToken := os.Getenv("SANDBOXES_TOKEN")

	// Check required environment variables
	if latticeEndpoint == "" || environmentToken == "" || sandboxesToken == "" {
		fmt.Println("Missing required environment variables")
		os.Exit(1)
	}

	// Initialize headers for sandbox authorization
	headers := http.Header{}
	headers.Add("Anduril-Sandbox-Authorization", fmt.Sprintf("Bearer %s", sandboxesToken))

	// Create the client
	LatticeClient := client.NewClient(
		option.WithToken(environmentToken),
		option.WithBaseURL(fmt.Sprintf("https://%s", latticeEndpoint)),
		option.WithHTTPHeader(headers),
	)

	// Generate a unique ID for the entity
	entityId := uuid.New().String()

	// Get creation time
	creationTime := time.Now().UTC()

	// Continuously publish the entity
	for {
		latestTimestamp := time.Now().UTC()
		ctx := context.Background()

		// Create entity to publish
		entity := Lattice.Entity{
			EntityID:    &entityId,
			Description: Lattice.String("Friendly drone asset"),
			Aliases: &Lattice.Aliases{
				Name: Lattice.String("Drone 1"),
			},
			IsLive:      Lattice.Bool(true),
			CreatedTime: Lattice.Time(creationTime),
			ExpiryTime:  Lattice.Time(latestTimestamp.Add(1 * time.Minute)),
			Ontology: &Lattice.Ontology{
				Template:     Lattice.OntologyTemplateTemplateAsset.Ptr(),
				PlatformType: Lattice.String("UAV"),
			},
			MilView: &Lattice.MilView{
				Disposition: Lattice.MilViewDispositionDispositionFriendly.Ptr(),
				Environment: Lattice.MilViewEnvironmentEnvironmentAir.Ptr(),
			},
			Location: &Lattice.Location{
				Position: &Lattice.Position{
					LatitudeDegrees:   Lattice.Float64(50.91402185768586),
					LongitudeDegrees:  Lattice.Float64(0.79203612077257),
					AltitudeAsfMeters: Lattice.Float64(1000),
				},
			},
			Provenance: &Lattice.Provenance{
				IntegrationName:  Lattice.String("your_integration_name"),
				DataType:         Lattice.String("your_data_type"),
				SourceUpdateTime: Lattice.Time(latestTimestamp),
			},
			Health: &Lattice.Health{
				ConnectionStatus: Lattice.HealthConnectionStatusConnectionStatusOnline.Ptr(),
				HealthStatus:     Lattice.HealthHealthStatusHealthStatusHealthy.Ptr(),
				UpdateTime:       Lattice.Time(latestTimestamp),
			},
			TaskCatalog: &Lattice.TaskCatalog{
				TaskDefinitions: []*Lattice.TaskDefinition{
					{TaskSpecificationURL: Lattice.String("type.googleapis.com/anduril.tasks.v2.VisualId")},
					{TaskSpecificationURL: Lattice.String("type.googleapis.com/anduril.tasks.v2.Investigate")},
				},
			},
		}

		// Publish the entity
		_, err := LatticeClient.Entities.PublishEntity(ctx, &entity)

		// Handle errors
		if err != nil {
			fmt.Printf("Error publishing entity: %v\n", err)
		} else {
			fmt.Println("Published asset: " + entityId)
		}

		// Wait before next request
		time.Sleep(5 * time.Second)
	}
}

```

```java title={"Java (REST)"} startLine={80} maxLines={30}
package org.example;
import java.time.Instant;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.time.temporal.ChronoUnit;
import java.util.Arrays;
import java.util.UUID;

import com.anduril.Lattice;
import com.anduril.types.Aliases;
import com.anduril.types.Entity;
import com.anduril.types.Health;
import com.anduril.types.HealthConnectionStatus;
import com.anduril.types.HealthHealthStatus;
import com.anduril.types.Location;
import com.anduril.types.MilView;
import com.anduril.types.MilViewDisposition;
import com.anduril.types.MilViewEnvironment;
import com.anduril.types.Ontology;
import com.anduril.types.OntologyTemplate;
import com.anduril.types.Position;
import com.anduril.types.Provenance;
import com.anduril.types.TaskCatalog;
import com.anduril.types.TaskDefinition;



public class PublishAgent {
    public static void main(String[] args) {
        // Get environment variables
        String endpoint = System.getenv("LATTICE_ENDPOINT");
        String environmentToken = System.getenv("ENVIRONMENT_TOKEN");
        String sandboxesToken = System.getenv("SANDBOXES_TOKEN");
        
        // Check required variables and ensure URL has https://
        if (endpoint == null || environmentToken == null || sandboxesToken == null) {
            System.err.println("Missing required environment variables");
            System.exit(1);
        }
        if (!endpoint.startsWith("https://")) endpoint = "https://" + endpoint;
        
        try {
            // Create Lattice client with Sandbox authentication
            Lattice client = Lattice.builder()
                .url(endpoint)
                .token(environmentToken)
                .addHeader("Anduril-Sandbox-Authorization", "Bearer " + sandboxesToken)
                .build();
            
            // Generate a unique ID for the entity
            String entityId = UUID.randomUUID().toString();
            
            // Set a radius, in degrees, to simulate the entity moving in a circle
            double radiusDegrees = 0.1;
            double count = 0.0;
            Instant creationTime = Instant.now();
            
            // Continuously publish the entity
            while (true) {
                try {
                    // Get current timestamp
                    Instant latestTimestamp = Instant.now();
                    
                    // Update position
                    count += 0.1;
                    double t = Math.toRadians(count);
                    
                    // Create the entity
                    TaskDefinition visualId = TaskDefinition.builder()
                        .taskSpecificationUrl("type.googleapis.com/anduril.tasks.v2.VisualId")
                        .build();

                    TaskDefinition investigate = TaskDefinition.builder()
                        .taskSpecificationUrl("type.googleapis.com/anduril.tasks.v2.Investigate")
                        .build();

                    Entity entity = Entity.builder()
                        .entityId(entityId)
                        .description("Friendly drone asset")
                        .aliases(Aliases.builder()
                            .name("Drone 1")
                            .build())
                        .isLive(true)
                        .createdTime(OffsetDateTime.ofInstant(creationTime, ZoneOffset.UTC))
                        .expiryTime(OffsetDateTime.ofInstant(latestTimestamp.plus(1, ChronoUnit.MINUTES), ZoneOffset.UTC))
                        .ontology(Ontology.builder()
                            .template(OntologyTemplate.TEMPLATE_ASSET)
                            .platformType("UAV")
                            .build())
                        .milView(MilView.builder()
                            .disposition(MilViewDisposition.DISPOSITION_FRIENDLY)
                            .environment(MilViewEnvironment.ENVIRONMENT_AIR)
                            .build())
                        .location(Location.builder()
                            .position(Position.builder()
                                .latitudeDegrees(50.91402185768586 + (radiusDegrees * Math.cos(t)))
                                .longitudeDegrees(0.79203612077257 + (radiusDegrees * Math.sin(t)))
                                .altitudeAglMeters(1000.0)
                                .build())
                            .build())
                        .provenance(Provenance.builder()
                            .integrationName("your_integration_name")
                            .dataType("your_data_type")
                            .sourceUpdateTime(OffsetDateTime.ofInstant(latestTimestamp, ZoneOffset.UTC))
                            .build())
                        .health(Health.builder()
                            .connectionStatus(HealthConnectionStatus.CONNECTION_STATUS_ONLINE)
                            .healthStatus(HealthHealthStatus.HEALTH_STATUS_HEALTHY)
                            .updateTime(OffsetDateTime.ofInstant(latestTimestamp, ZoneOffset.UTC))
                            .build())
                        .taskCatalog(TaskCatalog.builder()
                            .taskDefinitions(Arrays.asList(visualId, investigate))
                            .build())
                        .build();
                    
                    // Publish the entity
                    client.entities().publishEntity(entity);
                    System.out.println("Publishing asset.");
                    
                    Thread.sleep(1000);
                } catch (Exception e) {
                    System.err.println("Error: " + e.getMessage());
                    Thread.sleep(10000);
                }
            }
        } catch (Exception e) {
            System.err.println("Failed to initialize: " + e.getMessage());
        }
    }
}
```

```py title={"Python (REST)"} startLine={28} maxLines={30}
from anduril import Lattice, Aliases, MilView, Location, \
    Position, Ontology, Provenance, Health, TaskCatalog, TaskDefinition
from datetime import datetime, timezone, timedelta
import asyncio
import os
import sys
from uuid import uuid4

lattice_endpoint = os.getenv('LATTICE_ENDPOINT')
environment_token = os.getenv('ENVIRONMENT_TOKEN')

# Remove sandboxes_token from the following statements if you are not developing on Sandboxes.
sandboxes_token = os.getenv('SANDBOXES_TOKEN')
if not environment_token or not lattice_endpoint or not sandboxes_token:
    print("Missing required environment variables.")
    sys.exit(1)
    
client = Lattice(
    base_url=f"https://{lattice_endpoint}",
    token=lambda: str(environment_token),
    # Remove the following header if you are not developing on Sandboxes.
    headers={ "anduril-sandbox-authorization": f"Bearer {sandboxes_token}" }
)
id = str(uuid4())

async def app():
    try:
        creation_time = datetime.now(timezone.utc)
        while(True):
            latest_timestamp = datetime.now(timezone.utc)
            client.entities.publish_entity(
                entity_id= id,
                description="Drone 1",
                aliases=Aliases(
                    name="Drone 1"
                ),
                is_live=True,
                created_time=creation_time,
                expiry_time=latest_timestamp + timedelta(minutes=10),
                ontology=Ontology(
                    template="TEMPLATE_ASSET",
                    platform_type="UAV"
                ),
                mil_view=MilView(
                    disposition="DISPOSITION_FRIENDLY",
                    environment="ENVIRONMENT_AIR"
                ),
                location=Location(
                    position=Position(
                        latitude_degrees=50.91402185768586,
                        longitude_degrees=0.79203612077257,
                        altitude_asf_meters=1000
                    )
                ),
                provenance=Provenance(
                    integration_name="your_integration_name",
                    data_type="your_data_type",
                    source_update_time=latest_timestamp
                ),
                health=Health(
                    connection_status="CONNECTION_STATUS_ONLINE",
                    health_status="HEALTH_STATUS_HEALTHY",
                    update_time=latest_timestamp
                ),
                task_catalog=TaskCatalog(
                    task_definitions=[
                        TaskDefinition( task_specification_url="type.googleapis.com/anduril.tasks.v2.VisualId" ),
                        TaskDefinition( task_specification_url="type.googleapis.com/anduril.tasks.v2.Investigate" )
                    ]
                )
            )
            print(f"Published asset: {id}")
            await asyncio.sleep(1)

    except asyncio.CancelledError:
        print(">>>Exiting...")
    except Exception as error:
        print(f"Exception: {error}")

if __name__ == "__main__":
    asyncio.run(app())
```

```ts title={"Typescript (REST)"} startLine={31} maxLines={30}
import { LatticeClient } from "@anduril-industries/lattice-sdk";
import { v4 as uuidv4 } from 'uuid';

const latticeEndpoint = process.env.LATTICE_ENDPOINT;
const environmentToken = process.env.ENVIRONMENT_TOKEN;

// Remove sandboxesToken from the following statements if you are not developing on Sandboxes.
const sandboxesToken = process.env.SANDBOXES_TOKEN;
if (!latticeEndpoint || !environmentToken || !sandboxesToken) {
    console.log('Missing required environment variables.');
    process.exit(1);
}
const client = new LatticeClient(
    {
        baseUrl: `https://${latticeEndpoint}`,
        token: environmentToken,
        // Remove the following if you are not developing on Sandboxes.
        headers: { "Anduril-Sandbox-Authorization": `Bearer ${sandboxesToken}` }
    }
);
const entityId = uuidv4()
const minutes = 10;
function getExpiryTime(minutes: number): string {
    const now = new Date();
    now.setMinutes(now.getMinutes() + minutes);
    return now.toISOString();
}
async function App() {
    try {
        const latestTimestamp = new Date().toISOString();
        await client.entities.publishEntity(
            {
                entityId: entityId,
                description: "Drone 1",
                aliases: { name: "Drone 1" },
                isLive: true,
                createdTime: latestTimestamp,
                expiryTime: getExpiryTime(minutes),
                ontology: {
                    template: "TEMPLATE_ASSET",
                    platformType: "UAV"
                },
                milView: {
                    disposition: "DISPOSITION_FRIENDLY",
                    environment: "ENVIRONMENT_AIR"
                },
                location: {
                    position: {
                        latitudeDegrees: 50.91402185768586,
                        longitudeDegrees: 0.79203612077257,
                        altitudeAsfMeters: 1000
                    }
                },
                provenance: {
                    integrationName: "your_integration_name",
                    dataType: "your_data_type",
                    sourceUpdateTime: latestTimestamp
                },
                health: {
                    connectionStatus: "CONNECTION_STATUS_ONLINE",
                    healthStatus: "HEALTH_STATUS_HEALTHY",
                    updateTime: latestTimestamp
                },
                taskCatalog: {
                    taskDefinitions: [
                        { taskSpecificationUrl: "type.googleapis.com/anduril.tasks.v2.VisualId" },
                        { taskSpecificationUrl: "type.googleapis.com/anduril.tasks.v2.Investigate" }
                    ]
                }
            }
        );
        console.log(`Published asset: ${entityId}`);
    } catch (error) {
        console.log(`Encountered the following error while publishing entity: ${error}`);
    }
}
(async function runIndefinitely() {
    while (true) {
        await App();
        await new Promise(resolve => setTimeout(resolve, 1000));
    }
})();
```

```rs title={"Rust (REST)"} startLine={45} maxLines={30}
use anduril_lattice_sdk::prelude::*;

use std::env;
use std::time::Duration;
use uuid::Uuid;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let lattice_endpoint = env::var("LATTICE_ENDPOINT")
        .expect("LATTICE_ENDPOINT environment variable not set");
    let environment_token = env::var("ENVIRONMENT_TOKEN")
        .expect("ENVIRONMENT_TOKEN environment variable not set");
    // Remove sandboxes_token if you are not developing on Sandboxes.
    let sandboxes_token = env::var("SANDBOXES_TOKEN")
        .expect("SANDBOXES_TOKEN environment variable not set");

    if environment_token.is_empty() || lattice_endpoint.is_empty() || sandboxes_token.is_empty() {
        eprintln!("Missing required environment variables:");
        eprintln!("  LATTICE_ENDPOINT");
        eprintln!("  ENVIRONMENT_TOKEN");
        eprintln!("  SANDBOXES_TOKEN");
        std::process::exit(1);
    }

    let config = ClientConfig {
        base_url: format!("https://{}", lattice_endpoint),
        token: Some(environment_token),
        // Remove custom_headers if you are not developing on Sandboxes.
        custom_headers: HashMap::from([(
            "anduril-sandbox-authorization".to_string(),
            format!("Bearer {}", sandboxes_token),
        )]),
        ..Default::default()
    };
    let client = Lattice::new(config)?;

    // A stable entity ID for the lifetime of this process.
    let id = Uuid::new_v4().to_string();

    // Entities must be republished at least every 5 minutes to persist across
    // Lattice service restarts, so publish in a loop.
    let creation_time = Utc::now().fixed_offset();
    loop {
        let latest_timestamp = Utc::now().fixed_offset();
        let entity = Entity {
            entity_id: Some(id.clone()),
            description: Some("Drone 1".to_string()),
            aliases: Some(Aliases {
                name: Some("Drone 1".to_string()),
                ..Default::default()
            }),
            is_live: Some(true),
            created_time: Some(creation_time),
            expiry_time: Some((Utc::now() + chrono::Duration::minutes(10)).fixed_offset()),
            ontology: Some(Ontology {
                template: Some(OntologyTemplate::TemplateAsset),
                platform_type: Some("UAV".to_string()),
                ..Default::default()
            }),
            mil_view: Some(MilView {
                disposition: Some(MilViewDisposition::DispositionFriendly),
                environment: Some(MilViewEnvironment::EnvironmentAir),
                ..Default::default()
            }),
            location: Some(Location {
                position: Some(Position {
                    latitude_degrees: Some(50.91402185768586),
                    longitude_degrees: Some(0.79203612077257),
                    altitude_asf_meters: Some(1000.0),
                    ..Default::default()
                }),
                ..Default::default()
            }),
            provenance: Some(Provenance {
                integration_name: Some("your_integration_name".to_string()),
                data_type: Some("your_data_type".to_string()),
                source_update_time: Some(latest_timestamp),
                ..Default::default()
            }),
            health: Some(Health {
                connection_status: Some(HealthConnectionStatus::ConnectionStatusOnline),
                health_status: Some(HealthHealthStatus::HealthStatusHealthy),
                update_time: Some(latest_timestamp),
                ..Default::default()
            }),
            task_catalog: Some(TaskCatalog {
                task_definitions: Some(vec![
                    TaskDefinition {
                        task_specification_url: Some(
                            "type.googleapis.com/anduril.tasks.v2.VisualId".to_string(),
                        ),
                        ..Default::default()
                    },
                    TaskDefinition {
                        task_specification_url: Some(
                            "type.googleapis.com/anduril.tasks.v2.Investigate".to_string(),
                        ),
                        ..Default::default()
                    },
                ]),
                ..Default::default()
            }),
            ..Default::default()
        };

        match client.entities.publish_entity(&entity, None).await {
            Ok(_) => println!("Published asset: {}", id),
            Err(e) => eprintln!("Exception: {}", e),
        }

        tokio::time::sleep(Duration::from_secs(1)).await;
    }
}

```

```go title={"Go (gRPC)"} startLine={68} maxLines={30}
// This Go example is compatible with artifacts generated using
// the following grpc/go plugin: https://buf.build/anduril/lattice-sdk/sdks/main:grpc/go
package main

import (
	"context"
	"fmt"
	"log"
	"os"
	"time"

	"schema-registry.developer.anduril.com/gen/go/anduril/lattice-sdk/grpc/go/anduril/entitymanager/v1/entitymanagerv1grpc"
	entitymanagerv1 "schema-registry.developer.anduril.com/gen/go/anduril/lattice-sdk/protocolbuffers/go/anduril/entitymanager/v1"
	ontologyv1 "schema-registry.developer.anduril.com/gen/go/anduril/lattice-sdk/protocolbuffers/go/anduril/ontology/v1"
	tasksv2 "schema-registry.developer.anduril.com/gen/go/anduril/lattice-sdk/protocolbuffers/go/anduril/tasks/v2"
	"github.com/google/uuid"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"
	"google.golang.org/protobuf/types/known/timestamppb"
	"google.golang.org/protobuf/types/known/wrapperspb"
)

func main() {
	// Get environment variables
	environmentToken := os.Getenv("ENVIRONMENT_TOKEN")
	latticeEndpoint := os.Getenv("LATTICE_ENDPOINT")
	sandboxesToken := os.Getenv("SANDBOXES_TOKEN")

	// Check required environment variables
	if latticeEndpoint == "" || environmentToken == "" || sandboxesToken == "" {
		fmt.Println("Missing required environment variables")
		os.Exit(1)
	}

	// Setup authentication
	auth := &BearerTokenAuth{
		Token:          environmentToken,
		SandboxesToken: sandboxesToken,
	}

	// Setup gRPC connection options
	opts := []grpc.DialOption{
		grpc.WithTransportCredentials(credentials.NewClientTLSFromCert(nil, "")),
		grpc.WithPerRPCCredentials(auth),
	}

	conn, err := grpc.NewClient(latticeEndpoint, opts...)
	if err != nil {
		log.Fatalf("Did not connect: %v", err)
	}
	defer conn.Close()

	client := entitymanagerv1grpc.NewEntityManagerAPIClient(conn)

	// Generate a unique ID for the entity
	entityId := uuid.New().String()

	// Get creation time
	creationTime := time.Now().UTC()
	creationTimestamp := timestamppb.New(creationTime)

	// Continuously publish the entity
	for {
		// Get current timestamp for this update
		latestTimestamp := time.Now().UTC()
		ctx := context.Background()

		// Create entity to publish
		entity := &entitymanagerv1.Entity{
			EntityId:    entityId,
			Description: "Agent entity",
			Aliases: &entitymanagerv1.Aliases{
				Name: "Agent 1",
			},
			IsLive:      true,
			CreatedTime: creationTimestamp,
			ExpiryTime:  timestamppb.New(latestTimestamp.Add(1 * time.Minute)),
			Ontology: &entitymanagerv1.Ontology{
				Template:     entitymanagerv1.Template_TEMPLATE_ASSET,
				PlatformType: "UAV",
			},
			MilView: &entitymanagerv1.MilView{
				Disposition: ontologyv1.Disposition_DISPOSITION_FRIENDLY,
				Environment: ontologyv1.Environment_ENVIRONMENT_AIR,
			},
			Location: &entitymanagerv1.Location{
				Position: &entitymanagerv1.Position{
					LatitudeDegrees:   50.91402185768586,
					LongitudeDegrees:  0.79203612077257,
					AltitudeAsfMeters: wrapperspb.Double(1000),
				},
			},
			Provenance: &entitymanagerv1.Provenance{
				IntegrationName:  "agent_integration",
				DataType:         "agent_data",
				SourceUpdateTime: timestamppb.New(latestTimestamp),
			},
			Health: &entitymanagerv1.Health{
				ConnectionStatus: entitymanagerv1.ConnectionStatus_CONNECTION_STATUS_ONLINE,
				HealthStatus:     entitymanagerv1.HealthStatus_HEALTH_STATUS_HEALTHY,
				UpdateTime:       timestamppb.New(latestTimestamp),
			},
			TaskCatalog: &tasksv2.TaskCatalog{
				TaskDefinitions: []*tasksv2.TaskDefinition{
					{
						TaskSpecificationUrl: "type.googleapis.com/anduril.tasks.v2.VisualId",
					},
					{
						TaskSpecificationUrl: "type.googleapis.com/anduril.tasks.v2.Investigate",
					},
				},
			},
		}

		// Create publish request
		request := &entitymanagerv1.PublishEntityRequest{
			Entity: entity,
		}

		// Publish the entity
		_, err := client.PublishEntity(ctx, request)

		// Handle errors
		if err != nil {
			fmt.Printf("Error publishing entity: %v\n", err)
		} else {
			fmt.Printf("Published agent: %s\n", entityId)
		}

		// Wait before next request
		time.Sleep(5 * time.Second)
	}
}

```

```py title={"Python (gRPC)"} startLine={60} maxLines={30}
# This Python example is compatible with artifacts generated using the following
# Buf plugins: https://schema-registry.developer.anduril.com/anduril/lattice-sdk/sdks/main:bufbuild/py
# and https://schema-registry.developer.anduril.com/anduril/lattice-sdk/sdks/main:connectrpc/py

import os
import sys
import time
import uuid
from datetime import datetime, timedelta, timezone
from bearer_auth import create_client
from protobuf.wkt import DoubleValue, Timestamp

from anduril.entitymanager.v1.entity_pub_pb import Aliases, Entity, Provenance
from anduril.entitymanager.v1.entity_manager_api_pub_pb import PublishEntityRequest
from anduril.entitymanager.v1.entity_manager_api_pub_connect import EntityManagerAPIClientSync
from anduril.entitymanager.v1.health_status_pub_pb import (
    ConnectionStatus,
    Health,
    HealthStatus,
)
from anduril.entitymanager.v1.location_pub_pb import Location, Position
from anduril.entitymanager.v1.ontology_pub_pb import MilView, Ontology
from anduril.entitymanager.v1.types_pub_pb import Template
from anduril.ontology.v1.type_pub_pb import Disposition, Environment
from anduril.tasks.v2.catalog_pub_pb import TaskCatalog, TaskDefinition


def to_timestamp(dt: datetime) -> Timestamp:
    return Timestamp.from_datetime(dt)


def main():
    environment_token = os.getenv("ENVIRONMENT_TOKEN")
    lattice_endpoint = os.getenv("LATTICE_ENDPOINT")
    sandboxes_token = os.getenv("SANDBOXES_TOKEN")

    if not environment_token or not lattice_endpoint or not sandboxes_token:
        print("Missing required environment variables", file=sys.stderr)
        sys.exit(1)

    # Create the API client over Connect gRPC with authentication
    client = create_client(EntityManagerAPIClientSync, lattice_endpoint, environment_token, sandboxes_token)

    entity_id = str(uuid.uuid4())
    creation_timestamp = to_timestamp(datetime.now(timezone.utc))

    while True:
        latest = datetime.now(timezone.utc)
        latest_ts = to_timestamp(latest)
        expiry_ts = to_timestamp(latest + timedelta(minutes=1))

        entity = Entity(
            entity_id=entity_id,
            description="Agent entity",
            aliases=Aliases(name="Agent 1"),
            is_live=True,
            created_time=creation_timestamp,
            expiry_time=expiry_ts,
            ontology=Ontology(template=Template.ASSET, platform_type="UAV"),
            mil_view=MilView(
                disposition=Disposition.FRIENDLY,
                environment=Environment.AIR,
            ),
            location=Location(
                position=Position(
                    latitude_degrees=50.91402185768586,
                    longitude_degrees=0.79203612077257,
                    altitude_asf_meters=DoubleValue(value=1000),
                ),
            ),
            provenance=Provenance(
                integration_name="agent_integration",
                data_type="agent_data",
                source_update_time=latest_ts,
            ),
            health=Health(
                connection_status=ConnectionStatus.ONLINE,
                health_status=HealthStatus.HEALTHY,
                update_time=latest_ts,
            ),
            task_catalog=TaskCatalog(
                task_definitions=[
                    TaskDefinition(task_specification_url="type.googleapis.com/anduril.tasks.v2.VisualId"),
                    TaskDefinition(task_specification_url="type.googleapis.com/anduril.tasks.v2.Investigate"),
                ],
            ),
        )

        try:
            client.publish_entity(PublishEntityRequest(entity=entity))
            print(f"Published agent: {entity_id}")
        except Exception as error:
            print(f"Error publishing entity: {error}", file=sys.stderr)

        time.sleep(5)


if __name__ == "__main__":
    main()

```

```rs title={"Rust (gRPC)"} startLine={91} maxLines={30}
// This Rust example is compatible with artifacts generated using
// the following grpc/rust plugin: https://buf.build/anduril/lattice-sdk/sdks/main:community/neoeinstein-tonic
mod bearer_auth;

use anduril_lattice_sdk_community_neoeinstein_prost::anduril::entitymanager::v1::{
    Aliases, ConnectionStatus, Entity, Health, HealthStatus, Location, MilView, Ontology,
    Position, Provenance, PublishEntityRequest, Template,
};
use anduril_lattice_sdk_community_neoeinstein_prost::anduril::ontology::v1::{
    Disposition, Environment,
};
use anduril_lattice_sdk_community_neoeinstein_prost::anduril::tasks::v2::{
    TaskCatalog, TaskDefinition,
};
use anduril_lattice_sdk_community_neoeinstein_tonic::anduril::entitymanager::v1::tonic::entity_manager_api_client::EntityManagerApiClient;
use bearer_auth::EnvironmentTokenAuth;
use prost_types::Timestamp;
use tonic::metadata::MetadataValue;
use tonic::transport::{Channel, ClientTlsConfig};
use tonic::Request;
use uuid::Uuid;

use std::env;
use std::time::{Duration, SystemTime, UNIX_EPOCH};

fn now_timestamp() -> Timestamp {
    let d = SystemTime::now().duration_since(UNIX_EPOCH).unwrap();
    Timestamp {
        seconds: d.as_secs() as i64,
        nanos: d.subsec_nanos() as i32,
    }
}

fn add_seconds(ts: &Timestamp, seconds: i64) -> Timestamp {
    Timestamp {
        seconds: ts.seconds + seconds,
        nanos: ts.nanos,
    }
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Load environment variables
    let environment_token = env::var("ENVIRONMENT_TOKEN")
        .expect("ENVIRONMENT_TOKEN environment variable not set");
    let lattice_endpoint = env::var("LATTICE_ENDPOINT")
        .expect("LATTICE_ENDPOINT environment variable not set");
    let sandboxes_token = env::var("SANDBOXES_TOKEN")
        .expect("SANDBOXES_TOKEN environment variable not set");

    // Set up authentication handler
    let auth = EnvironmentTokenAuth::new(
        environment_token,
        sandboxes_token,
    );

    // Create gRPC channel with TLS
    let tls_config = ClientTlsConfig::new().with_native_roots();
    let channel = Channel::from_shared(format!("https://{}", lattice_endpoint))?
        .tls_config(tls_config)?
        .connect()
        .await?;

    // Fetch a fresh access token and build metadata values
    let access_token = auth.get_token().await?;
    let sandboxes_token = auth.sandboxes_token();
    let auth_header: MetadataValue<_> = format!("Bearer {}", access_token).parse()?;
    let sandbox_header: MetadataValue<_> = format!("Bearer {}", sandboxes_token).parse()?;

    // Create EntityManager API client with authentication interceptor
    let mut client = EntityManagerApiClient::with_interceptor(
        channel,
        move |mut req: Request<()>| {
            req.metadata_mut()
                .insert("authorization", auth_header.clone());
            req.metadata_mut()
                .insert("anduril-sandbox-authorization", sandbox_header.clone());
            Ok(req)
        },
    );

    // Generate a unique ID for the entity
    let entity_id = Uuid::new_v4().to_string();
    let creation_timestamp = now_timestamp();

    // Continuously publish the entity
    loop {
        let latest_timestamp = now_timestamp();

        // Create entity to publish
        let entity = Entity {
            entity_id: entity_id.clone(),
            description: "Agent entity".to_string(),
            aliases: Some(Aliases {
                name: "Agent 1".to_string(),
                ..Default::default()
            }),
            is_live: true,
            created_time: Some(creation_timestamp.clone()),
            expiry_time: Some(add_seconds(&latest_timestamp, 60)),
            ontology: Some(Ontology {
                template: Template::Asset as i32,
                platform_type: "UAV".to_string(),
                ..Default::default()
            }),
            mil_view: Some(MilView {
                disposition: Disposition::Friendly as i32,
                environment: Environment::Air as i32,
                ..Default::default()
            }),
            location: Some(Location {
                position: Some(Position {
                    latitude_degrees: 50.91402185768586,
                    longitude_degrees: 0.79203612077257,
                    altitude_asf_meters: Some(1000.0),
                    ..Default::default()
                }),
                ..Default::default()
            }),
            provenance: Some(Provenance {
                integration_name: "agent_integration".to_string(),
                data_type: "agent_data".to_string(),
                source_update_time: Some(latest_timestamp.clone()),
                ..Default::default()
            }),
            health: Some(Health {
                connection_status: ConnectionStatus::Online as i32,
                health_status: HealthStatus::Healthy as i32,
                update_time: Some(latest_timestamp.clone()),
                ..Default::default()
            }),
            task_catalog: Some(TaskCatalog {
                task_definitions: vec![
                    TaskDefinition {
                        task_specification_url: "type.googleapis.com/anduril.tasks.v2.VisualId"
                            .to_string(),
                    },
                    TaskDefinition {
                        task_specification_url:
                            "type.googleapis.com/anduril.tasks.v2.Investigate".to_string(),
                    },
                ],
            }),
            ..Default::default()
        };

        // Publish the entity
        let request = Request::new(PublishEntityRequest {
            entity: Some(entity),
        });
        match client.publish_entity(request).await {
            Ok(_) => println!("Published agent: {}", entity_id),
            Err(e) => eprintln!("Error publishing entity: {}", e),
        }

        tokio::time::sleep(Duration::from_secs(5)).await;
    }
}

```

If successful, you see the entity ID in the console:

```bash
2025-07-16T02:50:04.694Z [INFO]: Published asset: <entity-id>
```

## Process assigned tasks

Once an agent is published, the integration should open a stream to receive tasks routed to it. Each task carries a
[`specification`](/reference/rest/tasks/create-task#request.body.specification) -- a
[`google.protobuf.Any`](/guides/tasks/define-a-task#define-a-custom-schema) that holds the task's data.
To act on a task, the agent reads the metadata off the request, then decodes the specification into
the task type it advertised in its catalog.

#### Stream tasks as an agent

Use [`StreamAsAgent`](/reference/rest/tasks/stream-as-agent),
or [ListenAsAgent](/reference/grpc/anduril-taskmanager-v-1/task-manager-api/anduril-taskmanager-v-1-listen-as-agent)
for gRPC, to open the stream. When an `executeRequest` arrives, the agent reads the task ID and status version,
parses the task data, then reports that it has started the task.

Task delivery over a stream does not wait for the agent to acknowledge receipt, so a silently
dropped connection might result in your agent silently skipping a task. To get a positive signal
that the connection is alive, set `heartbeatIntervalMs` when you open the stream:

**`heartbeatIntervalMs`** `Number`

The interval in milliseconds at which Lattice sends heartbeat events on the stream.
For `StreamAsAgent`, the default is 30000 ms, and the minimum is 1000 ms. Smaller values are
raised to 1000 ms. For `ListenAsAgent`, heartbeats are disabled unless you set this field.

---

When set, Lattice sends a heartbeat at the specified interval, and your agent can treat a missing heartbeat as a dropped
connection and reconnect. Heartbeats arrive on the same stream as task requests and carry only a
timestamp, so the receive loop skips them rather than treating them as tasks.

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`:

```go title={"Go (REST)"} startLine={107} maxLines={26}
package main

import (
	"context"
	"errors"
	"fmt"
	"io"
	"log"
	"net/http"
	"os"
	"time"

	Lattice "github.com/anduril/lattice-sdk-go/v5"
	"github.com/anduril/lattice-sdk-go/v5/client"
	"github.com/anduril/lattice-sdk-go/v5/option"
)

// Type URL of the Objective task defined in objective.proto.
const objectiveType = "type.googleapis.com/org.example.reconnaissance.v1beta.Objective"

// parseObjective parses the Objective carried in the task's specification. Over
// REST, the specification is a google.protobuf.Any represented as JSON: a Type
// URL alongside the task's flattened fields, which the SDK surfaces as
// ExtraProperties.
func parseObjective(specification *Lattice.GoogleProtobufAny) {
	if specification == nil || specification.Type == nil || *specification.Type != objectiveType {
		log.Printf("Unsupported task type: %v", specification.GetType())
		return
	}

	// Objective is a oneof: read whichever target the operator set.
	fields := specification.ExtraProperties
	if entityID, ok := fields["entityId"].(string); ok && entityID != "" {
		log.Printf("  Objective: entity %s", entityID)
	} else if lla, ok := fields["lla"].(map[string]interface{}); ok {
		log.Printf("  Objective: LLA (lat %v, lon %v, alt %vm)",
			lla["latitudeDegrees"], lla["longitudeDegrees"], lla["altitudeHaeM"])
	} else {
		log.Printf("  Objective: no target set")
	}
}

func main() {
	// Get environment variables
	latticeEndpoint := os.Getenv("LATTICE_ENDPOINT")
	environmentToken := os.Getenv("ENVIRONMENT_TOKEN")
	sandboxesToken := os.Getenv("SANDBOXES_TOKEN")

	// Check required environment variables
	if latticeEndpoint == "" || environmentToken == "" || sandboxesToken == "" {
		fmt.Println("Missing required environment variables")
		os.Exit(1)
	}

	// Initialize headers for sandbox authorization
	headers := http.Header{}
	headers.Add("Anduril-Sandbox-Authorization", fmt.Sprintf("Bearer %s", sandboxesToken))
	// Create the client
	LatticeClient := client.NewClient(
		option.WithToken(environmentToken),
		option.WithBaseURL(fmt.Sprintf("https://%s", latticeEndpoint)),
		option.WithHTTPHeader(headers),
	)

	// Set the entity ID to listen for tasks
	entityId := "<AGENT_ID>"
	fmt.Printf("Streaming tasks for entity %s...\n", entityId)

	// Create context for the request
	ctx := context.Background()

	// Create agent stream request. Set HeartbeatIntervalMs so Lattice sends
	// periodic heartbeats; a missing heartbeat signals a dropped connection.
	heartbeatIntervalMs := 30000
	agentStreamRequest := Lattice.AgentStreamRequest{
		AgentSelector: &Lattice.EntityIDsSelector{
			EntityIDs: []string{entityId},
		},
		HeartbeatIntervalMs: &heartbeatIntervalMs,
	}

	// Stream tasks
	stream, err := LatticeClient.Tasks.StreamAsAgent(ctx, &agentStreamRequest)
	if err != nil {
		fmt.Printf("Error streaming tasks: %v\n", err)
		os.Exit(1)
	}

	// Process stream events
	for {
		select {
		case <-ctx.Done():
			log.Printf("Context canceled: %v", ctx.Err())
			return
		default:
			// Continue processing
		}

		event, err := stream.Recv()

		if errors.Is(err, io.EOF) {
			log.Println("Stream completed successfully.")
			return
		}

		if err != nil {
			log.Printf("Error receiving message: %v", err)
			continue
		}

		if event.Event == "heartbeat" {
			timestamp := *event.Heartbeat.Timestamp
			log.Printf("Heartbeat: %s", timestamp)
		} else {
			request := event.GetAgentRequest()
			if executeRequest := request.GetExecuteRequest(); executeRequest != nil {
				task := executeRequest.GetTask()
				if task != nil {
					taskId := *task.GetVersion().GetTaskID()
					taskStatusVersion := *task.GetVersion().GetStatusVersion()
					description := *task.GetDescription()

					log.Printf("Starting task %s, version %d: %s", taskId, taskStatusVersion, description)

					// Parse the Objective the operator sent with the task.
					parseObjective(task.GetSpecification())

					// Update task status to STATUS_EXECUTING
					result, err := startTask(ctx, LatticeClient, taskId, int(taskStatusVersion), entityId)
					if err != nil {
						log.Printf("Error starting task: %v", err)
						continue
					}

					log.Printf("Started task with status version: %d", *result.StatusVersion)
				}
			} else if completeRequest := request.GetCompleteRequest(); completeRequest != nil {
				taskToComplete := completeRequest.GetTaskID()
				if taskToComplete != nil {
					log.Printf("Completing task: %s", *taskToComplete)
				}
			} else if cancelRequest := request.GetCancelRequest(); cancelRequest != nil {
				taskToCancel := cancelRequest.GetTaskID()
				if taskToCancel != nil {
					log.Printf("Cancelling task: %s", *taskToCancel)
				}
			}
		}

		// Sleep briefly to prevent tight looping
		time.Sleep(100 * time.Millisecond)
	}
}

// startTask updates the task status to STATUS_EXECUTING
func startTask(ctx context.Context, client *client.Client, taskId string, taskStatusVersion int, agentEntityId string) (*Lattice.TaskVersion, error) {
	// Increment status version for the update
	taskStatusVersion++

	// Create system principal with the agent entity ID
	principal := Lattice.Principal{
		System: &Lattice.System{
			EntityID: &agentEntityId,
		},
	}

	taskStatus := Lattice.TaskStatus{
		Status: Lattice.TaskStatusStatusStatusExecuting.Ptr(),
	}

	// Create task status update request
	taskStatusUpdate := Lattice.TaskStatusUpdate{
		TaskID:        taskId,
		StatusVersion: &taskStatusVersion,
		NewStatus:     &taskStatus,
		Author:        &principal,
	}

	// Call the UpdateTaskStatus API
	task, err := client.Tasks.UpdateTaskStatus(ctx, &taskStatusUpdate)
	if err != nil {
		return nil, fmt.Errorf("error updating task status: %w", err)
	}

	return task.Version, nil
}

```

```java title={"Java (REST)"} startLine={88} maxLines={27}
package org.example;

import java.util.Arrays;
import java.util.Map;
import java.util.concurrent.CompletableFuture;

import com.anduril.AsyncLattice;
import com.anduril.resources.tasks.requests.AgentStreamRequest;
import com.anduril.resources.tasks.requests.TaskStatusUpdate;
import com.anduril.resources.tasks.types.StreamAsAgentResponse;
import com.anduril.types.AgentStreamEvent;
import com.anduril.types.EntityIdsSelector;
import com.anduril.types.GoogleProtobufAny;
import com.anduril.types.Principal;
import com.anduril.types.Task;
import com.anduril.types.TaskStatus;
import com.anduril.types.TaskStatusStatus;
import com.anduril.types.TaskVersion;

/**
 * StartTask demonstrates how to use the Lattice SDK to stream tasks assigned to an agent
 * and update task status.
 */
public class StartTask {
    // Entity ID to listen for tasks - replace with your actual entity ID.
    private static final String ENTITY_ID = "<AGENT_ID>";

    // Type URL of the Objective task defined in objective.proto.
    private static final String OBJECTIVE_TYPE =
        "type.googleapis.com/org.example.reconnaissance.v1beta.Objective";

    /**
     * Parse the Objective carried in the task's specification.
     * Over REST, the specification is a google.protobuf.Any represented as JSON:
     * a type URL alongside the task's flattened fields.
     */
    private static void parseObjective(Task task) {
        GoogleProtobufAny specification = task.getSpecification().orElse(null);
        if (specification == null || !OBJECTIVE_TYPE.equals(specification.getType().orElse(null))) {
            System.out.println("Unsupported task type");
            return;
        }

        // Objective is a oneof: read whichever target the operator set.
        Map<String, Object> fields = specification.getAdditionalProperties();
        Object entityId = fields.get("entityId");
        Object lla = fields.get("lla");
        if (entityId != null) {
            System.out.println("  Objective: entity " + entityId);
        } else if (lla instanceof Map) {
            @SuppressWarnings("unchecked")
            Map<String, Object> point = (Map<String, Object>) lla;
            System.out.println("  Objective: LLA (lat " + point.get("latitudeDegrees")
                + ", lon " + point.get("longitudeDegrees")
                + ", alt " + point.get("altitudeHaeM") + "m)");
        } else {
            System.out.println("  Objective: no target set");
        }
    }

    public static void main(String[] args) {
        String endpoint = System.getenv("LATTICE_ENDPOINT");
        String environmentToken = System.getenv("ENVIRONMENT_TOKEN");
        String sandboxesToken = System.getenv("SANDBOXES_TOKEN");

        if (endpoint == null || environmentToken == null || sandboxesToken == null) {
            System.err.println("Missing required environment variables");
            System.exit(1);
        }
        if (!endpoint.startsWith("https://")) endpoint = "https://" + endpoint;

        try {
            AsyncLattice client = AsyncLattice.builder()
                .url(endpoint)
                .token(environmentToken)
                .addHeader("Anduril-Sandbox-Authorization", "Bearer " + sandboxesToken)
                .build();

            System.out.println("Streaming tasks for: " + ENTITY_ID);

            // Create agent selector with the specified entity ID.
            EntityIdsSelector agentSelector = EntityIdsSelector.builder()
                .entityIds(Arrays.asList(ENTITY_ID))
                .build();

            // Create agent stream request. Set heartbeatIntervalMs so Lattice sends
            // periodic heartbeats; a missing heartbeat signals a dropped connection.
            AgentStreamRequest agentRequest = AgentStreamRequest.builder()
                .agentSelector(agentSelector)
                .heartbeatIntervalMs(30000)
                .build();

            // Stream tasks from the server
            client.tasks().streamAsAgent(agentRequest)
                .thenAccept(requests -> {
                    for (StreamAsAgentResponse request : requests) {
                        if (request.isHeartbeat()) {
                            System.out.println("Heartbeat: " + request.getHeartbeat().get().getTimestamp().get());
                        } else {
                            AgentStreamEvent event = request.getAgentRequest().get();
                            if (event.getExecuteRequest().isPresent()) {
                                Task task = event.getExecuteRequest().get().getTask().get();

                                String taskId = task
                                    .getVersion().get()
                                    .getTaskId().get();

                                int taskStatusVersion = task
                                    .getVersion().get()
                                    .getStatusVersion().get();

                                String description = task
                                    .getDescription().orElse("No description");

                                System.out.println("Starting task " + taskId + ", version " + taskStatusVersion + ": " + description);

                                // Parse the Objective the operator sent with the task.
                                parseObjective(task);

                                try {
                                    // Update task status.
                                    TaskVersion result = startTask(client, taskId, taskStatusVersion, ENTITY_ID).get();
                                    System.out.println("Started task with status version: " + result.getStatusVersion().get());
                                } catch (Exception e) {
                                    System.err.println("Error starting task: " + e.getMessage());
                                }
                            } else if (event.getCompleteRequest().isPresent()) {
                                System.out.println("Completing task: " + event.getCompleteRequest().get()
                                    .getTaskId().get());
                            } else if (event.getCancelRequest().isPresent()) {
                                System.out.println("Cancelling task: " + event.getCancelRequest().get()
                                    .getTaskId().get());
                            }
                        }
                    }
                })
                .exceptionally(ex -> {
                    System.err.println("Error streaming tasks: " + ex.getMessage());
                    return null;
                })
                .join();
        } catch (Exception e) {
            System.err.println("Failed to initialize: " + e.getMessage());
        }
    }

    /**
     * Update task status to STATUS_EXECUTING.
     *
     * @param client Lattice client
     * @param taskId Task ID to update
     * @param taskStatusVersion Current task status version
     * @param agentEntityId Agent entity ID performing the update
     * @return CompletableFuture containing the updated TaskVersion
     */
    private static CompletableFuture<TaskVersion> startTask(AsyncLattice client, String taskId, Integer taskStatusVersion, String agentEntityId) {
        try {
            // Create system principal with the agent entity ID.
            Principal principal = Principal.builder()
                .system(com.anduril.types.System.builder()
                    .entityId(agentEntityId)
                    .build())
                .build();

            // Create task status for update.
            TaskStatus taskStatus = TaskStatus.builder()
                .status(TaskStatusStatus.STATUS_EXECUTING)
                .build();

            // Increment status version for the update
            taskStatusVersion += 1;

            // Create task status update request.
            TaskStatusUpdate taskStatusUpdate = TaskStatusUpdate.builder()
                .statusVersion(taskStatusVersion)
                .newStatus(taskStatus)
                .author(principal)
                .build();

            return client.tasks().updateTaskStatus(taskId, taskStatusUpdate)
                .thenApply(newStatus -> newStatus.getVersion().get());
        } catch (Exception e) {
            System.err.println("Encountered the following error while starting task: " + e.getMessage());
            return CompletableFuture.failedFuture(e);
        }
    }
}

```

```py title={"Python (REST)"} startLine={74} maxLines={17}
from anduril import AsyncLattice, EntityIdsSelector, TaskStatus, Principal, System
import asyncio
import os
import sys

lattice_endpoint = os.getenv('LATTICE_ENDPOINT')
environment_token = os.getenv('ENVIRONMENT_TOKEN')

# Remove sandboxes_token from the following statements if you are not developing on Sandboxes.
sandboxes_token = os.getenv('SANDBOXES_TOKEN')
if not environment_token or not lattice_endpoint or not sandboxes_token:
    print("Missing required environment variables.")
    sys.exit(1)

client = AsyncLattice(
    base_url=f"https://{lattice_endpoint}",
    token=lambda: str(environment_token),
    # Remove the following header if you are not developing on Sandboxes.
    headers={ "anduril-sandbox-authorization": f"Bearer {sandboxes_token}" }
)

# Type URL of the Objective task defined in your .proto file.
OBJECTIVE_TYPE = "type.googleapis.com/org.example.reconnaissance.v1beta.Objective"

def parse_objective(specification):
    """Parse the Objective carried in the task's specification.

    Over REST, the specification is a google.protobuf.Any represented as JSON:
    a `type` URL alongside the task's flattened fields.
    """
    if not specification or specification.type != OBJECTIVE_TYPE:
        print(f"Unsupported task type: {getattr(specification, 'type', None)}")
        return

    # Objective is a oneof: read whichever target the operator set.
    extra = specification.dict()
    if extra.get("entityId"):
        print(f"  Objective: entity {extra['entityId']}")
    elif extra.get("lla"):
        lla = extra["lla"]
        print(
            f"  Objective: LLA (lat {lla.get('latitudeDegrees')}, "
            f"lon {lla.get('longitudeDegrees')}, "
            f"alt {lla.get('altitudeHaeM')}m)"
        )
    else:
        print("  Objective: no target set")

async def start_task(task_id, task_status_version, entity_id):
    try:
        task_status_version += 1
        response = await client.tasks.update_task_status(
            task_id=task_id,
            status_version=task_status_version,
            new_status=TaskStatus(
                status="STATUS_EXECUTING"
            ),
            author=Principal(
                system=System(
                    entity_id=entity_id
                )
            )
        )
        return response
    except Exception as error:
        print(f"Encountered the following error while starting task: {error}")

async def main(entity_id):
    try:
        print(f"Streaming tasks for entity: {entity_id}...")
        # Set heartbeat_interval_ms so Lattice sends periodic heartbeats; a missing
        # heartbeat signals a dropped connection.
        requests = client.tasks.stream_as_agent(
            agent_selector=EntityIdsSelector(
                entity_ids=[entity_id]
            ),
            heartbeat_interval_ms=30000
        )

        async for request in requests:
            if request.event == "heartbeat":
                print(f"Heartbeat: {request.timestamp}")
                continue

            if request.execute_request and request.execute_request.task and request.execute_request.task.version:
                version = request.execute_request.task.version
                task_id = version.task_id
                task_status_version = version.status_version
                task_description = request.execute_request.task.description

                print(f"Starting task {task_id}, version {task_status_version}: {task_description}")
                # Parse the Objective the operator sent with the task.
                parse_objective(request.execute_request.task.specification)

                result = await start_task(task_id, task_status_version, entity_id)
                if result and result.version:
                    print(f"Started task with status version: {result.version.status_version}.")

    except Exception as error:
        print(f"Exception: {error}")

if __name__ == "__main__":
    raise SystemExit(asyncio.run(main(entity_id="<AGENT_ID>")))
```

```ts title={"Typescript (REST)"} startLine={78} maxLines={18}
import { LatticeClient, Lattice } from "@anduril-industries/lattice-sdk";

const latticeEndpoint = process.env.LATTICE_ENDPOINT;
const environmentToken = process.env.ENVIRONMENT_TOKEN;
// Remove sandboxesToken from the following statements if you are not developing on Sandboxes.
const sandboxesToken = process.env.SANDBOXES_TOKEN;
if (!latticeEndpoint || !environmentToken || !sandboxesToken) {
    console.log('Missing required environment variables.');
    process.exit(1);
}
const client = new LatticeClient(
    {
        baseUrl: `https://${latticeEndpoint}`,
        token: environmentToken,
        // Remove the following if you are not developing on Sandboxes.
        headers: {  "Anduril-Sandbox-Authorization": `Bearer ${sandboxesToken}`  }
    }
);

// Type URL of the Objective task defined in objective.proto.
const OBJECTIVE_TYPE = "type.googleapis.com/org.example.reconnaissance.v1beta.Objective";

// Parse the Objective carried in the task's specification.
// Over REST, the specification is a google.protobuf.Any represented as JSON:
// an `@type` URL alongside the task's flattened fields.
function parseObjective(specification?: Lattice.GoogleProtobufAny) {
    if (specification?.["@type"] !== OBJECTIVE_TYPE) {
        console.log(`Unsupported task type: ${specification?.["@type"]}`);
        return;
    }

    // Objective is a oneof: read whichever target the operator set.
    if (specification.entityId) {
        console.log(`  Objective: entity ${specification.entityId}`);
    } else if (specification.lla) {
        const lla = specification.lla;
        console.log(`  Objective: LLA (lat ${lla.latitudeDegrees}, lon ${lla.longitudeDegrees}, alt ${lla.altitudeHaeM}m)`);
    } else {
        console.log("  Objective: no target set");
    }
}

async function startTask(taskId: string, statusVersion: number, entityId: string) {
    try {
        const response = await client.tasks.updateTaskStatus(
            {
                taskId,
                // Increment the task status version.
                statusVersion: statusVersion + 1,
                author: {
                    system: {
                        entityId: entityId
                    }
                },
                // Indicate that the agent has started the task.
                newStatus: {
                    status: "STATUS_EXECUTING"
                }
            }
        )
        return response;
    } catch (error) {
        console.log(`Encountered the following error while starting task: ${error}`);
    }
}

async function App(entityId: string) {
    try {
        console.log(`Streaming tasks for entity ${entityId}...`);
        // Set heartbeatIntervalMs so Lattice sends periodic heartbeats; a missing
        // heartbeat signals a dropped connection.
        const requests = await client.tasks.streamAsAgent(
            {
                agentSelector: {
                    entityIds: [entityId]
                },
                heartbeatIntervalMs: 30000
            }
        );

        for await (const request of requests) {
            if (request.event === "heartbeat") {
                console.log(`Heartbeat: ${request?.timestamp}`);
                continue;
            }

            if (request.executeRequest) {
                const task = request.executeRequest?.task;
                const taskId = task?.version?.taskId;
                const taskStatusVersion = task?.version?.statusVersion;
                const taskDescription = task?.description;
                if (!taskId || taskStatusVersion == null) continue;

                console.log(`Starting task ${taskId}, version ${taskStatusVersion}: ${taskDescription}`);
                // Parse the Objective the operator sent with the task.
                parseObjective(task?.specification);

                const result = await startTask(taskId, taskStatusVersion, entityId);
                console.log(`Started task ${taskId}, with status version number: ${result?.version?.statusVersion}.`);

            } else if (request.completeRequest) {
                console.log(`Completing task: ${request.completeRequest.taskId}`);
            } else if (request.cancelRequest) {
                console.log(`Cancelling task: ${request.cancelRequest.taskId}`);
            }
        }
    } catch (error) {
        console.log(`Encountered the following error while processing task: ${error}`);
    }
}

// Replace with the ID of entity listening for a task
const entityId = '<AGENT_ID>';
App(entityId);
```

```rs title={"Rust (REST)"} startLine={108} maxLines={26}
use anduril_lattice_sdk::prelude::*;

use std::env;

// Type URL of the Objective task defined in your .proto file.
const OBJECTIVE_TYPE: &str = "type.googleapis.com/org.example.package.version.Task";

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let lattice_endpoint = env::var("LATTICE_ENDPOINT")
        .expect("LATTICE_ENDPOINT environment variable not set");
    let environment_token = env::var("ENVIRONMENT_TOKEN")
        .expect("ENVIRONMENT_TOKEN environment variable not set");
    // Remove sandboxes_token if you are not developing on Sandboxes.
    let sandboxes_token = env::var("SANDBOXES_TOKEN")
        .expect("SANDBOXES_TOKEN environment variable not set");

    if environment_token.is_empty() || lattice_endpoint.is_empty() || sandboxes_token.is_empty() {
        eprintln!("Missing required environment variables:");
        eprintln!("  LATTICE_ENDPOINT");
        eprintln!("  ENVIRONMENT_TOKEN");
        eprintln!("  SANDBOXES_TOKEN");
        std::process::exit(1);
    }

    let config = ClientConfig {
        base_url: format!("https://{}", lattice_endpoint),
        token: Some(environment_token),
        // Remove custom_headers if you are not developing on Sandboxes.
        custom_headers: HashMap::from([(
            "anduril-sandbox-authorization".to_string(),
            format!("Bearer {}", sandboxes_token),
        )]),
        ..Default::default()
    };
    let client = Lattice::new(config)?;

    // Replace with the entity ID of the agent to stream tasks for.
    let entity_id = "<AGENT_ID>";
    run(&client, entity_id).await;

    Ok(())
}

/// Parse the Objective carried in the task's specification.
///
/// Over REST, the specification is a google.protobuf.Any represented as JSON: a
/// `type` URL alongside the task's flattened fields (captured in `extra`).
fn parse_objective(specification: Option<&GoogleProtobufAny>) {
    let spec = match specification {
        Some(spec) if spec.r#type.as_deref() == Some(OBJECTIVE_TYPE) => spec,
        other => {
            println!(
                "Unsupported task type: {:?}",
                other.and_then(|s| s.r#type.as_deref())
            );
            return;
        }
    };

    // Objective is a oneof: read whichever target the operator set.
    if let Some(entity_id) = spec.extra.get("entityId").and_then(|v| v.as_str()) {
        println!("  Objective: entity {}", entity_id);
    } else if let Some(lla) = spec.extra.get("lla") {
        println!(
            "  Objective: LLA (lat {}, lon {}, alt {}m)",
            lla.get("latitudeDegrees").unwrap_or(&Value::Null),
            lla.get("longitudeDegrees").unwrap_or(&Value::Null),
            lla.get("altitudeHaeM").unwrap_or(&Value::Null),
        );
    } else {
        println!("  Objective: no target set");
    }
}

/// Move a task into the executing state by advancing its status version.
async fn start_task(
    client: &Lattice,
    task_id: &str,
    task_status_version: i64,
    entity_id: &str,
) -> Option<Task> {
    let request = TaskStatusUpdate {
        status_version: Some(task_status_version + 1),
        new_status: Some(TaskStatus {
            status: Some(TaskStatusStatus::StatusExecuting),
            ..Default::default()
        }),
        author: Some(Principal {
            system: Some(System {
                entity_id: Some(entity_id.to_string()),
                ..Default::default()
            }),
            ..Default::default()
        }),
    };

    match client.tasks.update_task_status(task_id, &request, None).await {
        Ok(task) => Some(task),
        Err(error) => {
            eprintln!("Encountered the following error while starting task: {}", error);
            None
        }
    }
}

/// Stream tasks for the agent and start each newly delivered task.
async fn run(client: &Lattice, entity_id: &str) {
    println!("Streaming tasks for entity: {}...", entity_id);

    // Set heartbeat_interval_ms so Lattice sends periodic heartbeats; a missing
    // heartbeat signals a dropped connection.
    let request = AgentStreamRequest {
        agent_selector: Some(EntityIdsSelector {
            entity_ids: Some(vec![entity_id.to_string()]),
            ..Default::default()
        }),
        heartbeat_interval_ms: Some(30000),
    };

    let stream = match client.tasks.stream_as_agent(&request, None).await {
        Ok(stream) => stream,
        Err(error) => {
            eprintln!("Exception: {}", error);
            return;
        }
    };
    let mut stream = std::pin::pin!(stream);

    while let Some(result) = stream.next().await {
        match result {
            Ok(StreamAsAgentResponse::Heartbeat { data, .. }) => {
                println!("Heartbeat: {:?}", data.heartbeat_object_fields.timestamp);
            }
            Ok(StreamAsAgentResponse::AgentRequest { data, .. }) => {
                let fields = &data.agent_task_request_fields;
                if let Some(execute) = &fields.execute_request {
                    if let Some(task) = &execute.task {
                        if let Some(version) = &task.version {
                            let task_id = version.task_id.clone().unwrap_or_default();
                            let task_status_version = version.status_version.unwrap_or(0);
                            println!(
                                "Starting task {}, version {}: {:?}",
                                task_id, task_status_version, task.description
                            );
                            // Parse the Objective the operator sent with the task.
                            parse_objective(task.specification.as_ref());

                            if let Some(result) =
                                start_task(client, &task_id, task_status_version, entity_id).await
                            {
                                if let Some(version) = result.version {
                                    println!(
                                        "Started task with status version: {:?}.",
                                        version.status_version
                                    );
                                }
                            }
                        }
                    }
                }
            }
            Ok(other) => println!("Event: {:?}", other),
            Err(error) => eprintln!("Exception: {}", error),
        }
    }
}

```

```go title={"Go (gRPC)"} startLine={105} maxLines={26}
// This Go example is compatible with artifacts generated using
// the following grpc/go plugin: https://buf.build/anduril/lattice-sdk/sdks/main:grpc/go
package main

import (
	"context"
	"fmt"
	"io"
	"log"
	"os"
	"time"

	"schema-registry.developer.anduril.com/gen/go/anduril/lattice-sdk/grpc/go/anduril/taskmanager/v1/taskmanagerv1grpc"
	taskmanagerv1 "schema-registry.developer.anduril.com/gen/go/anduril/lattice-sdk/protocolbuffers/go/anduril/taskmanager/v1"
	//Your custom task from schema-registry.developer.anduril.com
	reconnaissancev1beta "schema-registry.developer.anduril.com/gen/go/org/example/reconnaissance/protocolbuffers/go/org/example/reconnaissance/v1beta"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"
	"google.golang.org/protobuf/types/known/anypb"
)

// parseObjective parses the Objective carried in the task's specification.
// Over gRPC, the specification is a google.protobuf.Any; unmarshal it into the
// generated Objective message.
func parseObjective(specification *anypb.Any) {
	objective := &reconnaissancev1beta.Objective{}
	if err := specification.UnmarshalTo(objective); err != nil {
		fmt.Printf("Unsupported task type: %s\n", specification.GetTypeUrl())
		return
	}

	// Objective is a oneof: read whichever target the operator set.
	switch target := objective.GetTarget().(type) {
	case *reconnaissancev1beta.Objective_EntityId:
		fmt.Printf("  Objective: entity %s\n", target.EntityId)
	case *reconnaissancev1beta.Objective_Lla:
		lla := target.Lla
		fmt.Printf("  Objective: LLA (lat %v, lon %v, alt %vm)\n",
			lla.GetLatitudeDegrees(), lla.GetLongitudeDegrees(), lla.GetAltitudeHaeM())
	default:
		fmt.Println("  Objective: no target set")
	}
}

func main() {
	environmentToken := os.Getenv("ENVIRONMENT_TOKEN")
	latticeEndpoint := os.Getenv("LATTICE_ENDPOINT")
	sandboxesToken := os.Getenv("SANDBOXES_TOKEN")

	if latticeEndpoint == "" || environmentToken == "" || sandboxesToken == "" {
		fmt.Println("Missing required environment variables")
		os.Exit(1)
	}

	// Setup authentication
	auth := &BearerTokenAuth{
		Token:          environmentToken,
		SandboxesToken: sandboxesToken,
	}

	opts := []grpc.DialOption{
		grpc.WithTransportCredentials(credentials.NewClientTLSFromCert(nil, "")),
		grpc.WithPerRPCCredentials(auth),
	}

	conn, err := grpc.NewClient(latticeEndpoint, opts...)
	if err != nil {
		log.Fatalf("Did not connect: %v", err)
	}
	defer conn.Close()

	client := taskmanagerv1grpc.NewTaskManagerAPIClient(conn)

	// Set the entity ID to listen for tasks
	entityId := "<ENTITY_ID>"
	fmt.Printf("Listening for tasks for entity %s...\n", entityId)

	for {
		try := func() bool {
			ctx := context.Background()

			// Create the ListenAsAgent request. Set HeartbeatIntervalMs so Lattice
			// sends periodic heartbeats; a missing heartbeat signals a dropped connection.
			listenRequest := &taskmanagerv1.ListenAsAgentRequest{
				AgentSelector: &taskmanagerv1.ListenAsAgentRequest_EntityIds{
					EntityIds: &taskmanagerv1.EntityIds{
						EntityIds: []string{entityId},
					},
				},
				HeartbeatIntervalMs: 30000,
			}

			// Call the ListenAsAgent method to get a stream of task requests
			stream, err := client.ListenAsAgent(ctx, listenRequest)
			if err != nil {
				fmt.Printf("Error starting task listener: %v\n", err)
				return false
			}

			for {
				response, err := stream.Recv()
				if err == io.EOF {
					fmt.Println("Stream ended")
					return false
				}
				if err != nil {
					fmt.Printf("Error receiving from stream: %v\n", err)
					return false
				}

				// A heartbeat confirms the connection is alive. It carries only a
				// timestamp, so skip it rather than treating it as a task request.
				if response.Heartbeat != nil {
					log.Printf("Heartbeat: %s", response.Heartbeat.Timestamp.AsTime())
					continue
				}

				// Process the response based on the type of task request
				switch x := response.Request.(type) {
				case *taskmanagerv1.ListenAsAgentResponse_ExecuteRequest:
					if x.ExecuteRequest != nil && x.ExecuteRequest.Task != nil &&
						x.ExecuteRequest.Task.Version != nil && x.ExecuteRequest.Task.Version.TaskId != "" {
						// Extract task-specific information
						taskId := x.ExecuteRequest.Task.Version.TaskId
						taskStatusVersion := x.ExecuteRequest.Task.Version.StatusVersion

						fmt.Printf("Starting task %s, version %d\n", taskId, taskStatusVersion)

						// Update task status to STATUS_EXECUTING
						result, err := startTask(ctx, client, taskId, taskStatusVersion, entityId)
						if err != nil {
							fmt.Printf("Error starting task: %v\n", err)
							return false
						}

						fmt.Printf("Started task with status version: %d\n", result.StatusVersion)
					}

				case *taskmanagerv1.ListenAsAgentResponse_CompleteRequest:
					if x.CompleteRequest != nil && x.CompleteRequest.TaskId != "" {
						fmt.Printf("Completing task: %s\n", x.CompleteRequest.TaskId)
					}

				case *taskmanagerv1.ListenAsAgentResponse_CancelRequest:
					if x.CancelRequest != nil && x.CancelRequest.TaskId != "" {
						fmt.Printf("Cancelling task: %s\n", x.CancelRequest.TaskId)
					}
				}
			}
		}()

		if !try {
			// If there was an error, wait a bit before retrying
			time.Sleep(1 * time.Second)
		}
	}
}

// startTask updates the task status to STATUS_EXECUTING
func startTask(ctx context.Context, client taskmanagerv1grpc.TaskManagerAPIClient, taskId string, taskStatusVersion uint32, agentEntityId string) (*taskmanagerv1.TaskVersion, error) {
	// Increment status version for the update
	taskStatusVersion++

	// Create system principal with the agent entity ID
	principal := &taskmanagerv1.Principal{
		Agent: &taskmanagerv1.Principal_System{
			System: &taskmanagerv1.System{
				EntityId: agentEntityId,
			},
		},
	}

	// Create task status update
	taskStatus := &taskmanagerv1.TaskStatus{
		Status: taskmanagerv1.Status_STATUS_EXECUTING,
	}

	// Create status update request
	statusUpdate := &taskmanagerv1.StatusUpdate{
		Version: &taskmanagerv1.TaskVersion{
			TaskId:            taskId,
			DefinitionVersion: 0,
			StatusVersion:     taskStatusVersion,
		},
		Status: taskStatus,
		Author: principal,
	}

	// Create the update status request
	updateRequest := &taskmanagerv1.UpdateStatusRequest{
		StatusUpdate: statusUpdate,
	}

	// Call the UpdateStatus API
	response, err := client.UpdateStatus(ctx, updateRequest)
	if err != nil {
		return nil, fmt.Errorf("error updating task status: %w", err)
	}

	return response.Task.Version, nil
}

```

```py title={"Python (gRPC)"} startLine={98} maxLines={19}
# This Python example is compatible with artifacts generated using the following
# Buf plugins: https://schema-registry.developer.anduril.com/anduril/lattice-sdk/sdks/main:bufbuild/py
# and https://schema-registry.developer.anduril.com/anduril/lattice-sdk/sdks/main:connectrpc/py

import os
import sys
import time
from bearer_auth import create_client
from protobuf import Oneof

from anduril.taskmanager.v1.task_pub_pb import (
    Principal,
    Status,
    System,
    TaskStatus,
    TaskVersion,
    StatusUpdate,
)
from anduril.taskmanager.v1.task_manager_api_pub_pb import (
    EntityIds,
    ListenAsAgentRequest,
    UpdateStatusRequest,
)
from anduril.taskmanager.v1.task_manager_api_pub_connect import TaskManagerAPIClientSync

# Your custom task from schema-registry.developer.anduril.com, generated with the
# same bufbuild/py plugin as the Lattice SDK.
from org.example.reconnaissance.v1beta.objective_pb import Objective


def parse_objective(specification):
    """Parse the Objective carried in the task's specification.

    Over gRPC, the specification is a google.protobuf.Any. Check the type
    before unpacking it into the generated Objective message.
    """
    if specification is None:
        print("Task has no specification")
        return

    # Over gRPC the specification is a protobuf Any; unpack returns None when it
    # does not carry an Objective.
    objective = specification.unpack(Objective)
    if objective is None:
        print(f"Unsupported task type: {specification.type_url}")
        return

    # Objective is a oneof: read whichever target the operator set.
    target = objective.target
    if target.field == "entity_id":
        print(f"  Objective: entity {target.value}")
    elif target.field == "lla":
        lla = target.value
        print(
            f"  Objective: LLA (lat {lla.latitude_degrees}, "
            f"lon {lla.longitude_degrees}, alt {lla.altitude_hae_m}m)"
        )
    else:
        print("  Objective: no target set")


def start_task(client, task_id, task_status_version, agent_entity_id):
    """Update the task status to STATUS_EXECUTING."""
    task_status_version += 1

    principal = Principal(agent=Oneof("system", System(entity_id=agent_entity_id)))
    task_status = TaskStatus(status=Status.EXECUTING)

    status_update = StatusUpdate(
        version=TaskVersion(task_id=task_id, definition_version=0, status_version=task_status_version),
        status=task_status,
        author=principal,
    )

    response = client.update_status(UpdateStatusRequest(status_update=status_update))
    return response.task.version


def main():
    environment_token = os.getenv("ENVIRONMENT_TOKEN")
    lattice_endpoint = os.getenv("LATTICE_ENDPOINT")
    sandboxes_token = os.getenv("SANDBOXES_TOKEN")

    if not environment_token or not lattice_endpoint or not sandboxes_token:
        print("Missing required environment variables", file=sys.stderr)
        sys.exit(1)

    # Create the API client over Connect gRPC with authentication
    client = create_client(TaskManagerAPIClientSync, lattice_endpoint, environment_token, sandboxes_token)

    entity_id = "<ENTITY_ID>"
    print(f"Listening for tasks for entity {entity_id}...")

    while True:
        try:
            # Set heartbeat_interval_ms so Lattice sends periodic heartbeats; a
            # missing heartbeat signals a dropped connection.
            stream = client.listen_as_agent(
                ListenAsAgentRequest(
                    agent_selector=Oneof("entity_ids", EntityIds(entity_ids=[entity_id])),
                    heartbeat_interval_ms=30000,
                )
            )

            for response in stream:
                # A heartbeat confirms the connection is alive. It carries only a
                # timestamp, so skip it rather than treating it as a task request.
                if response.has_field("heartbeat"):
                    print(f"Heartbeat: {response.heartbeat.timestamp.to_datetime()}")
                    continue

                # The remaining responses carry a `request` oneof; inspect which
                # arm is populated.
                request = response.request
                if request is None:
                    continue
                if request.field == "execute_request":
                    task = request.value.task
                    if task and task.version and task.version.task_id:
                        task_id = task.version.task_id
                        task_status_version = task.version.status_version
                        print(f"Starting task {task_id}, version {task_status_version}")

                        # Parse the Objective the operator sent with the task.
                        parse_objective(task.specification)

                        try:
                            result = start_task(client, task_id, task_status_version, entity_id)
                            print(f"Started task with status version: {result.status_version}")
                        except Exception as error:
                            print(f"Error starting task: {error}", file=sys.stderr)
                            break
                elif request.field == "complete_request":
                    if request.value.task_id:
                        print(f"Completing task: {request.value.task_id}")
                elif request.field == "cancel_request":
                    if request.value.task_id:
                        print(f"Cancelling task: {request.value.task_id}")
        except Exception as error:
            print(f"Error in stream: {error}", file=sys.stderr)

        # If the stream ended or errored, wait a bit before retrying
        time.sleep(1)


if __name__ == "__main__":
    main()

```

```rs title={"Rust (gRPC)"} startLine={127} maxLines={20}
// This Rust example is compatible with artifacts generated using
// the following grpc/rust plugin: https://buf.build/anduril/lattice-sdk/sdks/main:community/neoeinstein-tonic
mod bearer_auth;

use anduril_lattice_sdk_community_neoeinstein_prost::anduril::taskmanager::v1::{
    listen_as_agent_request, listen_as_agent_response, principal, EntityIds, ListenAsAgentRequest,
    Principal, Status, StatusUpdate, System, TaskStatus, TaskVersion, UpdateStatusRequest,
};
use anduril_lattice_sdk_community_neoeinstein_tonic::anduril::taskmanager::v1::tonic::task_manager_api_client::TaskManagerApiClient;
//Your custom task from schema-registry.developer.anduril.com
use org_example_reconnaissance_community_neoeinstein_prost::org::example::reconnaissance::v1beta::{
    objective, Objective,
};
use bearer_auth::EnvironmentTokenAuth;
use prost::Message;
use tonic::metadata::MetadataValue;
use tonic::transport::{Channel, ClientTlsConfig};
use tonic::Request;

use std::env;
use std::time::Duration;

// Type URL of the Objective task defined in objective.proto.
const OBJECTIVE_TYPE: &str =
    "type.googleapis.com/org.example.reconnaissance.v1beta.Objective";

// Parse the Objective carried in the task's specification. Over gRPC, the
// specification is a google.protobuf.Any; decode its `value` bytes into the
// generated Objective message after checking the type URL.
fn parse_objective(specification: &prost_types::Any) {
    if specification.type_url != OBJECTIVE_TYPE {
        eprintln!("Unsupported task type: {}", specification.type_url);
        return;
    }

    match Objective::decode(&specification.value[..]) {
        Ok(objective) => {
            // Objective is a oneof: read whichever target the operator set.
            match objective.target {
                Some(objective::Target::EntityId(entity_id)) => {
                    println!("  Objective: entity {}", entity_id);
                }
                Some(objective::Target::Lla(lla)) => {
                    println!(
                        "  Objective: LLA (lat {}, lon {}, alt {}m)",
                        lla.latitude_degrees, lla.longitude_degrees, lla.altitude_hae_m
                    );
                }
                None => println!("  Objective: no target set"),
            }
        }
        Err(e) => eprintln!("Failed to decode Objective: {}", e),
    }
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let environment_token = env::var("ENVIRONMENT_TOKEN")
        .expect("ENVIRONMENT_TOKEN environment variable not set");
    let lattice_endpoint = env::var("LATTICE_ENDPOINT")
        .expect("LATTICE_ENDPOINT environment variable not set");
    let sandboxes_token = env::var("SANDBOXES_TOKEN")
        .expect("SANDBOXES_TOKEN environment variable not set");

    let auth = EnvironmentTokenAuth::new(
        environment_token,
        sandboxes_token,
    );

    let tls_config = ClientTlsConfig::new().with_native_roots();
    let channel = Channel::from_shared(format!("https://{}", lattice_endpoint))?
        .tls_config(tls_config)?
        .connect()
        .await?;

    let access_token = auth.get_token().await?;
    let sandboxes_token = auth.sandboxes_token().to_string();
    let auth_header: MetadataValue<_> = format!("Bearer {}", access_token).parse()?;
    let sandbox_header: MetadataValue<_> = format!("Bearer {}", sandboxes_token).parse()?;

    let mut client = TaskManagerApiClient::with_interceptor(
        channel,
        move |mut req: Request<()>| {
            req.metadata_mut()
                .insert("authorization", auth_header.clone());
            req.metadata_mut()
                .insert("anduril-sandbox-authorization", sandbox_header.clone());
            Ok(req)
        },
    );

    // Set the entity ID to listen for tasks
    let entity_id = "<ENTITY_ID>".to_string();
    println!("Listening for tasks for entity {}...", entity_id);

    // Reconnect loop: open a fresh ListenAsAgent stream if the previous one
    // disconnects or errors.
    loop {
        // Set heartbeat_interval_ms so Lattice sends periodic heartbeats; a
        // missing heartbeat signals a dropped connection.
        let request = Request::new(ListenAsAgentRequest {
            agent_selector: Some(listen_as_agent_request::AgentSelector::EntityIds(EntityIds {
                entity_ids: vec![entity_id.clone()],
            })),
            heartbeat_interval_ms: 30000,
            ..Default::default()
        });

        let mut stream = match client.listen_as_agent(request).await {
            Ok(resp) => resp.into_inner(),
            Err(e) => {
                eprintln!("Error starting task listener: {}", e);
                tokio::time::sleep(Duration::from_secs(1)).await;
                continue;
            }
        };

        loop {
            let response = match stream.message().await {
                Ok(Some(r)) => r,
                Ok(None) => {
                    println!("Stream ended");
                    break;
                }
                Err(e) => {
                    eprintln!("Error receiving from stream: {}", e);
                    break;
                }
            };

            // A heartbeat confirms the connection is alive. It carries only a
            // timestamp, so skip it rather than treating it as a task request.
            if let Some(heartbeat) = &response.heartbeat {
                println!("Heartbeat: {:?}", heartbeat.timestamp);
                continue;
            }

            // Process the response based on the type of task request
            match response.request {
                Some(listen_as_agent_response::Request::ExecuteRequest(execute)) => {
                    if let Some(task) = execute.task {
                        if let Some(version) = task.version {
                            let task_id = version.task_id.clone();
                            let task_status_version = version.status_version;
                            println!(
                                "Starting task {}, version {}",
                                task_id, task_status_version
                            );

                            // Parse the Objective the operator sent with the task.
                            if let Some(specification) = &task.specification {
                                parse_objective(specification);
                            }

                            // Create status update request
                            let status_update = StatusUpdate {
                                version: Some(TaskVersion {
                                    task_id: task_id.clone(),
                                    // Increment status version for the update
                                    status_version: task_status_version + 1,
                                    definition_version: 0,
                                    ..Default::default()
                                }),
                                status: Some(TaskStatus {
                                    status: Status::Executing as i32,
                                    ..Default::default()
                                }),
                                author: Some(Principal {
                                    agent: Some(principal::Agent::System(System {
                                        entity_id: entity_id.clone(),
                                        ..Default::default()
                                    })),
                                    ..Default::default()
                                }),
                                ..Default::default()
                            };

                            // Call the UpdateStatus API
                            match client
                                .update_status(Request::new(UpdateStatusRequest {
                                    status_update: Some(status_update),
                                }))
                                .await
                            {
                                Ok(resp) => {
                                    if let Some(v) =
                                        resp.into_inner().task.and_then(|t| t.version)
                                    {
                                        println!(
                                            "Started task with status version: {}",
                                            v.status_version
                                        );
                                    }
                                }
                                Err(e) => eprintln!("Error starting task: {}", e),
                            }
                        }
                    }
                }
                Some(listen_as_agent_response::Request::CompleteRequest(complete)) => {
                    if !complete.task_id.is_empty() {
                        println!("Completing task: {}", complete.task_id);
                    }
                }
                Some(listen_as_agent_response::Request::CancelRequest(cancel)) => {
                    if !cancel.task_id.is_empty() {
                        println!("Cancelling task: {}", cancel.task_id);
                    }
                }
                None => {}
            }
        }

        // If there was an error, wait a bit before retrying
        tokio::time::sleep(Duration::from_secs(1)).await;
    }
}

```

If successful, you see the following output:

```bash
Listening for tasks for entity with ID <entity-id>...
```

> **Note**
>
> An agent should also handle stream interruptions gracefully. If the connection to Lattice
> fails, the stream closes. Handle stream errors and reconnect so that your agent keeps its
> subscription to task updates through transient network issues.
>
> For more information, see [Retry connections](/guides/best-practices/retry-connections).

This example reports `STATUS_EXECUTING` as soon as a task arrives, but an agent isn't obligated
to accept every task.

Before executing the task, validate that the agent can actually perform the task --
for example, that the requested parameters are within its capabilities. If it can't, the agent
can reject the task by reporting `STATUS_DONE_NOT_OK` with a
[`TaskError`](/reference/rest/tasks/update-task-status#request.body.newStatus.taskError) of
`ERROR_CODE_REJECTED` instead of starting execution.

#### Parse the task specification

Decode the task's `specification` to read the `Objective` the operator sent.
Over REST, the specification is delivered as JSON -- a `type` URL alongside the task's flattened
fields. Over gRPC, it's a protobuf `Any` that you unpack into the `Objective` message
generated from your message. Check the type URL before reading the fields, since an
agent's catalog can advertise more than one task type. Because `Objective` is a `oneof`,
inspect which target is set -- an `entity_id` or an `lla` point:

```go title={"Go (REST)"} startLine={18} maxLines={20}
package main

import (
	"context"
	"errors"
	"fmt"
	"io"
	"log"
	"net/http"
	"os"
	"time"

	Lattice "github.com/anduril/lattice-sdk-go/v5"
	"github.com/anduril/lattice-sdk-go/v5/client"
	"github.com/anduril/lattice-sdk-go/v5/option"
)

// Type URL of the Objective task defined in objective.proto.
const objectiveType = "type.googleapis.com/org.example.reconnaissance.v1beta.Objective"

// parseObjective parses the Objective carried in the task's specification. Over
// REST, the specification is a google.protobuf.Any represented as JSON: a Type
// URL alongside the task's flattened fields, which the SDK surfaces as
// ExtraProperties.
func parseObjective(specification *Lattice.GoogleProtobufAny) {
	if specification == nil || specification.Type == nil || *specification.Type != objectiveType {
		log.Printf("Unsupported task type: %v", specification.GetType())
		return
	}

	// Objective is a oneof: read whichever target the operator set.
	fields := specification.ExtraProperties
	if entityID, ok := fields["entityId"].(string); ok && entityID != "" {
		log.Printf("  Objective: entity %s", entityID)
	} else if lla, ok := fields["lla"].(map[string]interface{}); ok {
		log.Printf("  Objective: LLA (lat %v, lon %v, alt %vm)",
			lla["latitudeDegrees"], lla["longitudeDegrees"], lla["altitudeHaeM"])
	} else {
		log.Printf("  Objective: no target set")
	}
}

func main() {
	// Get environment variables
	latticeEndpoint := os.Getenv("LATTICE_ENDPOINT")
	environmentToken := os.Getenv("ENVIRONMENT_TOKEN")
	sandboxesToken := os.Getenv("SANDBOXES_TOKEN")

	// Check required environment variables
	if latticeEndpoint == "" || environmentToken == "" || sandboxesToken == "" {
		fmt.Println("Missing required environment variables")
		os.Exit(1)
	}

	// Initialize headers for sandbox authorization
	headers := http.Header{}
	headers.Add("Anduril-Sandbox-Authorization", fmt.Sprintf("Bearer %s", sandboxesToken))
	// Create the client
	LatticeClient := client.NewClient(
		option.WithToken(environmentToken),
		option.WithBaseURL(fmt.Sprintf("https://%s", latticeEndpoint)),
		option.WithHTTPHeader(headers),
	)

	// Set the entity ID to listen for tasks
	entityId := "<AGENT_ID>"
	fmt.Printf("Streaming tasks for entity %s...\n", entityId)

	// Create context for the request
	ctx := context.Background()

	// Create agent stream request. Set HeartbeatIntervalMs so Lattice sends
	// periodic heartbeats; a missing heartbeat signals a dropped connection.
	heartbeatIntervalMs := 30000
	agentStreamRequest := Lattice.AgentStreamRequest{
		AgentSelector: &Lattice.EntityIDsSelector{
			EntityIDs: []string{entityId},
		},
		HeartbeatIntervalMs: &heartbeatIntervalMs,
	}

	// Stream tasks
	stream, err := LatticeClient.Tasks.StreamAsAgent(ctx, &agentStreamRequest)
	if err != nil {
		fmt.Printf("Error streaming tasks: %v\n", err)
		os.Exit(1)
	}

	// Process stream events
	for {
		select {
		case <-ctx.Done():
			log.Printf("Context canceled: %v", ctx.Err())
			return
		default:
			// Continue processing
		}

		event, err := stream.Recv()

		if errors.Is(err, io.EOF) {
			log.Println("Stream completed successfully.")
			return
		}

		if err != nil {
			log.Printf("Error receiving message: %v", err)
			continue
		}

		if event.Event == "heartbeat" {
			timestamp := *event.Heartbeat.Timestamp
			log.Printf("Heartbeat: %s", timestamp)
		} else {
			request := event.GetAgentRequest()
			if executeRequest := request.GetExecuteRequest(); executeRequest != nil {
				task := executeRequest.GetTask()
				if task != nil {
					taskId := *task.GetVersion().GetTaskID()
					taskStatusVersion := *task.GetVersion().GetStatusVersion()
					description := *task.GetDescription()

					log.Printf("Starting task %s, version %d: %s", taskId, taskStatusVersion, description)

					// Parse the Objective the operator sent with the task.
					parseObjective(task.GetSpecification())

					// Update task status to STATUS_EXECUTING
					result, err := startTask(ctx, LatticeClient, taskId, int(taskStatusVersion), entityId)
					if err != nil {
						log.Printf("Error starting task: %v", err)
						continue
					}

					log.Printf("Started task with status version: %d", *result.StatusVersion)
				}
			} else if completeRequest := request.GetCompleteRequest(); completeRequest != nil {
				taskToComplete := completeRequest.GetTaskID()
				if taskToComplete != nil {
					log.Printf("Completing task: %s", *taskToComplete)
				}
			} else if cancelRequest := request.GetCancelRequest(); cancelRequest != nil {
				taskToCancel := cancelRequest.GetTaskID()
				if taskToCancel != nil {
					log.Printf("Cancelling task: %s", *taskToCancel)
				}
			}
		}

		// Sleep briefly to prevent tight looping
		time.Sleep(100 * time.Millisecond)
	}
}

// startTask updates the task status to STATUS_EXECUTING
func startTask(ctx context.Context, client *client.Client, taskId string, taskStatusVersion int, agentEntityId string) (*Lattice.TaskVersion, error) {
	// Increment status version for the update
	taskStatusVersion++

	// Create system principal with the agent entity ID
	principal := Lattice.Principal{
		System: &Lattice.System{
			EntityID: &agentEntityId,
		},
	}

	taskStatus := Lattice.TaskStatus{
		Status: Lattice.TaskStatusStatusStatusExecuting.Ptr(),
	}

	// Create task status update request
	taskStatusUpdate := Lattice.TaskStatusUpdate{
		TaskID:        taskId,
		StatusVersion: &taskStatusVersion,
		NewStatus:     &taskStatus,
		Author:        &principal,
	}

	// Call the UpdateTaskStatus API
	task, err := client.Tasks.UpdateTaskStatus(ctx, &taskStatusUpdate)
	if err != nil {
		return nil, fmt.Errorf("error updating task status: %w", err)
	}

	return task.Version, nil
}

```

```java title={"Java (REST)"} startLine={29} maxLines={22}
package org.example;

import java.util.Arrays;
import java.util.Map;
import java.util.concurrent.CompletableFuture;

import com.anduril.AsyncLattice;
import com.anduril.resources.tasks.requests.AgentStreamRequest;
import com.anduril.resources.tasks.requests.TaskStatusUpdate;
import com.anduril.resources.tasks.types.StreamAsAgentResponse;
import com.anduril.types.AgentStreamEvent;
import com.anduril.types.EntityIdsSelector;
import com.anduril.types.GoogleProtobufAny;
import com.anduril.types.Principal;
import com.anduril.types.Task;
import com.anduril.types.TaskStatus;
import com.anduril.types.TaskStatusStatus;
import com.anduril.types.TaskVersion;

/**
 * StartTask demonstrates how to use the Lattice SDK to stream tasks assigned to an agent
 * and update task status.
 */
public class StartTask {
    // Entity ID to listen for tasks - replace with your actual entity ID.
    private static final String ENTITY_ID = "<AGENT_ID>";

    // Type URL of the Objective task defined in objective.proto.
    private static final String OBJECTIVE_TYPE =
        "type.googleapis.com/org.example.reconnaissance.v1beta.Objective";

    /**
     * Parse the Objective carried in the task's specification.
     * Over REST, the specification is a google.protobuf.Any represented as JSON:
     * a type URL alongside the task's flattened fields.
     */
    private static void parseObjective(Task task) {
        GoogleProtobufAny specification = task.getSpecification().orElse(null);
        if (specification == null || !OBJECTIVE_TYPE.equals(specification.getType().orElse(null))) {
            System.out.println("Unsupported task type");
            return;
        }

        // Objective is a oneof: read whichever target the operator set.
        Map<String, Object> fields = specification.getAdditionalProperties();
        Object entityId = fields.get("entityId");
        Object lla = fields.get("lla");
        if (entityId != null) {
            System.out.println("  Objective: entity " + entityId);
        } else if (lla instanceof Map) {
            @SuppressWarnings("unchecked")
            Map<String, Object> point = (Map<String, Object>) lla;
            System.out.println("  Objective: LLA (lat " + point.get("latitudeDegrees")
                + ", lon " + point.get("longitudeDegrees")
                + ", alt " + point.get("altitudeHaeM") + "m)");
        } else {
            System.out.println("  Objective: no target set");
        }
    }

    public static void main(String[] args) {
        String endpoint = System.getenv("LATTICE_ENDPOINT");
        String environmentToken = System.getenv("ENVIRONMENT_TOKEN");
        String sandboxesToken = System.getenv("SANDBOXES_TOKEN");

        if (endpoint == null || environmentToken == null || sandboxesToken == null) {
            System.err.println("Missing required environment variables");
            System.exit(1);
        }
        if (!endpoint.startsWith("https://")) endpoint = "https://" + endpoint;

        try {
            AsyncLattice client = AsyncLattice.builder()
                .url(endpoint)
                .token(environmentToken)
                .addHeader("Anduril-Sandbox-Authorization", "Bearer " + sandboxesToken)
                .build();

            System.out.println("Streaming tasks for: " + ENTITY_ID);

            // Create agent selector with the specified entity ID.
            EntityIdsSelector agentSelector = EntityIdsSelector.builder()
                .entityIds(Arrays.asList(ENTITY_ID))
                .build();

            // Create agent stream request. Set heartbeatIntervalMs so Lattice sends
            // periodic heartbeats; a missing heartbeat signals a dropped connection.
            AgentStreamRequest agentRequest = AgentStreamRequest.builder()
                .agentSelector(agentSelector)
                .heartbeatIntervalMs(30000)
                .build();

            // Stream tasks from the server
            client.tasks().streamAsAgent(agentRequest)
                .thenAccept(requests -> {
                    for (StreamAsAgentResponse request : requests) {
                        if (request.isHeartbeat()) {
                            System.out.println("Heartbeat: " + request.getHeartbeat().get().getTimestamp().get());
                        } else {
                            AgentStreamEvent event = request.getAgentRequest().get();
                            if (event.getExecuteRequest().isPresent()) {
                                Task task = event.getExecuteRequest().get().getTask().get();

                                String taskId = task
                                    .getVersion().get()
                                    .getTaskId().get();

                                int taskStatusVersion = task
                                    .getVersion().get()
                                    .getStatusVersion().get();

                                String description = task
                                    .getDescription().orElse("No description");

                                System.out.println("Starting task " + taskId + ", version " + taskStatusVersion + ": " + description);

                                // Parse the Objective the operator sent with the task.
                                parseObjective(task);

                                try {
                                    // Update task status.
                                    TaskVersion result = startTask(client, taskId, taskStatusVersion, ENTITY_ID).get();
                                    System.out.println("Started task with status version: " + result.getStatusVersion().get());
                                } catch (Exception e) {
                                    System.err.println("Error starting task: " + e.getMessage());
                                }
                            } else if (event.getCompleteRequest().isPresent()) {
                                System.out.println("Completing task: " + event.getCompleteRequest().get()
                                    .getTaskId().get());
                            } else if (event.getCancelRequest().isPresent()) {
                                System.out.println("Cancelling task: " + event.getCancelRequest().get()
                                    .getTaskId().get());
                            }
                        }
                    }
                })
                .exceptionally(ex -> {
                    System.err.println("Error streaming tasks: " + ex.getMessage());
                    return null;
                })
                .join();
        } catch (Exception e) {
            System.err.println("Failed to initialize: " + e.getMessage());
        }
    }

    /**
     * Update task status to STATUS_EXECUTING.
     *
     * @param client Lattice client
     * @param taskId Task ID to update
     * @param taskStatusVersion Current task status version
     * @param agentEntityId Agent entity ID performing the update
     * @return CompletableFuture containing the updated TaskVersion
     */
    private static CompletableFuture<TaskVersion> startTask(AsyncLattice client, String taskId, Integer taskStatusVersion, String agentEntityId) {
        try {
            // Create system principal with the agent entity ID.
            Principal principal = Principal.builder()
                .system(com.anduril.types.System.builder()
                    .entityId(agentEntityId)
                    .build())
                .build();

            // Create task status for update.
            TaskStatus taskStatus = TaskStatus.builder()
                .status(TaskStatusStatus.STATUS_EXECUTING)
                .build();

            // Increment status version for the update
            taskStatusVersion += 1;

            // Create task status update request.
            TaskStatusUpdate taskStatusUpdate = TaskStatusUpdate.builder()
                .statusVersion(taskStatusVersion)
                .newStatus(taskStatus)
                .author(principal)
                .build();

            return client.tasks().updateTaskStatus(taskId, taskStatusUpdate)
                .thenApply(newStatus -> newStatus.getVersion().get());
        } catch (Exception e) {
            System.err.println("Encountered the following error while starting task: " + e.getMessage());
            return CompletableFuture.failedFuture(e);
        }
    }
}

```

```py title={"Python (REST)"} startLine={23} maxLines={18}
from anduril import AsyncLattice, EntityIdsSelector, TaskStatus, Principal, System
import asyncio
import os
import sys

lattice_endpoint = os.getenv('LATTICE_ENDPOINT')
environment_token = os.getenv('ENVIRONMENT_TOKEN')

# Remove sandboxes_token from the following statements if you are not developing on Sandboxes.
sandboxes_token = os.getenv('SANDBOXES_TOKEN')
if not environment_token or not lattice_endpoint or not sandboxes_token:
    print("Missing required environment variables.")
    sys.exit(1)

client = AsyncLattice(
    base_url=f"https://{lattice_endpoint}",
    token=lambda: str(environment_token),
    # Remove the following header if you are not developing on Sandboxes.
    headers={ "anduril-sandbox-authorization": f"Bearer {sandboxes_token}" }
)

# Type URL of the Objective task defined in your .proto file.
OBJECTIVE_TYPE = "type.googleapis.com/org.example.reconnaissance.v1beta.Objective"

def parse_objective(specification):
    """Parse the Objective carried in the task's specification.

    Over REST, the specification is a google.protobuf.Any represented as JSON:
    a `type` URL alongside the task's flattened fields.
    """
    if not specification or specification.type != OBJECTIVE_TYPE:
        print(f"Unsupported task type: {getattr(specification, 'type', None)}")
        return

    # Objective is a oneof: read whichever target the operator set.
    extra = specification.dict()
    if extra.get("entityId"):
        print(f"  Objective: entity {extra['entityId']}")
    elif extra.get("lla"):
        lla = extra["lla"]
        print(
            f"  Objective: LLA (lat {lla.get('latitudeDegrees')}, "
            f"lon {lla.get('longitudeDegrees')}, "
            f"alt {lla.get('altitudeHaeM')}m)"
        )
    else:
        print("  Objective: no target set")

async def start_task(task_id, task_status_version, entity_id):
    try:
        task_status_version += 1
        response = await client.tasks.update_task_status(
            task_id=task_id,
            status_version=task_status_version,
            new_status=TaskStatus(
                status="STATUS_EXECUTING"
            ),
            author=Principal(
                system=System(
                    entity_id=entity_id
                )
            )
        )
        return response
    except Exception as error:
        print(f"Encountered the following error while starting task: {error}")

async def main(entity_id):
    try:
        print(f"Streaming tasks for entity: {entity_id}...")
        # Set heartbeat_interval_ms so Lattice sends periodic heartbeats; a missing
        # heartbeat signals a dropped connection.
        requests = client.tasks.stream_as_agent(
            agent_selector=EntityIdsSelector(
                entity_ids=[entity_id]
            ),
            heartbeat_interval_ms=30000
        )

        async for request in requests:
            if request.event == "heartbeat":
                print(f"Heartbeat: {request.timestamp}")
                continue

            if request.execute_request and request.execute_request.task and request.execute_request.task.version:
                version = request.execute_request.task.version
                task_id = version.task_id
                task_status_version = version.status_version
                task_description = request.execute_request.task.description

                print(f"Starting task {task_id}, version {task_status_version}: {task_description}")
                # Parse the Objective the operator sent with the task.
                parse_objective(request.execute_request.task.specification)

                result = await start_task(task_id, task_status_version, entity_id)
                if result and result.version:
                    print(f"Started task with status version: {result.version.status_version}.")

    except Exception as error:
        print(f"Exception: {error}")

if __name__ == "__main__":
    raise SystemExit(asyncio.run(main(entity_id="<AGENT_ID>")))
```

```ts title={"Typescript (REST)"} startLine={21} maxLines={17}
import { LatticeClient, Lattice } from "@anduril-industries/lattice-sdk";

const latticeEndpoint = process.env.LATTICE_ENDPOINT;
const environmentToken = process.env.ENVIRONMENT_TOKEN;
// Remove sandboxesToken from the following statements if you are not developing on Sandboxes.
const sandboxesToken = process.env.SANDBOXES_TOKEN;
if (!latticeEndpoint || !environmentToken || !sandboxesToken) {
    console.log('Missing required environment variables.');
    process.exit(1);
}
const client = new LatticeClient(
    {
        baseUrl: `https://${latticeEndpoint}`,
        token: environmentToken,
        // Remove the following if you are not developing on Sandboxes.
        headers: {  "Anduril-Sandbox-Authorization": `Bearer ${sandboxesToken}`  }
    }
);

// Type URL of the Objective task defined in objective.proto.
const OBJECTIVE_TYPE = "type.googleapis.com/org.example.reconnaissance.v1beta.Objective";

// Parse the Objective carried in the task's specification.
// Over REST, the specification is a google.protobuf.Any represented as JSON:
// an `@type` URL alongside the task's flattened fields.
function parseObjective(specification?: Lattice.GoogleProtobufAny) {
    if (specification?.["@type"] !== OBJECTIVE_TYPE) {
        console.log(`Unsupported task type: ${specification?.["@type"]}`);
        return;
    }

    // Objective is a oneof: read whichever target the operator set.
    if (specification.entityId) {
        console.log(`  Objective: entity ${specification.entityId}`);
    } else if (specification.lla) {
        const lla = specification.lla;
        console.log(`  Objective: LLA (lat ${lla.latitudeDegrees}, lon ${lla.longitudeDegrees}, alt ${lla.altitudeHaeM}m)`);
    } else {
        console.log("  Objective: no target set");
    }
}

async function startTask(taskId: string, statusVersion: number, entityId: string) {
    try {
        const response = await client.tasks.updateTaskStatus(
            {
                taskId,
                // Increment the task status version.
                statusVersion: statusVersion + 1,
                author: {
                    system: {
                        entityId: entityId
                    }
                },
                // Indicate that the agent has started the task.
                newStatus: {
                    status: "STATUS_EXECUTING"
                }
            }
        )
        return response;
    } catch (error) {
        console.log(`Encountered the following error while starting task: ${error}`);
    }
}

async function App(entityId: string) {
    try {
        console.log(`Streaming tasks for entity ${entityId}...`);
        // Set heartbeatIntervalMs so Lattice sends periodic heartbeats; a missing
        // heartbeat signals a dropped connection.
        const requests = await client.tasks.streamAsAgent(
            {
                agentSelector: {
                    entityIds: [entityId]
                },
                heartbeatIntervalMs: 30000
            }
        );

        for await (const request of requests) {
            if (request.event === "heartbeat") {
                console.log(`Heartbeat: ${request?.timestamp}`);
                continue;
            }

            if (request.executeRequest) {
                const task = request.executeRequest?.task;
                const taskId = task?.version?.taskId;
                const taskStatusVersion = task?.version?.statusVersion;
                const taskDescription = task?.description;
                if (!taskId || taskStatusVersion == null) continue;

                console.log(`Starting task ${taskId}, version ${taskStatusVersion}: ${taskDescription}`);
                // Parse the Objective the operator sent with the task.
                parseObjective(task?.specification);

                const result = await startTask(taskId, taskStatusVersion, entityId);
                console.log(`Started task ${taskId}, with status version number: ${result?.version?.statusVersion}.`);

            } else if (request.completeRequest) {
                console.log(`Completing task: ${request.completeRequest.taskId}`);
            } else if (request.cancelRequest) {
                console.log(`Cancelling task: ${request.cancelRequest.taskId}`);
            }
        }
    } catch (error) {
        console.log(`Encountered the following error while processing task: ${error}`);
    }
}

// Replace with the ID of entity listening for a task
const entityId = '<AGENT_ID>';
App(entityId);
```

```rs title={"Rust (REST)"} startLine={49} maxLines={26}
use anduril_lattice_sdk::prelude::*;

use std::env;

// Type URL of the Objective task defined in your .proto file.
const OBJECTIVE_TYPE: &str = "type.googleapis.com/org.example.package.version.Task";

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let lattice_endpoint = env::var("LATTICE_ENDPOINT")
        .expect("LATTICE_ENDPOINT environment variable not set");
    let environment_token = env::var("ENVIRONMENT_TOKEN")
        .expect("ENVIRONMENT_TOKEN environment variable not set");
    // Remove sandboxes_token if you are not developing on Sandboxes.
    let sandboxes_token = env::var("SANDBOXES_TOKEN")
        .expect("SANDBOXES_TOKEN environment variable not set");

    if environment_token.is_empty() || lattice_endpoint.is_empty() || sandboxes_token.is_empty() {
        eprintln!("Missing required environment variables:");
        eprintln!("  LATTICE_ENDPOINT");
        eprintln!("  ENVIRONMENT_TOKEN");
        eprintln!("  SANDBOXES_TOKEN");
        std::process::exit(1);
    }

    let config = ClientConfig {
        base_url: format!("https://{}", lattice_endpoint),
        token: Some(environment_token),
        // Remove custom_headers if you are not developing on Sandboxes.
        custom_headers: HashMap::from([(
            "anduril-sandbox-authorization".to_string(),
            format!("Bearer {}", sandboxes_token),
        )]),
        ..Default::default()
    };
    let client = Lattice::new(config)?;

    // Replace with the entity ID of the agent to stream tasks for.
    let entity_id = "<AGENT_ID>";
    run(&client, entity_id).await;

    Ok(())
}

/// Parse the Objective carried in the task's specification.
///
/// Over REST, the specification is a google.protobuf.Any represented as JSON: a
/// `type` URL alongside the task's flattened fields (captured in `extra`).
fn parse_objective(specification: Option<&GoogleProtobufAny>) {
    let spec = match specification {
        Some(spec) if spec.r#type.as_deref() == Some(OBJECTIVE_TYPE) => spec,
        other => {
            println!(
                "Unsupported task type: {:?}",
                other.and_then(|s| s.r#type.as_deref())
            );
            return;
        }
    };

    // Objective is a oneof: read whichever target the operator set.
    if let Some(entity_id) = spec.extra.get("entityId").and_then(|v| v.as_str()) {
        println!("  Objective: entity {}", entity_id);
    } else if let Some(lla) = spec.extra.get("lla") {
        println!(
            "  Objective: LLA (lat {}, lon {}, alt {}m)",
            lla.get("latitudeDegrees").unwrap_or(&Value::Null),
            lla.get("longitudeDegrees").unwrap_or(&Value::Null),
            lla.get("altitudeHaeM").unwrap_or(&Value::Null),
        );
    } else {
        println!("  Objective: no target set");
    }
}

/// Move a task into the executing state by advancing its status version.
async fn start_task(
    client: &Lattice,
    task_id: &str,
    task_status_version: i64,
    entity_id: &str,
) -> Option<Task> {
    let request = TaskStatusUpdate {
        status_version: Some(task_status_version + 1),
        new_status: Some(TaskStatus {
            status: Some(TaskStatusStatus::StatusExecuting),
            ..Default::default()
        }),
        author: Some(Principal {
            system: Some(System {
                entity_id: Some(entity_id.to_string()),
                ..Default::default()
            }),
            ..Default::default()
        }),
    };

    match client.tasks.update_task_status(task_id, &request, None).await {
        Ok(task) => Some(task),
        Err(error) => {
            eprintln!("Encountered the following error while starting task: {}", error);
            None
        }
    }
}

/// Stream tasks for the agent and start each newly delivered task.
async fn run(client: &Lattice, entity_id: &str) {
    println!("Streaming tasks for entity: {}...", entity_id);

    // Set heartbeat_interval_ms so Lattice sends periodic heartbeats; a missing
    // heartbeat signals a dropped connection.
    let request = AgentStreamRequest {
        agent_selector: Some(EntityIdsSelector {
            entity_ids: Some(vec![entity_id.to_string()]),
            ..Default::default()
        }),
        heartbeat_interval_ms: Some(30000),
    };

    let stream = match client.tasks.stream_as_agent(&request, None).await {
        Ok(stream) => stream,
        Err(error) => {
            eprintln!("Exception: {}", error);
            return;
        }
    };
    let mut stream = std::pin::pin!(stream);

    while let Some(result) = stream.next().await {
        match result {
            Ok(StreamAsAgentResponse::Heartbeat { data, .. }) => {
                println!("Heartbeat: {:?}", data.heartbeat_object_fields.timestamp);
            }
            Ok(StreamAsAgentResponse::AgentRequest { data, .. }) => {
                let fields = &data.agent_task_request_fields;
                if let Some(execute) = &fields.execute_request {
                    if let Some(task) = &execute.task {
                        if let Some(version) = &task.version {
                            let task_id = version.task_id.clone().unwrap_or_default();
                            let task_status_version = version.status_version.unwrap_or(0);
                            println!(
                                "Starting task {}, version {}: {:?}",
                                task_id, task_status_version, task.description
                            );
                            // Parse the Objective the operator sent with the task.
                            parse_objective(task.specification.as_ref());

                            if let Some(result) =
                                start_task(client, &task_id, task_status_version, entity_id).await
                            {
                                if let Some(version) = result.version {
                                    println!(
                                        "Started task with status version: {:?}.",
                                        version.status_version
                                    );
                                }
                            }
                        }
                    }
                }
            }
            Ok(other) => println!("Event: {:?}", other),
            Err(error) => eprintln!("Exception: {}", error),
        }
    }
}

```

```go title={"Go (gRPC)"} startLine={23} maxLines={16}
// This Go example is compatible with artifacts generated using
// the following grpc/go plugin: https://buf.build/anduril/lattice-sdk/sdks/main:grpc/go
package main

import (
	"context"
	"fmt"
	"io"
	"log"
	"os"
	"time"

	"schema-registry.developer.anduril.com/gen/go/anduril/lattice-sdk/grpc/go/anduril/taskmanager/v1/taskmanagerv1grpc"
	taskmanagerv1 "schema-registry.developer.anduril.com/gen/go/anduril/lattice-sdk/protocolbuffers/go/anduril/taskmanager/v1"
	//Your custom task from schema-registry.developer.anduril.com
	reconnaissancev1beta "schema-registry.developer.anduril.com/gen/go/org/example/reconnaissance/protocolbuffers/go/org/example/reconnaissance/v1beta"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"
	"google.golang.org/protobuf/types/known/anypb"
)

// parseObjective parses the Objective carried in the task's specification.
// Over gRPC, the specification is a google.protobuf.Any; unmarshal it into the
// generated Objective message.
func parseObjective(specification *anypb.Any) {
	objective := &reconnaissancev1beta.Objective{}
	if err := specification.UnmarshalTo(objective); err != nil {
		fmt.Printf("Unsupported task type: %s\n", specification.GetTypeUrl())
		return
	}

	// Objective is a oneof: read whichever target the operator set.
	switch target := objective.GetTarget().(type) {
	case *reconnaissancev1beta.Objective_EntityId:
		fmt.Printf("  Objective: entity %s\n", target.EntityId)
	case *reconnaissancev1beta.Objective_Lla:
		lla := target.Lla
		fmt.Printf("  Objective: LLA (lat %v, lon %v, alt %vm)\n",
			lla.GetLatitudeDegrees(), lla.GetLongitudeDegrees(), lla.GetAltitudeHaeM())
	default:
		fmt.Println("  Objective: no target set")
	}
}

func main() {
	environmentToken := os.Getenv("ENVIRONMENT_TOKEN")
	latticeEndpoint := os.Getenv("LATTICE_ENDPOINT")
	sandboxesToken := os.Getenv("SANDBOXES_TOKEN")

	if latticeEndpoint == "" || environmentToken == "" || sandboxesToken == "" {
		fmt.Println("Missing required environment variables")
		os.Exit(1)
	}

	// Setup authentication
	auth := &BearerTokenAuth{
		Token:          environmentToken,
		SandboxesToken: sandboxesToken,
	}

	opts := []grpc.DialOption{
		grpc.WithTransportCredentials(credentials.NewClientTLSFromCert(nil, "")),
		grpc.WithPerRPCCredentials(auth),
	}

	conn, err := grpc.NewClient(latticeEndpoint, opts...)
	if err != nil {
		log.Fatalf("Did not connect: %v", err)
	}
	defer conn.Close()

	client := taskmanagerv1grpc.NewTaskManagerAPIClient(conn)

	// Set the entity ID to listen for tasks
	entityId := "<ENTITY_ID>"
	fmt.Printf("Listening for tasks for entity %s...\n", entityId)

	for {
		try := func() bool {
			ctx := context.Background()

			// Create the ListenAsAgent request. Set HeartbeatIntervalMs so Lattice
			// sends periodic heartbeats; a missing heartbeat signals a dropped connection.
			listenRequest := &taskmanagerv1.ListenAsAgentRequest{
				AgentSelector: &taskmanagerv1.ListenAsAgentRequest_EntityIds{
					EntityIds: &taskmanagerv1.EntityIds{
						EntityIds: []string{entityId},
					},
				},
				HeartbeatIntervalMs: 30000,
			}

			// Call the ListenAsAgent method to get a stream of task requests
			stream, err := client.ListenAsAgent(ctx, listenRequest)
			if err != nil {
				fmt.Printf("Error starting task listener: %v\n", err)
				return false
			}

			for {
				response, err := stream.Recv()
				if err == io.EOF {
					fmt.Println("Stream ended")
					return false
				}
				if err != nil {
					fmt.Printf("Error receiving from stream: %v\n", err)
					return false
				}

				// A heartbeat confirms the connection is alive. It carries only a
				// timestamp, so skip it rather than treating it as a task request.
				if response.Heartbeat != nil {
					log.Printf("Heartbeat: %s", response.Heartbeat.Timestamp.AsTime())
					continue
				}

				// Process the response based on the type of task request
				switch x := response.Request.(type) {
				case *taskmanagerv1.ListenAsAgentResponse_ExecuteRequest:
					if x.ExecuteRequest != nil && x.ExecuteRequest.Task != nil &&
						x.ExecuteRequest.Task.Version != nil && x.ExecuteRequest.Task.Version.TaskId != "" {
						// Extract task-specific information
						taskId := x.ExecuteRequest.Task.Version.TaskId
						taskStatusVersion := x.ExecuteRequest.Task.Version.StatusVersion

						fmt.Printf("Starting task %s, version %d\n", taskId, taskStatusVersion)

						// Update task status to STATUS_EXECUTING
						result, err := startTask(ctx, client, taskId, taskStatusVersion, entityId)
						if err != nil {
							fmt.Printf("Error starting task: %v\n", err)
							return false
						}

						fmt.Printf("Started task with status version: %d\n", result.StatusVersion)
					}

				case *taskmanagerv1.ListenAsAgentResponse_CompleteRequest:
					if x.CompleteRequest != nil && x.CompleteRequest.TaskId != "" {
						fmt.Printf("Completing task: %s\n", x.CompleteRequest.TaskId)
					}

				case *taskmanagerv1.ListenAsAgentResponse_CancelRequest:
					if x.CancelRequest != nil && x.CancelRequest.TaskId != "" {
						fmt.Printf("Cancelling task: %s\n", x.CancelRequest.TaskId)
					}
				}
			}
		}()

		if !try {
			// If there was an error, wait a bit before retrying
			time.Sleep(1 * time.Second)
		}
	}
}

// startTask updates the task status to STATUS_EXECUTING
func startTask(ctx context.Context, client taskmanagerv1grpc.TaskManagerAPIClient, taskId string, taskStatusVersion uint32, agentEntityId string) (*taskmanagerv1.TaskVersion, error) {
	// Increment status version for the update
	taskStatusVersion++

	// Create system principal with the agent entity ID
	principal := &taskmanagerv1.Principal{
		Agent: &taskmanagerv1.Principal_System{
			System: &taskmanagerv1.System{
				EntityId: agentEntityId,
			},
		},
	}

	// Create task status update
	taskStatus := &taskmanagerv1.TaskStatus{
		Status: taskmanagerv1.Status_STATUS_EXECUTING,
	}

	// Create status update request
	statusUpdate := &taskmanagerv1.StatusUpdate{
		Version: &taskmanagerv1.TaskVersion{
			TaskId:            taskId,
			DefinitionVersion: 0,
			StatusVersion:     taskStatusVersion,
		},
		Status: taskStatus,
		Author: principal,
	}

	// Create the update status request
	updateRequest := &taskmanagerv1.UpdateStatusRequest{
		StatusUpdate: statusUpdate,
	}

	// Call the UpdateStatus API
	response, err := client.UpdateStatus(ctx, updateRequest)
	if err != nil {
		return nil, fmt.Errorf("error updating task status: %w", err)
	}

	return response.Task.Version, nil
}

```

```py title={"Python (gRPC)"} startLine={25} maxLines={23}
# This Python example is compatible with artifacts generated using the following
# Buf plugins: https://schema-registry.developer.anduril.com/anduril/lattice-sdk/sdks/main:bufbuild/py
# and https://schema-registry.developer.anduril.com/anduril/lattice-sdk/sdks/main:connectrpc/py

import os
import sys
import time
from bearer_auth import create_client
from protobuf import Oneof

from anduril.taskmanager.v1.task_pub_pb import (
    Principal,
    Status,
    System,
    TaskStatus,
    TaskVersion,
    StatusUpdate,
)
from anduril.taskmanager.v1.task_manager_api_pub_pb import (
    EntityIds,
    ListenAsAgentRequest,
    UpdateStatusRequest,
)
from anduril.taskmanager.v1.task_manager_api_pub_connect import TaskManagerAPIClientSync

# Your custom task from schema-registry.developer.anduril.com, generated with the
# same bufbuild/py plugin as the Lattice SDK.
from org.example.reconnaissance.v1beta.objective_pb import Objective


def parse_objective(specification):
    """Parse the Objective carried in the task's specification.

    Over gRPC, the specification is a google.protobuf.Any. Check the type
    before unpacking it into the generated Objective message.
    """
    if specification is None:
        print("Task has no specification")
        return

    # Over gRPC the specification is a protobuf Any; unpack returns None when it
    # does not carry an Objective.
    objective = specification.unpack(Objective)
    if objective is None:
        print(f"Unsupported task type: {specification.type_url}")
        return

    # Objective is a oneof: read whichever target the operator set.
    target = objective.target
    if target.field == "entity_id":
        print(f"  Objective: entity {target.value}")
    elif target.field == "lla":
        lla = target.value
        print(
            f"  Objective: LLA (lat {lla.latitude_degrees}, "
            f"lon {lla.longitude_degrees}, alt {lla.altitude_hae_m}m)"
        )
    else:
        print("  Objective: no target set")


def start_task(client, task_id, task_status_version, agent_entity_id):
    """Update the task status to STATUS_EXECUTING."""
    task_status_version += 1

    principal = Principal(agent=Oneof("system", System(entity_id=agent_entity_id)))
    task_status = TaskStatus(status=Status.EXECUTING)

    status_update = StatusUpdate(
        version=TaskVersion(task_id=task_id, definition_version=0, status_version=task_status_version),
        status=task_status,
        author=principal,
    )

    response = client.update_status(UpdateStatusRequest(status_update=status_update))
    return response.task.version


def main():
    environment_token = os.getenv("ENVIRONMENT_TOKEN")
    lattice_endpoint = os.getenv("LATTICE_ENDPOINT")
    sandboxes_token = os.getenv("SANDBOXES_TOKEN")

    if not environment_token or not lattice_endpoint or not sandboxes_token:
        print("Missing required environment variables", file=sys.stderr)
        sys.exit(1)

    # Create the API client over Connect gRPC with authentication
    client = create_client(TaskManagerAPIClientSync, lattice_endpoint, environment_token, sandboxes_token)

    entity_id = "<ENTITY_ID>"
    print(f"Listening for tasks for entity {entity_id}...")

    while True:
        try:
            # Set heartbeat_interval_ms so Lattice sends periodic heartbeats; a
            # missing heartbeat signals a dropped connection.
            stream = client.listen_as_agent(
                ListenAsAgentRequest(
                    agent_selector=Oneof("entity_ids", EntityIds(entity_ids=[entity_id])),
                    heartbeat_interval_ms=30000,
                )
            )

            for response in stream:
                # A heartbeat confirms the connection is alive. It carries only a
                # timestamp, so skip it rather than treating it as a task request.
                if response.has_field("heartbeat"):
                    print(f"Heartbeat: {response.heartbeat.timestamp.to_datetime()}")
                    continue

                # The remaining responses carry a `request` oneof; inspect which
                # arm is populated.
                request = response.request
                if request is None:
                    continue
                if request.field == "execute_request":
                    task = request.value.task
                    if task and task.version and task.version.task_id:
                        task_id = task.version.task_id
                        task_status_version = task.version.status_version
                        print(f"Starting task {task_id}, version {task_status_version}")

                        # Parse the Objective the operator sent with the task.
                        parse_objective(task.specification)

                        try:
                            result = start_task(client, task_id, task_status_version, entity_id)
                            print(f"Started task with status version: {result.status_version}")
                        except Exception as error:
                            print(f"Error starting task: {error}", file=sys.stderr)
                            break
                elif request.field == "complete_request":
                    if request.value.task_id:
                        print(f"Completing task: {request.value.task_id}")
                elif request.field == "cancel_request":
                    if request.value.task_id:
                        print(f"Cancelling task: {request.value.task_id}")
        except Exception as error:
            print(f"Error in stream: {error}", file=sys.stderr)

        # If the stream ended or errored, wait a bit before retrying
        time.sleep(1)


if __name__ == "__main__":
    main()

```

```rs title={"Rust (gRPC)"} startLine={22} maxLines={28}
// This Rust example is compatible with artifacts generated using
// the following grpc/rust plugin: https://buf.build/anduril/lattice-sdk/sdks/main:community/neoeinstein-tonic
mod bearer_auth;

use anduril_lattice_sdk_community_neoeinstein_prost::anduril::taskmanager::v1::{
    listen_as_agent_request, listen_as_agent_response, principal, EntityIds, ListenAsAgentRequest,
    Principal, Status, StatusUpdate, System, TaskStatus, TaskVersion, UpdateStatusRequest,
};
use anduril_lattice_sdk_community_neoeinstein_tonic::anduril::taskmanager::v1::tonic::task_manager_api_client::TaskManagerApiClient;
//Your custom task from schema-registry.developer.anduril.com
use org_example_reconnaissance_community_neoeinstein_prost::org::example::reconnaissance::v1beta::{
    objective, Objective,
};
use bearer_auth::EnvironmentTokenAuth;
use prost::Message;
use tonic::metadata::MetadataValue;
use tonic::transport::{Channel, ClientTlsConfig};
use tonic::Request;

use std::env;
use std::time::Duration;

// Type URL of the Objective task defined in objective.proto.
const OBJECTIVE_TYPE: &str =
    "type.googleapis.com/org.example.reconnaissance.v1beta.Objective";

// Parse the Objective carried in the task's specification. Over gRPC, the
// specification is a google.protobuf.Any; decode its `value` bytes into the
// generated Objective message after checking the type URL.
fn parse_objective(specification: &prost_types::Any) {
    if specification.type_url != OBJECTIVE_TYPE {
        eprintln!("Unsupported task type: {}", specification.type_url);
        return;
    }

    match Objective::decode(&specification.value[..]) {
        Ok(objective) => {
            // Objective is a oneof: read whichever target the operator set.
            match objective.target {
                Some(objective::Target::EntityId(entity_id)) => {
                    println!("  Objective: entity {}", entity_id);
                }
                Some(objective::Target::Lla(lla)) => {
                    println!(
                        "  Objective: LLA (lat {}, lon {}, alt {}m)",
                        lla.latitude_degrees, lla.longitude_degrees, lla.altitude_hae_m
                    );
                }
                None => println!("  Objective: no target set"),
            }
        }
        Err(e) => eprintln!("Failed to decode Objective: {}", e),
    }
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let environment_token = env::var("ENVIRONMENT_TOKEN")
        .expect("ENVIRONMENT_TOKEN environment variable not set");
    let lattice_endpoint = env::var("LATTICE_ENDPOINT")
        .expect("LATTICE_ENDPOINT environment variable not set");
    let sandboxes_token = env::var("SANDBOXES_TOKEN")
        .expect("SANDBOXES_TOKEN environment variable not set");

    let auth = EnvironmentTokenAuth::new(
        environment_token,
        sandboxes_token,
    );

    let tls_config = ClientTlsConfig::new().with_native_roots();
    let channel = Channel::from_shared(format!("https://{}", lattice_endpoint))?
        .tls_config(tls_config)?
        .connect()
        .await?;

    let access_token = auth.get_token().await?;
    let sandboxes_token = auth.sandboxes_token().to_string();
    let auth_header: MetadataValue<_> = format!("Bearer {}", access_token).parse()?;
    let sandbox_header: MetadataValue<_> = format!("Bearer {}", sandboxes_token).parse()?;

    let mut client = TaskManagerApiClient::with_interceptor(
        channel,
        move |mut req: Request<()>| {
            req.metadata_mut()
                .insert("authorization", auth_header.clone());
            req.metadata_mut()
                .insert("anduril-sandbox-authorization", sandbox_header.clone());
            Ok(req)
        },
    );

    // Set the entity ID to listen for tasks
    let entity_id = "<ENTITY_ID>".to_string();
    println!("Listening for tasks for entity {}...", entity_id);

    // Reconnect loop: open a fresh ListenAsAgent stream if the previous one
    // disconnects or errors.
    loop {
        // Set heartbeat_interval_ms so Lattice sends periodic heartbeats; a
        // missing heartbeat signals a dropped connection.
        let request = Request::new(ListenAsAgentRequest {
            agent_selector: Some(listen_as_agent_request::AgentSelector::EntityIds(EntityIds {
                entity_ids: vec![entity_id.clone()],
            })),
            heartbeat_interval_ms: 30000,
            ..Default::default()
        });

        let mut stream = match client.listen_as_agent(request).await {
            Ok(resp) => resp.into_inner(),
            Err(e) => {
                eprintln!("Error starting task listener: {}", e);
                tokio::time::sleep(Duration::from_secs(1)).await;
                continue;
            }
        };

        loop {
            let response = match stream.message().await {
                Ok(Some(r)) => r,
                Ok(None) => {
                    println!("Stream ended");
                    break;
                }
                Err(e) => {
                    eprintln!("Error receiving from stream: {}", e);
                    break;
                }
            };

            // A heartbeat confirms the connection is alive. It carries only a
            // timestamp, so skip it rather than treating it as a task request.
            if let Some(heartbeat) = &response.heartbeat {
                println!("Heartbeat: {:?}", heartbeat.timestamp);
                continue;
            }

            // Process the response based on the type of task request
            match response.request {
                Some(listen_as_agent_response::Request::ExecuteRequest(execute)) => {
                    if let Some(task) = execute.task {
                        if let Some(version) = task.version {
                            let task_id = version.task_id.clone();
                            let task_status_version = version.status_version;
                            println!(
                                "Starting task {}, version {}",
                                task_id, task_status_version
                            );

                            // Parse the Objective the operator sent with the task.
                            if let Some(specification) = &task.specification {
                                parse_objective(specification);
                            }

                            // Create status update request
                            let status_update = StatusUpdate {
                                version: Some(TaskVersion {
                                    task_id: task_id.clone(),
                                    // Increment status version for the update
                                    status_version: task_status_version + 1,
                                    definition_version: 0,
                                    ..Default::default()
                                }),
                                status: Some(TaskStatus {
                                    status: Status::Executing as i32,
                                    ..Default::default()
                                }),
                                author: Some(Principal {
                                    agent: Some(principal::Agent::System(System {
                                        entity_id: entity_id.clone(),
                                        ..Default::default()
                                    })),
                                    ..Default::default()
                                }),
                                ..Default::default()
                            };

                            // Call the UpdateStatus API
                            match client
                                .update_status(Request::new(UpdateStatusRequest {
                                    status_update: Some(status_update),
                                }))
                                .await
                            {
                                Ok(resp) => {
                                    if let Some(v) =
                                        resp.into_inner().task.and_then(|t| t.version)
                                    {
                                        println!(
                                            "Started task with status version: {}",
                                            v.status_version
                                        );
                                    }
                                }
                                Err(e) => eprintln!("Error starting task: {}", e),
                            }
                        }
                    }
                }
                Some(listen_as_agent_response::Request::CompleteRequest(complete)) => {
                    if !complete.task_id.is_empty() {
                        println!("Completing task: {}", complete.task_id);
                    }
                }
                Some(listen_as_agent_response::Request::CancelRequest(cancel)) => {
                    if !cancel.task_id.is_empty() {
                        println!("Cancelling task: {}", cancel.task_id);
                    }
                }
                None => {}
            }
        }

        // If there was an error, wait a bit before retrying
        tokio::time::sleep(Duration::from_secs(1)).await;
    }
}

```

> **Note**
>
> The gRPC examples import an `Objective` type generated from your own
> `objective.proto`. [Publish your schema](/guides/developer-tools/registry) to the Schema
> Registry, then generate the language bindings before running the agent.

## Task and verify the task handler

The following steps aren't required to implement tasking -- they show how to exercise your task
handler end to end in a development environment. Assign a task to your agent, then confirm it
receives, parses, and updates the task.

#### Assign a task using the UI

In most cases an operator uses the Lattice UI to task your agent. To test the agent's task handler in Lattice Sandboxes,
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 the
   `Objective` task.
3. From the **Task Details** panel on the right hand side,
   set the objective -- either an entity to reconnoiter or a lat/lon/altitude point -- then choose
   **Execute Task**. This is the same field your agent reads
   from the task specification.

#### Verify the task status

If successful, you see the parsed task data and the updated status version in your local
development console:

```bash
Starting task <task-id>, version 1: Reconnaissance objective
  Objective: LLA (lat 37.7749, lon -122.4194, alt 100.0m)
Started task with status version: 2.
```

The agent has successfully parsed the task data, updated the task status, and incremented the
task status version.

## Update the status of a task

As an agent makes progress, it reports real-time updates to Lattice with
[`UpdateTaskStatus`](/reference/rest/tasks/update-task-status), incrementing the task's
`statusVersion` on each update. In Lattice, tasks move through the following states:

#### \`STATUS_SENT\`

Lattice automatically starts with `STATUS_SENT` when it sends a task to an agent:

```json
"task": {
    "version": {
        "taskId": "my-task",
        "definitionVersion": 1,
        "statusVersion": 1
    },
    "status": {
        "status": "STATUS_SENT"
    }
}

```

#### \`STATUS_MACHINE_RECEIPT\`

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

```json
"statusUpdate": {
    "version": {
        "taskId": "my-task",
        "definitionVersion": 1,
        "statusVersion": 2
    },
    "status": {
        "status": "STATUS_MACHINE_RECEIPT"
    }
}
```

#### \`STATUS_ACK\`

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

```json
"statusUpdate": {
    "version": {
        "taskId": "my-task",
        "definitionVersion": 1,
        "statusVersion": 3
    },
    "status": {
        "status": "STATUS_ACK"
    }
}
```

#### \`STATUS_WILCO\`

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

```json
"statusUpdate": {
    "version": {
        "taskId": "my-task",
        "definitionVersion": 1,
        "statusVersion": 4
    },
    "status": {
        "status": "STATUS_WILCO"
    }
}
```

#### \`STATUS_EXECUTING\`

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

```json
"statusUpdate": {
    "version": {
        "taskId": "my-task",
        "definitionVersion": 1,
        "statusVersion": 5
    },
    "status": {
        "status": "STATUS_EXECUTING"
    }
}
```

#### \`STATUS_DONE_OK\`

Finally, when the agent reaches a terminal state and completes the task successfully, it
reports `STATUS_DONE_OK`. The agent can reach this state on its own, such as when its logic determines that the task's objective is met, or in response to an operator-initiated request for task completion:

```json
"statusUpdate": {
    "version": {
        "taskId": "my-task",
        "definitionVersion": 1,
        "statusVersion": 6
    },
    "status": {
        "status": "STATUS_DONE_OK"
    }
}
```

#### \`STATUS_DONE_NOT_OK\`

If the agent reaches a terminal state but does not complete the task successfully, it
reports `STATUS_DONE_NOT_OK`. The agent can reach this state on its own, such as when its logic determines that the task can't be completed, or in response to an operator-initiated request for task completion or cancellation.
The agent should include a descriptive [`TaskError`](/reference/rest/tasks/update-task-status#request.body.newStatus.taskError)
when reporting `STATUS_DONE_NOT_OK`:

```json
"statusUpdate": {
    "version": {
        "taskId": "my-task",
        "definitionVersion": 1,
        "statusVersion": 7
    },
    "status": {
        "status": "STATUS_DONE_NOT_OK",
        "taskError": {
        "code": "ERROR_CODE_FAILED",
        "message": "The asset failed task execution due to an internal error."
        }
    }
}
```

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 reaches either state, it's complete and cannot be updated.

## Handle task cancellation

An operator can request that a task be cancelled while the agent is executing it. When a task has
already been sent to an agent, Lattice routes the [`CancelTask`](/reference/rest/tasks/cancel-task)
request to that agent, which decides whether to accept or reject it. For the operator's side of this
workflow, see [Cancel tasks](/guides/tasks/command-and-operate#cancel-tasks).

#### Set up a task processor

Create a task processor that listens for tasks assigned to your agent and handles cancellation requests.
For demonstration purposes, this example uses a `TASK_ACTIVE` environment variable to control whether the agent accepts or rejects cancellations:

```go title={"Go (REST)"} startLine={53} maxLines={25}
package main

import (
	"context"
	"errors"
	"fmt"
	"io"
	"log"
	"net/http"
	"os"
	"strings"
	"time"

	Lattice "github.com/anduril/lattice-sdk-go/v5"
	"github.com/anduril/lattice-sdk-go/v5/client"
	"github.com/anduril/lattice-sdk-go/v5/option"
)

// Sets whether the task is currently being processed. If so, it cannot be cancelled.
var taskActive bool

func main() {
	// Get environment variables
	latticeEndpoint := os.Getenv("LATTICE_ENDPOINT")
	environmentToken := os.Getenv("ENVIRONMENT_TOKEN")
	sandboxesToken := os.Getenv("SANDBOXES_TOKEN")
	taskActiveStr := os.Getenv("TASK_ACTIVE")

	// Check required environment variables
	if latticeEndpoint == "" || environmentToken == "" || sandboxesToken == "" {
		fmt.Println("Missing required environment variables")
		os.Exit(1)
	}

	// Parse TASK_ACTIVE environment variable
	taskActive = strings.ToLower(taskActiveStr) == "true"

	// Initialize headers for sandbox authorization
	headers := http.Header{}
	headers.Add("Anduril-Sandbox-Authorization", fmt.Sprintf("Bearer %s", sandboxesToken))

	// Create the client
	LatticeClient := client.NewClient(
		option.WithToken(environmentToken),
		option.WithBaseURL(fmt.Sprintf("https://%s", latticeEndpoint)),
		option.WithHTTPHeader(headers),
	)

	// Set the entity ID to listen for tasks
	entityId := "<AGENT_ID>"
	fmt.Printf("Streaming tasks for entity: %s...\n", entityId)

	// Create context for the request
	ctx := context.Background()

	// Create agent stream request
	agentStreamRequest := Lattice.AgentStreamRequest{
		AgentSelector: &Lattice.EntityIDsSelector{
			EntityIDs: []string{entityId},
		},
	}

	// Stream tasks
	stream, err := LatticeClient.Tasks.StreamAsAgent(ctx, &agentStreamRequest)
	if err != nil {
		fmt.Printf("Error streaming tasks: %v\n", err)
		os.Exit(1)
	}

	// Process stream events
	for {
		select {
		case <-ctx.Done():
			log.Printf("Context canceled: %v", ctx.Err())
			return
		default:
			// Continue processing
		}

		event, err := stream.Recv()

		if errors.Is(err, io.EOF) {
			log.Println("Stream completed successfully.")
			return
		}

		if err != nil {
			log.Printf("Error receiving message: %v", err)
			continue
		}

		if event.Event == "heartbeat" {
			timestamp := *event.Heartbeat.Timestamp
			log.Printf("Heartbeat: %s", timestamp)
		} else {
			request := event.GetAgentRequest()
			if executeRequest := request.GetExecuteRequest(); executeRequest != nil {
				task := executeRequest.GetTask()
				if task != nil {
					taskId := *task.GetVersion().GetTaskID()
					taskStatusVersion := *task.GetVersion().GetStatusVersion()
					description := *task.GetDescription()

					log.Printf("Starting task %s, version %d: %s", taskId, taskStatusVersion, description)

					// Update task status to STATUS_EXECUTING
					result, err := executeTask(ctx, LatticeClient, taskId, int(taskStatusVersion), entityId)
					if err != nil {
						log.Printf("Error starting task: %v", err)
						continue
					}

					log.Printf("Started task with status version: %d", *result.StatusVersion)
				}
			} else if completeRequest := request.GetCompleteRequest(); completeRequest != nil {
				taskToComplete := completeRequest.GetTaskID()
				if taskToComplete != nil {
					log.Printf("Completing task: %s", *taskToComplete)
					err := completeTask(ctx, LatticeClient, *taskToComplete, entityId)
					if err != nil {
						log.Printf("Error completing task: %v", err)
					}
				}
			} else if cancelRequest := request.GetCancelRequest(); cancelRequest != nil {
				taskToCancel := cancelRequest.GetTaskID()
				if taskToCancel != nil {
					log.Printf("Cancelling task: %s", *taskToCancel)
					err := cancelTask(ctx, LatticeClient, *taskToCancel, entityId)
					if err != nil {
						log.Printf("Error cancelling task: %v", err)
					}
				}
			}
		}

		// Sleep briefly to prevent tight looping
		time.Sleep(100 * time.Millisecond)
	}
}

// executeTask updates the task status to STATUS_EXECUTING
func executeTask(ctx context.Context, client *client.Client, taskId string, taskStatusVersion int, agentEntityId string) (*Lattice.TaskVersion, error) {
	// Increment status version for the update
	taskStatusVersion++

	// Create system principal with the agent entity ID
	principal := Lattice.Principal{
		System: &Lattice.System{
			EntityID: &agentEntityId,
		},
	}

	taskStatus := Lattice.TaskStatus{
		Status: Lattice.TaskStatusStatusStatusExecuting.Ptr(),
	}

	// Create task status update request
	taskStatusUpdate := Lattice.TaskStatusUpdate{
		TaskID:        taskId,
		StatusVersion: &taskStatusVersion,
		NewStatus:     &taskStatus,
		Author:        &principal,
	}

	// Call the UpdateTaskStatus API
	task, err := client.Tasks.UpdateTaskStatus(ctx, &taskStatusUpdate)
	if err != nil {
		return nil, fmt.Errorf("error updating task status: %w", err)
	}

	return task.Version, nil
}

// cancelTask handles cancellation requests from Lattice
func cancelTask(ctx context.Context, client *client.Client, taskId string, entityId string) error {
	// Get current task to retrieve status_version
	getTaskRequest := Lattice.GetTaskRequest{
		TaskID: taskId,
	}
	task, err := client.Tasks.GetTask(ctx, &getTaskRequest)
	if err != nil {
		return fmt.Errorf("error getting task: %w", err)
	}
	if task.Status == nil || task.Version == nil || task.Version.StatusVersion == nil {
		return fmt.Errorf("task status or version is missing")
	}
	currentTaskStatus := task.Status.Status
	taskStatusVersion := *task.Version.StatusVersion
	taskStatusVersion++

	// Create system principal with the agent entity ID
	principal := Lattice.Principal{
		System: &Lattice.System{
			EntityID: &entityId,
		},
	}

	if taskActive {
		// Reject cancellation: task is active and cannot be cancelled
		rejectedMessage := "Task is already active, and cannot be cancelled."
		taskStatus := Lattice.TaskStatus{
			// Because the cancellation is being rejected, we do not
			// change the task status.
			Status: currentTaskStatus,
			TaskError: &Lattice.TaskError{
				Code:    Lattice.TaskErrorCodeErrorCodeRejected.Ptr(),
				Message: &rejectedMessage,
			},
		}

		taskStatusUpdate := Lattice.TaskStatusUpdate{
			TaskID:        taskId,
			StatusVersion: &taskStatusVersion,
			NewStatus:     &taskStatus,
			Author:        &principal,
		}

		_, err := client.Tasks.UpdateTaskStatus(ctx, &taskStatusUpdate)
		if err != nil {
			return fmt.Errorf("error updating task status: %w", err)
		}

		log.Println("Task could not be cancelled.")
	} else {
		// Accept cancellation
		cancelledMessage := "Task cancelled by agent."
		taskStatus := Lattice.TaskStatus{
			Status: Lattice.TaskStatusStatusStatusDoneNotOk.Ptr(),
			TaskError: &Lattice.TaskError{
				Code:    Lattice.TaskErrorCodeErrorCodeCancelled.Ptr(),
				Message: &cancelledMessage,
			},
		}

		taskStatusUpdate := Lattice.TaskStatusUpdate{
			TaskID:        taskId,
			StatusVersion: &taskStatusVersion,
			NewStatus:     &taskStatus,
			Author:        &principal,
		}

		_, err := client.Tasks.UpdateTaskStatus(ctx, &taskStatusUpdate)
		if err != nil {
			return fmt.Errorf("error updating task status: %w", err)
		}

		log.Println("Task has been cancelled.")
	}

	return nil
}

// completeTask updates the task status to STATUS_DONE_OK
func completeTask(ctx context.Context, client *client.Client, taskId string, entityId string) error {
	// Get current task to retrieve status_version
	getTaskRequest := Lattice.GetTaskRequest{
		TaskID: taskId,
	}
	task, err := client.Tasks.GetTask(ctx, &getTaskRequest)
	if err != nil {
		return fmt.Errorf("error getting task: %w", err)
	}

	if task.Version == nil || task.Version.StatusVersion == nil {
		return fmt.Errorf("task version is missing")
	}

	taskStatusVersion := *task.Version.StatusVersion
	// Increment version and update to terminal state
	taskStatusVersion++

	// Create system principal with the agent entity ID
	principal := Lattice.Principal{
		System: &Lattice.System{
			EntityID: &entityId,
		},
	}

	taskStatus := Lattice.TaskStatus{
		Status: Lattice.TaskStatusStatusStatusDoneOk.Ptr(),
	}

	taskStatusUpdate := Lattice.TaskStatusUpdate{
		TaskID:        taskId,
		StatusVersion: &taskStatusVersion,
		NewStatus:     &taskStatus,
		Author:        &principal,
	}

	_, err = client.Tasks.UpdateTaskStatus(ctx, &taskStatusUpdate)
	if err != nil {
		return fmt.Errorf("error updating task status: %w", err)
	}

	return nil
}

```

```java title={"Java (REST)"} startLine={168} maxLines={25}
package org.example;

import java.util.Arrays;
import java.util.concurrent.CompletableFuture;

import com.anduril.AsyncLattice;
import com.anduril.resources.tasks.requests.AgentStreamRequest;
import com.anduril.resources.tasks.requests.TaskStatusUpdate;
import com.anduril.resources.tasks.types.StreamAsAgentResponse;
import com.anduril.types.AgentStreamEvent;
import com.anduril.types.EntityIdsSelector;
import com.anduril.types.Principal;
import com.anduril.types.TaskError;
import com.anduril.types.TaskErrorCode;
import com.anduril.types.TaskStatus;
import com.anduril.types.TaskStatusStatus;
import com.anduril.types.TaskVersion;
public class ProcessTask {
    // Entity ID to listen for tasks - replace with your actual entity ID.
    private static final String ENTITY_ID = "<AGENT_ID>";

    // Sets whether the task is currently being processed. If so, it cannot be cancelled.
    private static boolean taskActive;

    public static void main(String[] args) {
        String endpoint = System.getenv("LATTICE_ENDPOINT");
        String environmentToken = System.getenv("ENVIRONMENT_TOKEN");
        String sandboxesToken = System.getenv("SANDBOXES_TOKEN");
        String taskActiveStr = System.getenv("TASK_ACTIVE");

        if (endpoint == null || environmentToken == null || sandboxesToken == null) {
            System.err.println("Missing required environment variables.");
            System.exit(1);
        }
        if (!endpoint.startsWith("https://")) endpoint = "https://" + endpoint;

        taskActive = "true".equalsIgnoreCase(taskActiveStr);

        try {
            AsyncLattice client = AsyncLattice.builder()
                .url(endpoint)
                .token(environmentToken)
                .addHeader("Anduril-Sandbox-Authorization", "Bearer " + sandboxesToken)
                .build();

            System.out.println("Streaming tasks for entity: " + ENTITY_ID + "...");

            // Create agent selector with the specified entity ID.
            EntityIdsSelector agentSelector = EntityIdsSelector.builder()
                .entityIds(Arrays.asList(ENTITY_ID))
                .build();

            // Create agent stream request.
            AgentStreamRequest agentRequest = AgentStreamRequest.builder()
                .agentSelector(agentSelector)
                .build();

            // Stream tasks from Lattice as an agent
            client.tasks().streamAsAgent(agentRequest)
                .thenAccept(requests -> {
                    for (StreamAsAgentResponse request : requests) {
                        if (request.isHeartbeat()) {
                            System.out.println("Heartbeat: " + request.getHeartbeat().get().getTimestamp().get());
                        } else {
                            AgentStreamEvent event = request.getAgentRequest().get();
                            if (event.getExecuteRequest().isPresent()) {
                                String taskId = event.getExecuteRequest().get()
                                    .getTask().get()
                                    .getVersion().get()
                                    .getTaskId().get();

                                int taskStatusVersion = event.getExecuteRequest().get()
                                    .getTask().get()
                                    .getVersion().get()
                                    .getStatusVersion().get();

                                String description = event.getExecuteRequest().get()
                                    .getTask().get()
                                    .getDescription().orElse("No description");

                                System.out.println("Starting task " + taskId + ", version " + taskStatusVersion + ": " + description);

                                try {
                                    TaskVersion result = startTask(client, taskId, taskStatusVersion, ENTITY_ID).get();
                                    System.out.println("Started task with status version: " + result.getStatusVersion().get());
                                } catch (Exception e) {
                                    System.err.println("Error starting task: " + e.getMessage());
                                }
                            } else if (event.getCompleteRequest().isPresent()) {
                                String taskId = event.getCompleteRequest().get().getTaskId().get();
                                System.out.println("Completing task: " + taskId);
                                try {
                                    completeTask(client, taskId, ENTITY_ID).get();
                                } catch (Exception e) {
                                    System.err.println("Error completing task: " + e.getMessage());
                                }
                            } else if (event.getCancelRequest().isPresent()) {
                                String taskId = event.getCancelRequest().get().getTaskId().get();
                                System.out.println("Cancelling task: " + taskId);
                                try {
                                    cancelTask(client, taskId, ENTITY_ID).get();
                                } catch (Exception e) {
                                    System.err.println("Error cancelling task: " + e.getMessage());
                                }
                            }
                        }
                    }
                })
                .exceptionally(ex -> {
                    System.err.println("Exception while streaming tasks: " + ex.getMessage());
                    return null;
                })
                .join();
        } catch (Exception e) {
            System.err.println("Error in streaming loop: " + e.getMessage());
        }
    }

    /**
     * Update task status to STATUS_EXECUTING.
     *
     * @param client Lattice client
     * @param taskId Task ID to update
     * @param taskStatusVersion Current task status version
     * @param agentEntityId Agent entity ID performing the update
     * @return CompletableFuture containing the updated TaskVersion
     */
    private static CompletableFuture<TaskVersion> startTask(AsyncLattice client, String taskId, Integer taskStatusVersion, String agentEntityId) {
        try {
            // Increment status version for the update
            taskStatusVersion += 1;

            // Create system principal with the agent entity ID.
            Principal principal = Principal.builder()
                .system(com.anduril.types.System.builder()
                    .entityId(agentEntityId)
                    .build())
                .build();

            // Create task status for update.
            TaskStatus taskStatus = TaskStatus.builder()
                .status(TaskStatusStatus.STATUS_EXECUTING)
                .build();

            // Create task status update request.
            TaskStatusUpdate taskStatusUpdate = TaskStatusUpdate.builder()
                .statusVersion(taskStatusVersion)
                .newStatus(taskStatus)
                .author(principal)
                .build();

            return client.tasks().updateTaskStatus(taskId, taskStatusUpdate)
                .thenApply(response -> response.getVersion().get());
        } catch (Exception e) {
            System.err.println("Encountered the following error while starting task: " + e.getMessage());
            return CompletableFuture.failedFuture(e);
        }
    }

    /**
     * Handle cancellation requests from Lattice.
     *
     * @param client Lattice client
     * @param taskId Task ID to cancel
     * @param entityId Agent entity ID performing the cancellation
     * @return CompletableFuture indicating completion
     */
    private static CompletableFuture<Void> cancelTask(AsyncLattice client, String taskId, String entityId) {
        // Get current task to retrieve status_version
        return client.tasks().getTask(taskId)
            .thenCompose(task -> {
                if (task.getStatus().isEmpty() || task.getVersion().isEmpty() || task.getVersion().get().getStatusVersion().isEmpty()) {
                    System.err.println("Task status or version is missing");
                    return CompletableFuture.failedFuture(new RuntimeException("Task status or version is missing"));
                }

                TaskStatusStatus currentTaskStatus = task.getStatus().get().getStatus().get();
                int taskStatusVersion = task.getVersion().get().getStatusVersion().get();
                taskStatusVersion += 1;

                // Create system principal with the agent entity ID.
                Principal principal = Principal.builder()
                    .system(com.anduril.types.System.builder()
                        .entityId(entityId)
                        .build())
                    .build();

                TaskStatus taskStatus;
                if (taskActive) {
                    // Reject cancellation: task is active and cannot be cancelled
                    taskStatus = TaskStatus.builder()
                        // Because the cancellation is being rejected, we do not
                        // change the task status.
                        .status(currentTaskStatus)
                        .taskError(TaskError.builder()
                            .code(TaskErrorCode.ERROR_CODE_REJECTED)
                            .message("Task is already active, and cannot be cancelled.")
                            .build())
                        .build();
                } else {
                    // Accept cancellation
                    taskStatus = TaskStatus.builder()
                        .status(TaskStatusStatus.STATUS_DONE_NOT_OK)
                        .taskError(TaskError.builder()
                            .code(TaskErrorCode.ERROR_CODE_CANCELLED)
                            .message("Task cancelled by agent.")
                            .build())
                        .build();
                }

                TaskStatusUpdate taskStatusUpdate = TaskStatusUpdate.builder()
                    .statusVersion(taskStatusVersion)
                    .newStatus(taskStatus)
                    .author(principal)
                    .build();

                return client.tasks().updateTaskStatus(taskId, taskStatusUpdate)
                    .thenAccept(response -> {
                        if (taskActive) {
                            System.out.println("Task could not be cancelled.");
                        } else {
                            System.out.println("Task has been cancelled.");
                        }
                    });
            })
            .exceptionally(ex -> {
                System.err.println("Encountered the following error while cancelling task: " + ex.getMessage());
                return null;
            });
    }

    /**
     * Update task status to STATUS_DONE_OK.
     *
     * @param client Lattice client
     * @param taskId Task ID to complete
     * @param entityId Agent entity ID performing the completion
     * @return CompletableFuture indicating completion
     */
    private static CompletableFuture<Void> completeTask(AsyncLattice client, String taskId, String entityId) {
        // Get current task to retrieve status_version
        return client.tasks().getTask(taskId)
            .thenCompose(task -> {
                if (task.getVersion().isEmpty() || task.getVersion().get().getStatusVersion().isEmpty()) {
                    System.err.println("Task version is missing");
                    return CompletableFuture.failedFuture(new RuntimeException("Task version is missing"));
                }

                int taskStatusVersion = task.getVersion().get().getStatusVersion().get();
                // Increment version and update to terminal state
                taskStatusVersion += 1;

                // Create system principal with the agent entity ID.
                Principal principal = Principal.builder()
                    .system(com.anduril.types.System.builder()
                        .entityId(entityId)
                        .build())
                    .build();

                TaskStatus taskStatus = TaskStatus.builder()
                    .status(TaskStatusStatus.STATUS_DONE_OK)
                    .build();

                TaskStatusUpdate taskStatusUpdate = TaskStatusUpdate.builder()
                    .statusVersion(taskStatusVersion)
                    .newStatus(taskStatus)
                    .author(principal)
                    .build();

                return client.tasks().updateTaskStatus(taskId, taskStatusUpdate)
                    .thenApply(response -> (Void) null);
            })
            .exceptionally(ex -> {
                System.err.println("Encountered the following error while completing task: " + ex.getMessage());
                return null;
            });
    }
}

```

```py title={"Python (REST)"} startLine={125} maxLines={25}
from anduril import AsyncLattice, EntityIdsSelector, TaskStatus, Principal, System, TaskError
import asyncio
import os
import sys

# Sets whether the task is currently being processed, if so, it cannot be cancelled.
task_active = os.getenv('TASK_ACTIVE', 'false').lower() == 'true'

lattice_endpoint = os.getenv('LATTICE_ENDPOINT')
environment_token = os.getenv('ENVIRONMENT_TOKEN')

# Remove sandboxes_token from the following statements if you are not developing on Sandboxes.
sandboxes_token = os.getenv('SANDBOXES_TOKEN')
if not environment_token or not lattice_endpoint or not sandboxes_token:
    print("Missing required environment variables.")
    sys.exit(1)

client = AsyncLattice(
    base_url=f"https://{lattice_endpoint}",
    token=lambda: str(environment_token),
    # Remove the following header if you are not developing on Sandboxes.
    headers={ "anduril-sandbox-authorization": f"Bearer {sandboxes_token}" }
)

async def start_task(task_id, task_status_version, entity_id):
    try:
        task_status_version += 1
        response = await client.tasks.update_task_status(
            task_id=task_id,
            status_version=task_status_version,
            new_status=TaskStatus(
                status="STATUS_EXECUTING"
            ),
            author=Principal(
                system=System(
                    entity_id=entity_id
                )
            )
        )
        return response
    except Exception as error:
        print(f"Encountered the following error while starting task: {error}")

async def cancel_task(task_id, entity_id):
    try:
        # Get current task to retrieve status_version
        task = await client.tasks.get_task(task_id=task_id)
        if not task.status or not task.version or task.version.status_version is None:
            print("Task status or version is missing")
            return

        current_task_status = task.status.status
        task_status_version = task.version.status_version
        task_status_version += 1
        
        if task_active:
            await client.tasks.update_task_status(
                task_id=task_id,
                status_version=task_status_version,
                new_status=TaskStatus(
                    # Because the cancellation is being rejected, we do not
                    # change the task status.
                    status=current_task_status,
                    task_error=TaskError(
                        code="ERROR_CODE_REJECTED",
                        message="Task is already active, and cannot be cancelled."
                    )
                ),
                author=Principal(
                    system=System(
                        entity_id=entity_id
                    )
                )
            )
            print("Task could not be cancelled.")
        else:
            await client.tasks.update_task_status(
                task_id=task_id,
                status_version=task_status_version,
                new_status=TaskStatus(
                    status="STATUS_DONE_NOT_OK",
                    task_error=TaskError(
                        code="ERROR_CODE_CANCELLED",
                        message="Task cancelled by agent."
                    )
                ),
                author=Principal(
                    system=System(
                        entity_id=entity_id
                    )
                )
            )
            print("Task has been cancelled.")
    except Exception as error:
        print(f"Encountered the following error while cancelling task: {error}")

async def complete_task(task_id, entity_id):
    try:
        # Get current task to retrieve status_version
        task = await client.tasks.get_task(task_id=task_id)
        if not task.version or task.version.status_version is None:
            print("Task version is missing")
            return

        task_status_version = task.version.status_version

        # Increment version and update to terminal state
        task_status_version += 1
        response = await client.tasks.update_task_status(
            task_id=task_id,
            status_version=task_status_version,
            new_status=TaskStatus(
                status="STATUS_DONE_OK"
            ),
            author=Principal(
                system=System(
                    entity_id=entity_id
                )
            )
        )
        return response
    except Exception as error:
        print(f"Encountered the following error while completing task: {error}")

async def main(entity_id):
    try:
        print(f"Streaming tasks for entity: {entity_id}...")
        requests = client.tasks.stream_as_agent(
            agent_selector=EntityIdsSelector(
                entity_ids=[entity_id]
            )
        )

        async for request in requests:
            if request.event == "heartbeat":
                print(f"Heartbeat: {request.timestamp}")
                continue

            if request.execute_request and request.execute_request.task and request.execute_request.task.version:
                version = request.execute_request.task.version
                task_id = version.task_id
                task_status_version = version.status_version
                task_description = request.execute_request.task.description

                print(f"Starting task {task_id}, version {task_status_version}: {task_description}")
                await start_task(task_id, task_status_version, entity_id)

            elif request.complete_request:
                task_id = request.complete_request.task_id
                print(f"Completing task: {task_id}")
                await complete_task(task_id, entity_id)

            elif request.cancel_request:
                task_id = request.cancel_request.task_id
                print(f"Cancelling task: {task_id}")
                await cancel_task(task_id, entity_id)

    except Exception as error:
        print(f"Exception: {error}")

if __name__ == "__main__":
    raise SystemExit(asyncio.run(main(entity_id="<AGENT_ID>")))

```

```ts title={"TypeScript (REST)"} startLine={163} maxLines={25}
import { LatticeClient } from "@anduril-industries/lattice-sdk";

// Entity ID to listen for tasks - replace with your actual entity ID.
const ENTITY_ID = "<AGENT_ID>";



const latticeEndpoint = process.env.LATTICE_ENDPOINT;
const environmentToken = process.env.ENVIRONMENT_TOKEN;
const sandboxesToken = process.env.SANDBOXES_TOKEN;
const taskActiveStr = process.env.TASK_ACTIVE;

if (!latticeEndpoint || !environmentToken || !sandboxesToken) {
    console.error("Missing required environment variables.");
    process.exit(1);
}

// Sets whether the task is currently being processed. If so, it cannot be cancelled.
const taskActive = taskActiveStr?.toLowerCase() === "true";

const client = new LatticeClient({
    baseUrl: latticeEndpoint.startsWith("https://") ? latticeEndpoint : `https://${latticeEndpoint}`,
    token: environmentToken,
    headers: { "Anduril-Sandbox-Authorization": `Bearer ${sandboxesToken}` }
});


// Update task status to STATUS_EXECUTING.
async function startTask(taskId: string, taskStatusVersion: number, agentEntityId: string) {
    try {
        // Increment status version for the update
        taskStatusVersion += 1;

        // Create system principal with the agent entity ID
        const principal = {
            system: {
                entityId: agentEntityId
            }
        };

        // Create task status for update
        const taskStatus = {
            status: "STATUS_EXECUTING" as const
        };

        // Update task status
        const response = await client.tasks.updateTaskStatus({
            taskId: taskId,
            statusVersion: taskStatusVersion,
            newStatus: taskStatus,
            author: principal
        });

        return response;
    } catch (error) {
        console.error(`Encountered the following error while starting task: ${error}`);
        throw error;
    }
}

// Handle cancellation requests from Lattice.
async function cancelTask(taskId: string, entityId: string) {
    try {
        // Get current task to retrieve status_version
        const task = await client.tasks.getTask({ taskId: taskId });

        if (!task.status || !task.version?.statusVersion) {
            console.error("Task status or version is missing");
            return;
        }

        const currentTaskStatus = task.status.status;
        let taskStatusVersion = task.version.statusVersion;
        taskStatusVersion += 1;

        // Create system principal with the agent entity ID
        const principal = {
            system: {
                entityId: entityId
            }
        };

        let taskStatus;
        if (taskActive) {
            // Reject cancellation: task is active and cannot be cancelled
            taskStatus = {
                // Because the cancellation is being rejected, we do not
                // change the task status.
                status: currentTaskStatus,
                taskError: {
                    code: "ERROR_CODE_REJECTED" as const,
                    message: "Task is already active, and cannot be cancelled."
                }
            };
        } else {
            // Accept cancellation
            taskStatus = {
                status: "STATUS_DONE_NOT_OK" as const,
                taskError: {
                    code: "ERROR_CODE_CANCELLED" as const,
                    message: "Task cancelled by agent."
                }
            };
        }

        await client.tasks.updateTaskStatus({
            taskId: taskId,
            statusVersion: taskStatusVersion,
            newStatus: taskStatus,
            author: principal
        });

        if (taskActive) {
            console.log("Task could not be cancelled.");
        } else {
            console.log("Task has been cancelled.");
        }
    } catch (error) {
        console.error(`Encountered the following error while cancelling task: ${error}`);
    }
}


// Update task status to STATUS_DONE_OK.
async function completeTask(taskId: string, entityId: string) {
    try {
        // Get current task to retrieve status_version
        const task = await client.tasks.getTask({ taskId: taskId });

        if (!task.version?.statusVersion) {
            console.error("Task version is missing");
            return;
        }

        let taskStatusVersion = task.version.statusVersion;
        // Increment version and update to terminal state
        taskStatusVersion += 1;

        // Create system principal with the agent entity ID
        const principal = {
            system: {
                entityId: entityId
            }
        };

        const taskStatus = {
            status: "STATUS_DONE_OK" as const
        };

        await client.tasks.updateTaskStatus({
            taskId: taskId,
            statusVersion: taskStatusVersion,
            newStatus: taskStatus,
            author: principal
        });

        console.log("Task has been completed.");
    } catch (error) {
        console.error(`Encountered the following error while completing task: ${error}`);
    }
}

async function App() {
    try {
        console.log(`Streaming tasks for entity: ${ENTITY_ID}...`);

        // Stream tasks from Lattice as an agent
        const requests = await client.tasks.streamAsAgent({
            agentSelector: {
                entityIds: [ENTITY_ID]
            }
        });

        for await (const request of requests) {
            if (request.event === "heartbeat") {
                console.log(`Heartbeat: ${request.timestamp}`);
                continue;
            }

            if (request.executeRequest) {
                const taskId = request.executeRequest.task?.version?.taskId;
                const taskStatusVersion = request.executeRequest.task?.version?.statusVersion;
                const description = request.executeRequest.task?.description || "No description";

                if (taskId && taskStatusVersion !== undefined) {
                    console.log(`Starting task ${taskId}, version ${taskStatusVersion}: ${description}`);

                    try {
                        const result = await startTask(taskId, taskStatusVersion, ENTITY_ID);
                        console.log(`Started task with status version: ${result.version?.statusVersion}`);
                    } catch (error) {
                        console.error(`Error starting task: ${error}`);
                    }
                }
            } else if (request.completeRequest) {
                const taskId = request.completeRequest.taskId;
                if (taskId) {
                    console.log(`Completing task: ${taskId}`);
                    try {
                        await completeTask(taskId, ENTITY_ID);
                    } catch (error) {
                        console.error(`Error completing task: ${error}`);
                    }
                }
            } else if (request.cancelRequest) {
                const taskId = request.cancelRequest.taskId;
                if (taskId) {
                    console.log(`Cancelling task: ${taskId}`);
                    try {
                        await cancelTask(taskId, ENTITY_ID);
                    } catch (error) {
                        console.error(`Error cancelling task: ${error}`);
                    }
                }
            }
        }
    } catch (error) {
        console.error(`Error in streaming loop: ${error}`);
    }
}

App();

```

```rs title={"Rust (REST)"} startLine={167} maxLines={25}
use anduril_lattice_sdk::prelude::*;

use std::env;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Sets whether the task is currently being processed; if so, it cannot be
    // cancelled.
    let task_active = env::var("TASK_ACTIVE")
        .map(|v| v.to_lowercase() == "true")
        .unwrap_or(false);

    let lattice_endpoint = env::var("LATTICE_ENDPOINT")
        .expect("LATTICE_ENDPOINT environment variable not set");
    let environment_token = env::var("ENVIRONMENT_TOKEN")
        .expect("ENVIRONMENT_TOKEN environment variable not set");
    // Remove sandboxes_token if you are not developing on Sandboxes.
    let sandboxes_token = env::var("SANDBOXES_TOKEN")
        .expect("SANDBOXES_TOKEN environment variable not set");

    if environment_token.is_empty() || lattice_endpoint.is_empty() || sandboxes_token.is_empty() {
        eprintln!("Missing required environment variables:");
        eprintln!("  LATTICE_ENDPOINT");
        eprintln!("  ENVIRONMENT_TOKEN");
        eprintln!("  SANDBOXES_TOKEN");
        std::process::exit(1);
    }

    let config = ClientConfig {
        base_url: format!("https://{}", lattice_endpoint),
        token: Some(environment_token),
        // Remove custom_headers if you are not developing on Sandboxes.
        custom_headers: HashMap::from([(
            "anduril-sandbox-authorization".to_string(),
            format!("Bearer {}", sandboxes_token),
        )]),
        ..Default::default()
    };
    let client = Lattice::new(config)?;

    // Replace with the entity ID of the agent to process tasks for.
    let entity_id = "<AGENT_ID>";
    run(&client, entity_id, task_active).await;

    Ok(())
}

/// Author principal for a system entity.
fn system_author(entity_id: &str) -> Principal {
    Principal {
        system: Some(System {
            entity_id: Some(entity_id.to_string()),
            ..Default::default()
        }),
        ..Default::default()
    }
}

/// Move a task into the executing state by advancing its status version.
async fn start_task(client: &Lattice, task_id: &str, task_status_version: i64, entity_id: &str) {
    let request = TaskStatusUpdate {
        status_version: Some(task_status_version + 1),
        new_status: Some(TaskStatus {
            status: Some(TaskStatusStatus::StatusExecuting),
            ..Default::default()
        }),
        author: Some(system_author(entity_id)),
    };

    if let Err(error) = client.tasks.update_task_status(task_id, &request, None).await {
        eprintln!("Encountered the following error while starting task: {}", error);
    }
}

/// Cancel a task, or reject the cancellation if the task is actively executing.
async fn cancel_task(client: &Lattice, task_id: &str, entity_id: &str, task_active: bool) {
    // Get the current task to retrieve its status_version.
    let task = match client.tasks.get_task(task_id, None).await {
        Ok(task) => task,
        Err(error) => {
            eprintln!("Encountered the following error while cancelling task: {}", error);
            return;
        }
    };

    let (Some(status), Some(version)) = (task.status.as_ref(), task.version.as_ref()) else {
        println!("Task status or version is missing");
        return;
    };
    let Some(current_status_version) = version.status_version else {
        println!("Task status or version is missing");
        return;
    };
    let current_task_status = status.status.clone();
    let next_status_version = current_status_version + 1;

    let request = if task_active {
        // Because the cancellation is being rejected, we do not change the task
        // status.
        TaskStatusUpdate {
            status_version: Some(next_status_version),
            new_status: Some(TaskStatus {
                status: current_task_status,
                task_error: Some(TaskError {
                    code: Some(TaskErrorCode::ErrorCodeRejected),
                    message: Some("Task is already active, and cannot be cancelled.".to_string()),
                    ..Default::default()
                }),
                ..Default::default()
            }),
            author: Some(system_author(entity_id)),
        }
    } else {
        TaskStatusUpdate {
            status_version: Some(next_status_version),
            new_status: Some(TaskStatus {
                status: Some(TaskStatusStatus::StatusDoneNotOk),
                task_error: Some(TaskError {
                    code: Some(TaskErrorCode::ErrorCodeCancelled),
                    message: Some("Task cancelled by agent.".to_string()),
                    ..Default::default()
                }),
                ..Default::default()
            }),
            author: Some(system_author(entity_id)),
        }
    };

    match client.tasks.update_task_status(task_id, &request, None).await {
        Ok(_) if task_active => println!("Task could not be cancelled."),
        Ok(_) => println!("Task has been cancelled."),
        Err(error) => eprintln!("Encountered the following error while cancelling task: {}", error),
    }
}

/// Complete a task by advancing it to the terminal STATUS_DONE_OK state.
async fn complete_task(client: &Lattice, task_id: &str, entity_id: &str) {
    // Get the current task to retrieve its status_version.
    let task = match client.tasks.get_task(task_id, None).await {
        Ok(task) => task,
        Err(error) => {
            eprintln!("Encountered the following error while completing task: {}", error);
            return;
        }
    };

    let Some(current_status_version) = task.version.and_then(|v| v.status_version) else {
        println!("Task version is missing");
        return;
    };

    let request = TaskStatusUpdate {
        // Increment version and update to a terminal state.
        status_version: Some(current_status_version + 1),
        new_status: Some(TaskStatus {
            status: Some(TaskStatusStatus::StatusDoneOk),
            ..Default::default()
        }),
        author: Some(system_author(entity_id)),
    };

    if let Err(error) = client.tasks.update_task_status(task_id, &request, None).await {
        eprintln!("Encountered the following error while completing task: {}", error);
    }
}

/// Stream tasks for the agent and route each request to the matching handler.
async fn run(client: &Lattice, entity_id: &str, task_active: bool) {
    println!("Streaming tasks for entity: {}...", entity_id);

    let request = AgentStreamRequest {
        agent_selector: Some(EntityIdsSelector {
            entity_ids: Some(vec![entity_id.to_string()]),
            ..Default::default()
        }),
        ..Default::default()
    };

    let stream = match client.tasks.stream_as_agent(&request, None).await {
        Ok(stream) => stream,
        Err(error) => {
            eprintln!("Exception: {}", error);
            return;
        }
    };
    let mut stream = std::pin::pin!(stream);

    while let Some(result) = stream.next().await {
        match result {
            Ok(StreamAsAgentResponse::Heartbeat { data, .. }) => {
                println!("Heartbeat: {:?}", data.heartbeat_object_fields.timestamp);
            }
            Ok(StreamAsAgentResponse::AgentRequest { data, .. }) => {
                let fields = &data.agent_task_request_fields;
                if let Some(execute) = &fields.execute_request {
                    if let Some(task) = &execute.task {
                        if let Some(version) = &task.version {
                            let task_id = version.task_id.clone().unwrap_or_default();
                            let task_status_version = version.status_version.unwrap_or(0);
                            println!(
                                "Starting task {}, version {}: {:?}",
                                task_id, task_status_version, task.description
                            );
                            start_task(client, &task_id, task_status_version, entity_id).await;
                        }
                    }
                } else if let Some(complete) = &fields.complete_request {
                    if let Some(task_id) = &complete.task_id {
                        println!("Completing task: {}", task_id);
                        complete_task(client, task_id, entity_id).await;
                    }
                } else if let Some(cancel) = &fields.cancel_request {
                    if let Some(task_id) = &cancel.task_id {
                        println!("Cancelling task: {}", task_id);
                        cancel_task(client, task_id, entity_id, task_active).await;
                    }
                }
            }
            Ok(other) => println!("Event: {:?}", other),
            Err(error) => eprintln!("Exception: {}", error),
        }
    }
}

```

```go title={"Go (gRPC)"} startLine={59} maxLines={25}
// This Go example is compatible with artifacts generated using
// the following grpc/go plugin: https://buf.build/anduril/lattice-sdk/sdks/main:grpc/go
package main

import (
	"context"
	"fmt"
	"io"
	"log"
	"os"
	"strings"
	"time"

	"schema-registry.developer.anduril.com/gen/go/anduril/lattice-sdk/grpc/go/anduril/taskmanager/v1/taskmanagerv1grpc"
	taskmanagerv1 "schema-registry.developer.anduril.com/gen/go/anduril/lattice-sdk/protocolbuffers/go/anduril/taskmanager/v1"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"
)

// Sets whether the task is currently being processed; if so, it cannot be cancelled.
var taskActive bool

func main() {
	environmentToken := os.Getenv("ENVIRONMENT_TOKEN")
	latticeEndpoint := os.Getenv("LATTICE_ENDPOINT")
	sandboxesToken := os.Getenv("SANDBOXES_TOKEN")
	taskActiveStr := os.Getenv("TASK_ACTIVE")

	if latticeEndpoint == "" || environmentToken == "" || sandboxesToken == "" {
		fmt.Println("Missing required environment variables")
		os.Exit(1)
	}

	// Parse TASK_ACTIVE environment variable
	taskActive = strings.ToLower(taskActiveStr) == "true"

	// Setup authentication
	auth := &BearerTokenAuth{
		Token:          environmentToken,
		SandboxesToken: sandboxesToken,
	}

	opts := []grpc.DialOption{
		grpc.WithTransportCredentials(credentials.NewClientTLSFromCert(nil, "")),
		grpc.WithPerRPCCredentials(auth),
	}

	conn, err := grpc.NewClient(latticeEndpoint, opts...)
	if err != nil {
		log.Fatalf("Did not connect: %v", err)
	}
	defer conn.Close()

	client := taskmanagerv1grpc.NewTaskManagerAPIClient(conn)

	// Set the entity ID to listen for tasks
	entityId := "<AGENT_ID>"
	fmt.Printf("Listening for tasks for entity %s...\n", entityId)

	for {
		try := func() bool {
			ctx := context.Background()

			// Create the ListenAsAgent request
			listenRequest := &taskmanagerv1.ListenAsAgentRequest{
				AgentSelector: &taskmanagerv1.ListenAsAgentRequest_EntityIds{
					EntityIds: &taskmanagerv1.EntityIds{
						EntityIds: []string{entityId},
					},
				},
			}

			// Call the ListenAsAgent method to get a stream of task requests
			stream, err := client.ListenAsAgent(ctx, listenRequest)
			if err != nil {
				fmt.Printf("Error starting task listener: %v\n", err)
				return false
			}

			for {
				response, err := stream.Recv()
				if err == io.EOF {
					fmt.Println("Stream ended")
					return false
				}
				if err != nil {
					fmt.Printf("Error receiving from stream: %v\n", err)
					return false
				}

				// Process the response based on the type of task request
				switch x := response.Request.(type) {
				case *taskmanagerv1.ListenAsAgentResponse_ExecuteRequest:
					if x.ExecuteRequest != nil && x.ExecuteRequest.Task != nil &&
						x.ExecuteRequest.Task.Version != nil && x.ExecuteRequest.Task.Version.TaskId != "" {
						// Extract task-specific information
						taskId := x.ExecuteRequest.Task.Version.TaskId
						taskStatusVersion := x.ExecuteRequest.Task.Version.StatusVersion

						fmt.Printf("Starting task %s, version %d\n", taskId, taskStatusVersion)

						// Update task status to STATUS_EXECUTING
						result, err := executeTask(ctx, client, taskId, taskStatusVersion, entityId)
						if err != nil {
							fmt.Printf("Error starting task: %v\n", err)
							return false
						}

						fmt.Printf("Started task with status version: %d\n", result.StatusVersion)
					}

				case *taskmanagerv1.ListenAsAgentResponse_CompleteRequest:
					if x.CompleteRequest != nil && x.CompleteRequest.TaskId != "" {
						fmt.Printf("Completing task: %s\n", x.CompleteRequest.TaskId)
						err := completeTask(ctx, client, x.CompleteRequest.TaskId, entityId)
						if err != nil {
							fmt.Printf("Error completing task: %v\n", err)
						}
					}

				case *taskmanagerv1.ListenAsAgentResponse_CancelRequest:
					if x.CancelRequest != nil && x.CancelRequest.TaskId != "" {
						fmt.Printf("Cancelling task: %s\n", x.CancelRequest.TaskId)
						err := cancelTask(ctx, client, x.CancelRequest.TaskId, entityId)
						if err != nil {
							fmt.Printf("Error cancelling task: %v\n", err)
						}
					}
				}
			}
		}()

		if !try {
			// If there was an error, wait a bit before retrying
			time.Sleep(1 * time.Second)
		}
	}
}

// executeTask updates the task status to STATUS_EXECUTING
func executeTask(ctx context.Context, client taskmanagerv1grpc.TaskManagerAPIClient, taskId string, taskStatusVersion uint32, agentEntityId string) (*taskmanagerv1.TaskVersion, error) {
	// Increment status version for the update
	taskStatusVersion++

	// Create system principal with the agent entity ID
	principal := &taskmanagerv1.Principal{
		Agent: &taskmanagerv1.Principal_System{
			System: &taskmanagerv1.System{
				EntityId: agentEntityId,
			},
		},
	}

	// Create task status update
	taskStatus := &taskmanagerv1.TaskStatus{
		Status: taskmanagerv1.Status_STATUS_EXECUTING,
	}

	// Create status update request
	statusUpdate := &taskmanagerv1.StatusUpdate{
		Version: &taskmanagerv1.TaskVersion{
			TaskId:            taskId,
			DefinitionVersion: 0,
			StatusVersion:     taskStatusVersion,
		},
		Status: taskStatus,
		Author: principal,
	}

	// Create the update status request
	updateRequest := &taskmanagerv1.UpdateStatusRequest{
		StatusUpdate: statusUpdate,
	}

	// Call the UpdateStatus API
	response, err := client.UpdateStatus(ctx, updateRequest)
	if err != nil {
		return nil, fmt.Errorf("error updating task status: %w", err)
	}

	return response.Task.Version, nil
}

// cancelTask handles cancellation requests from Lattice
func cancelTask(ctx context.Context, client taskmanagerv1grpc.TaskManagerAPIClient, taskId string, entityId string) error {
	// Get current task to retrieve status_version
	getTaskRequest := &taskmanagerv1.GetTaskRequest{
		TaskId: taskId,
	}
	taskResponse, err := client.GetTask(ctx, getTaskRequest)
	if err != nil {
		return fmt.Errorf("error getting task: %w", err)
	}
	if taskResponse.Task == nil || taskResponse.Task.Status == nil || taskResponse.Task.Version == nil {
		return fmt.Errorf("task status or version is missing")
	}
	currentTaskStatus := taskResponse.Task.Status.Status
	taskStatusVersion := taskResponse.Task.Version.StatusVersion
	taskStatusVersion++

	// Create system principal with the agent entity ID
	principal := &taskmanagerv1.Principal{
		Agent: &taskmanagerv1.Principal_System{
			System: &taskmanagerv1.System{
				EntityId: entityId,
			},
		},
	}

	if taskActive {
		// Reject cancellation: task is active and cannot be cancelled
		taskStatus := &taskmanagerv1.TaskStatus{
			// Because the cancellation is being rejected, we do not
			// change the task status.
			Status: currentTaskStatus,
			TaskError: &taskmanagerv1.TaskError{
				Code:    taskmanagerv1.ErrorCode_ERROR_CODE_REJECTED,
				Message: "Task is already active, and cannot be cancelled.",
			},
		}

		statusUpdate := &taskmanagerv1.StatusUpdate{
			Version: &taskmanagerv1.TaskVersion{
				TaskId:            taskId,
				DefinitionVersion: 0,
				StatusVersion:     taskStatusVersion,
			},
			Status: taskStatus,
			Author: principal,
		}

		updateRequest := &taskmanagerv1.UpdateStatusRequest{
			StatusUpdate: statusUpdate,
		}

		_, err := client.UpdateStatus(ctx, updateRequest)
		if err != nil {
			return fmt.Errorf("error updating task status: %w", err)
		}

		log.Println("Task could not be cancelled.")
	} else {
		// Accept cancellation
		taskStatus := &taskmanagerv1.TaskStatus{
			Status: taskmanagerv1.Status_STATUS_DONE_NOT_OK,
			TaskError: &taskmanagerv1.TaskError{
				Code:    taskmanagerv1.ErrorCode_ERROR_CODE_CANCELLED,
				Message: "Task cancelled by agent.",
			},
		}

		statusUpdate := &taskmanagerv1.StatusUpdate{
			Version: &taskmanagerv1.TaskVersion{
				TaskId:            taskId,
				DefinitionVersion: 0,
				StatusVersion:     taskStatusVersion,
			},
			Status: taskStatus,
			Author: principal,
		}

		updateRequest := &taskmanagerv1.UpdateStatusRequest{
			StatusUpdate: statusUpdate,
		}

		_, err := client.UpdateStatus(ctx, updateRequest)
		if err != nil {
			return fmt.Errorf("error updating task status: %w", err)
		}

		log.Println("Task has been cancelled.")
	}

	return nil
}

// completeTask updates the task status to STATUS_DONE_OK
func completeTask(ctx context.Context, client taskmanagerv1grpc.TaskManagerAPIClient, taskId string, entityId string) error {
	// Get current task to retrieve status_version
	getTaskRequest := &taskmanagerv1.GetTaskRequest{
		TaskId: taskId,
	}

	taskResponse, err := client.GetTask(ctx, getTaskRequest)
	if err != nil {
		return fmt.Errorf("error getting task: %w", err)
	}

	if taskResponse.Task == nil || taskResponse.Task.Version == nil {
		return fmt.Errorf("task version is missing")
	}

	taskStatusVersion := taskResponse.Task.Version.StatusVersion
	// Increment version and update to terminal state
	taskStatusVersion++

	// Create system principal with the agent entity ID
	principal := &taskmanagerv1.Principal{
		Agent: &taskmanagerv1.Principal_System{
			System: &taskmanagerv1.System{
				EntityId: entityId,
			},
		},
	}

	taskStatus := &taskmanagerv1.TaskStatus{
		Status: taskmanagerv1.Status_STATUS_DONE_OK,
	}

	statusUpdate := &taskmanagerv1.StatusUpdate{
		Version: &taskmanagerv1.TaskVersion{
			TaskId:            taskId,
			DefinitionVersion: 0,
			StatusVersion:     taskStatusVersion,
		},
		Status: taskStatus,
		Author: principal,
	}

	updateRequest := &taskmanagerv1.UpdateStatusRequest{
		StatusUpdate: statusUpdate,
	}

	_, err = client.UpdateStatus(ctx, updateRequest)
	if err != nil {
		return fmt.Errorf("error updating task status: %w", err)
	}

	return nil
}

```

```py title={"Python (gRPC)"} startLine={135} maxLines={25}
# This Python example is compatible with artifacts generated using the following
# Buf plugins: https://schema-registry.developer.anduril.com/anduril/lattice-sdk/sdks/main:bufbuild/py
# and https://schema-registry.developer.anduril.com/anduril/lattice-sdk/sdks/main:connectrpc/py

import os
import sys
import time
from bearer_auth import create_client
from protobuf import Oneof

from anduril.taskmanager.v1.task_pub_pb import (
    ErrorCode,
    Principal,
    Status,
    System,
    TaskError,
    TaskStatus,
    TaskVersion,
    StatusUpdate,
)
from anduril.taskmanager.v1.task_manager_api_pub_pb import (
    EntityIds,
    GetTaskRequest,
    ListenAsAgentRequest,
    UpdateStatusRequest,
)
from anduril.taskmanager.v1.task_manager_api_pub_connect import TaskManagerAPIClientSync


def execute_task(client, task_id, task_status_version, agent_entity_id):
    """Update the task status to STATUS_EXECUTING."""
    task_status_version += 1

    principal = Principal(agent=Oneof("system", System(entity_id=agent_entity_id)))
    task_status = TaskStatus(status=Status.EXECUTING)

    status_update = StatusUpdate(
        version=TaskVersion(task_id=task_id, definition_version=0, status_version=task_status_version),
        status=task_status,
        author=principal,
    )

    response = client.update_status(UpdateStatusRequest(status_update=status_update))
    return response.task.version


def cancel_task(client, task_id, entity_id, task_active):
    """Handle a cancellation request from Lattice."""
    task_response = client.get_task(GetTaskRequest(task_id=task_id))
    if not task_response.task or not task_response.task.status or not task_response.task.version:
        raise RuntimeError("task status or version is missing")

    current_task_status = task_response.task.status.status
    task_status_version = task_response.task.version.status_version + 1

    principal = Principal(agent=Oneof("system", System(entity_id=entity_id)))

    if task_active:
        # Reject cancellation: task is active and cannot be cancelled
        task_status = TaskStatus(
            # Because the cancellation is being rejected, we do not
            # change the task status.
            status=current_task_status,
            task_error=TaskError(
                code=ErrorCode.REJECTED,
                message="Task is already active, and cannot be cancelled.",
            ),
        )
        status_update = StatusUpdate(
            version=TaskVersion(task_id=task_id, definition_version=0, status_version=task_status_version),
            status=task_status,
            author=principal,
        )
        client.update_status(UpdateStatusRequest(status_update=status_update))
        print("Task could not be cancelled.")
    else:
        # Accept cancellation
        task_status = TaskStatus(
            status=Status.DONE_NOT_OK,
            task_error=TaskError(
                code=ErrorCode.CANCELLED,
                message="Task cancelled by agent.",
            ),
        )
        status_update = StatusUpdate(
            version=TaskVersion(task_id=task_id, definition_version=0, status_version=task_status_version),
            status=task_status,
            author=principal,
        )
        client.update_status(UpdateStatusRequest(status_update=status_update))
        print("Task has been cancelled.")


def complete_task(client, task_id, entity_id):
    """Update the task status to STATUS_DONE_OK."""
    task_response = client.get_task(GetTaskRequest(task_id=task_id))
    if not task_response.task or not task_response.task.version:
        raise RuntimeError("task version is missing")

    task_status_version = task_response.task.version.status_version + 1

    principal = Principal(agent=Oneof("system", System(entity_id=entity_id)))
    task_status = TaskStatus(status=Status.DONE_OK)

    status_update = StatusUpdate(
        version=TaskVersion(task_id=task_id, definition_version=0, status_version=task_status_version),
        status=task_status,
        author=principal,
    )
    client.update_status(UpdateStatusRequest(status_update=status_update))


def main():
    environment_token = os.getenv("ENVIRONMENT_TOKEN")
    lattice_endpoint = os.getenv("LATTICE_ENDPOINT")
    sandboxes_token = os.getenv("SANDBOXES_TOKEN")

    # Sets whether the task is currently being processed; if so, it cannot be cancelled.
    task_active = os.getenv("TASK_ACTIVE", "").lower() == "true"

    if not environment_token or not lattice_endpoint or not sandboxes_token:
        print("Missing required environment variables", file=sys.stderr)
        sys.exit(1)

    # Create the API client over Connect gRPC with authentication
    client = create_client(TaskManagerAPIClientSync, lattice_endpoint, environment_token, sandboxes_token)

    entity_id = "<AGENT_ID>"
    print(f"Listening for tasks for entity {entity_id}...")

    while True:
        try:
            listen_request = ListenAsAgentRequest(
                agent_selector=Oneof("entity_ids", EntityIds(entity_ids=[entity_id]))
            )
            stream = client.listen_as_agent(listen_request)

            for response in stream:
                request = response.request
                if request is None:
                    continue
                if request.field == "execute_request":
                    task = request.value.task
                    if task and task.version and task.version.task_id:
                        task_id = task.version.task_id
                        task_status_version = task.version.status_version
                        print(f"Starting task {task_id}, version {task_status_version}")

                        try:
                            result = execute_task(client, task_id, task_status_version, entity_id)
                            print(f"Started task with status version: {result.status_version}")
                        except Exception as error:
                            print(f"Error starting task: {error}", file=sys.stderr)
                            break
                elif request.field == "complete_request":
                    if request.value.task_id:
                        print(f"Completing task: {request.value.task_id}")
                        try:
                            complete_task(client, request.value.task_id, entity_id)
                        except Exception as error:
                            print(f"Error completing task: {error}", file=sys.stderr)
                elif request.field == "cancel_request":
                    if request.value.task_id:
                        print(f"Cancelling task: {request.value.task_id}")
                        try:
                            cancel_task(client, request.value.task_id, entity_id, task_active)
                        except Exception as error:
                            print(f"Error cancelling task: {error}", file=sys.stderr)
        except Exception as error:
            print(f"Error in stream: {error}", file=sys.stderr)

        # If the stream ended or errored, wait a bit before retrying
        time.sleep(1)


if __name__ == "__main__":
    main()

```

```rs title={"Rust (gRPC)"} startLine={67} maxLines={25}
// This Rust example is compatible with artifacts generated using
// the following grpc/rust plugin: https://buf.build/anduril/lattice-sdk/sdks/main:community/neoeinstein-tonic
mod bearer_auth;

use anduril_lattice_sdk_community_neoeinstein_prost::anduril::taskmanager::v1::{
    listen_as_agent_request, listen_as_agent_response, principal, EntityIds, ErrorCode,
    GetTaskRequest, ListenAsAgentRequest, Principal, Status, StatusUpdate, System, TaskError,
    TaskStatus, TaskVersion, UpdateStatusRequest,
};
use anduril_lattice_sdk_community_neoeinstein_tonic::anduril::taskmanager::v1::tonic::task_manager_api_client::TaskManagerApiClient;
use bearer_auth::EnvironmentTokenAuth;
use tonic::metadata::MetadataValue;
use tonic::service::interceptor::InterceptedService;
use tonic::transport::{Channel, ClientTlsConfig};
use tonic::Request;

use std::env;
use std::time::Duration;

type Interceptor = Box<dyn FnMut(Request<()>) -> Result<Request<()>, tonic::Status> + Send + Sync>;
type TaskClient = TaskManagerApiClient<InterceptedService<Channel, Interceptor>>;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let environment_token = env::var("ENVIRONMENT_TOKEN")
        .expect("ENVIRONMENT_TOKEN environment variable not set");
    let lattice_endpoint = env::var("LATTICE_ENDPOINT")
        .expect("LATTICE_ENDPOINT environment variable not set");
    let sandboxes_token = env::var("SANDBOXES_TOKEN")
        .expect("SANDBOXES_TOKEN environment variable not set");

    // When true, the agent cannot accept cancellations for an active task.
    let task_active = env::var("TASK_ACTIVE")
        .map(|v| v.eq_ignore_ascii_case("true"))
        .unwrap_or(false);

    let auth = EnvironmentTokenAuth::new(
        environment_token,
        sandboxes_token,
    );

    let tls_config = ClientTlsConfig::new().with_native_roots();
    let channel = Channel::from_shared(format!("https://{}", lattice_endpoint))?
        .tls_config(tls_config)?
        .connect()
        .await?;

    let access_token = auth.get_token().await?;
    let sandboxes_token = auth.sandboxes_token().to_string();
    let auth_header: MetadataValue<_> = format!("Bearer {}", access_token).parse()?;
    let sandbox_header: MetadataValue<_> = format!("Bearer {}", sandboxes_token).parse()?;

    let interceptor: Interceptor = Box::new(move |mut req: Request<()>| {
        req.metadata_mut()
            .insert("authorization", auth_header.clone());
        req.metadata_mut()
            .insert("anduril-sandbox-authorization", sandbox_header.clone());
        Ok(req)
    });
    let mut client: TaskClient = TaskManagerApiClient::with_interceptor(channel, interceptor);

    // Set the entity ID to listen for tasks
    let entity_id = "<AGENT_ID>".to_string();
    println!("Listening for tasks for entity {}...", entity_id);

    loop {
        let request = Request::new(ListenAsAgentRequest {
            agent_selector: Some(listen_as_agent_request::AgentSelector::EntityIds(EntityIds {
                entity_ids: vec![entity_id.clone()],
            })),
            ..Default::default()
        });

        let mut stream = match client.listen_as_agent(request).await {
            Ok(resp) => resp.into_inner(),
            Err(e) => {
                eprintln!("Error starting task listener: {}", e);
                tokio::time::sleep(Duration::from_secs(1)).await;
                continue;
            }
        };

        loop {
            let response = match stream.message().await {
                Ok(Some(r)) => r,
                Ok(None) => {
                    println!("Stream ended");
                    break;
                }
                Err(e) => {
                    eprintln!("Error receiving from stream: {}", e);
                    break;
                }
            };

            match response.request {
                Some(listen_as_agent_response::Request::ExecuteRequest(execute)) => {
                    if let Some(task) = execute.task {
                        if let Some(version) = task.version {
                            let task_id = version.task_id.clone();
                            let status_version = version.status_version;
                            println!("Starting task {}, version {}", task_id, status_version);

                            match execute_task(&mut client, &task_id, status_version, &entity_id)
                                .await
                            {
                                Ok(v) => println!(
                                    "Started task with status version: {}",
                                    v.status_version
                                ),
                                Err(e) => eprintln!("Error starting task: {}", e),
                            }
                        }
                    }
                }
                Some(listen_as_agent_response::Request::CompleteRequest(complete)) => {
                    if !complete.task_id.is_empty() {
                        println!("Completing task: {}", complete.task_id);
                        if let Err(e) =
                            complete_task(&mut client, &complete.task_id, &entity_id).await
                        {
                            eprintln!("Error completing task: {}", e);
                        }
                    }
                }
                Some(listen_as_agent_response::Request::CancelRequest(cancel)) => {
                    if !cancel.task_id.is_empty() {
                        println!("Cancelling task: {}", cancel.task_id);
                        if let Err(e) =
                            cancel_task(&mut client, &cancel.task_id, &entity_id, task_active)
                                .await
                        {
                            eprintln!("Error cancelling task: {}", e);
                        }
                    }
                }
                None => {}
            }
        }

        tokio::time::sleep(Duration::from_secs(1)).await;
    }
}

fn system_principal(entity_id: &str) -> Principal {
    Principal {
        agent: Some(principal::Agent::System(System {
            entity_id: entity_id.to_string(),
            ..Default::default()
        })),
        ..Default::default()
    }
}

// execute_task updates the task status to STATUS_EXECUTING.
async fn execute_task(
    client: &mut TaskClient,
    task_id: &str,
    task_status_version: u32,
    agent_entity_id: &str,
) -> Result<TaskVersion, tonic::Status> {
    let status_update = StatusUpdate {
        version: Some(TaskVersion {
            task_id: task_id.to_string(),
            status_version: task_status_version + 1,
            definition_version: 0,
            ..Default::default()
        }),
        status: Some(TaskStatus {
            status: Status::Executing as i32,
            ..Default::default()
        }),
        author: Some(system_principal(agent_entity_id)),
        ..Default::default()
    };

    let response = client
        .update_status(Request::new(UpdateStatusRequest {
            status_update: Some(status_update),
        }))
        .await?;

    response
        .into_inner()
        .task
        .and_then(|t| t.version)
        .ok_or_else(|| tonic::Status::internal("missing task version in response"))
}

// cancel_task handles cancellation requests from Lattice.
async fn cancel_task(
    client: &mut TaskClient,
    task_id: &str,
    entity_id: &str,
    task_active: bool,
) -> Result<(), tonic::Status> {
    // Get current task to retrieve status_version
    let task_response = client
        .get_task(Request::new(GetTaskRequest {
            task_id: task_id.to_string(),
            ..Default::default()
        }))
        .await?
        .into_inner()
        .task
        .ok_or_else(|| tonic::Status::internal("task is missing"))?;

    let current_status = task_response
        .status
        .as_ref()
        .map(|s| s.status)
        .unwrap_or(0);
    let version = task_response
        .version
        .ok_or_else(|| tonic::Status::internal("task version is missing"))?;
    let next_status_version = version.status_version + 1;

    let principal = system_principal(entity_id);

    let status_update = if task_active {
        // Reject cancellation: task is active and cannot be cancelled. Because
        // the cancellation is being rejected, we do not change the task status.
        StatusUpdate {
            version: Some(TaskVersion {
                task_id: task_id.to_string(),
                status_version: next_status_version,
                definition_version: 0,
                ..Default::default()
            }),
            status: Some(TaskStatus {
                status: current_status,
                task_error: Some(TaskError {
                    code: ErrorCode::Rejected as i32,
                    message: "Task is already active, and cannot be cancelled.".to_string(),
                    ..Default::default()
                }),
                ..Default::default()
            }),
            author: Some(principal),
            ..Default::default()
        }
    } else {
        // Accept cancellation
        StatusUpdate {
            version: Some(TaskVersion {
                task_id: task_id.to_string(),
                status_version: next_status_version,
                definition_version: 0,
                ..Default::default()
            }),
            status: Some(TaskStatus {
                status: Status::DoneNotOk as i32,
                task_error: Some(TaskError {
                    code: ErrorCode::Cancelled as i32,
                    message: "Task cancelled by agent.".to_string(),
                    ..Default::default()
                }),
                ..Default::default()
            }),
            author: Some(principal),
            ..Default::default()
        }
    };

    client
        .update_status(Request::new(UpdateStatusRequest {
            status_update: Some(status_update),
        }))
        .await?;

    if task_active {
        println!("Task could not be cancelled.");
    } else {
        println!("Task has been cancelled.");
    }
    Ok(())
}

// complete_task updates the task status to STATUS_DONE_OK.
async fn complete_task(
    client: &mut TaskClient,
    task_id: &str,
    entity_id: &str,
) -> Result<(), tonic::Status> {
    // Get current task to retrieve status_version
    let version = client
        .get_task(Request::new(GetTaskRequest {
            task_id: task_id.to_string(),
            ..Default::default()
        }))
        .await?
        .into_inner()
        .task
        .and_then(|t| t.version)
        .ok_or_else(|| tonic::Status::internal("task version is missing"))?;

    let status_update = StatusUpdate {
        version: Some(TaskVersion {
            task_id: task_id.to_string(),
            // Increment version and update to terminal state
            status_version: version.status_version + 1,
            definition_version: 0,
            ..Default::default()
        }),
        status: Some(TaskStatus {
            status: Status::DoneOk as i32,
            ..Default::default()
        }),
        author: Some(system_principal(entity_id)),
        ..Default::default()
    };

    client
        .update_status(Request::new(UpdateStatusRequest {
            status_update: Some(status_update),
        }))
        .await?;
    Ok(())
}

```

The [`StreamAsAgent`](/reference/rest/tasks/stream-as-agent) API establishes a server-sent events (SSE) stream that delivers three types of requests:
**`executeRequest`** which notifies the agent to start executing a task, **`completeRequest`** which requests the agent to complete a task, and
**`cancelRequest`** which requests the agent to cancel a task.

#### Handle cancellation requests

When a `cancelRequest` arrives, the agent retrieves the current task state and decides whether to accept or reject the cancellation.

**Rejecting cancellation:**

If the task is active and cannot be cancelled, the agent rejects the cancellation by
keeping the current status and attaching a [`TaskError`](/reference/rest/tasks/update-task-status#request.body.newStatus.taskError)
with code [`ERROR_CODE_REJECTED`](/reference/rest/tasks/update-task-status#request.body.newStatus.taskError.code):

```go title={"Go (REST)"} startLine={197} maxLines={18}
package main

import (
	"context"
	"errors"
	"fmt"
	"io"
	"log"
	"net/http"
	"os"
	"strings"
	"time"

	Lattice "github.com/anduril/lattice-sdk-go/v5"
	"github.com/anduril/lattice-sdk-go/v5/client"
	"github.com/anduril/lattice-sdk-go/v5/option"
)

// Sets whether the task is currently being processed. If so, it cannot be cancelled.
var taskActive bool

func main() {
	// Get environment variables
	latticeEndpoint := os.Getenv("LATTICE_ENDPOINT")
	environmentToken := os.Getenv("ENVIRONMENT_TOKEN")
	sandboxesToken := os.Getenv("SANDBOXES_TOKEN")
	taskActiveStr := os.Getenv("TASK_ACTIVE")

	// Check required environment variables
	if latticeEndpoint == "" || environmentToken == "" || sandboxesToken == "" {
		fmt.Println("Missing required environment variables")
		os.Exit(1)
	}

	// Parse TASK_ACTIVE environment variable
	taskActive = strings.ToLower(taskActiveStr) == "true"

	// Initialize headers for sandbox authorization
	headers := http.Header{}
	headers.Add("Anduril-Sandbox-Authorization", fmt.Sprintf("Bearer %s", sandboxesToken))

	// Create the client
	LatticeClient := client.NewClient(
		option.WithToken(environmentToken),
		option.WithBaseURL(fmt.Sprintf("https://%s", latticeEndpoint)),
		option.WithHTTPHeader(headers),
	)

	// Set the entity ID to listen for tasks
	entityId := "<AGENT_ID>"
	fmt.Printf("Streaming tasks for entity: %s...\n", entityId)

	// Create context for the request
	ctx := context.Background()

	// Create agent stream request
	agentStreamRequest := Lattice.AgentStreamRequest{
		AgentSelector: &Lattice.EntityIDsSelector{
			EntityIDs: []string{entityId},
		},
	}

	// Stream tasks
	stream, err := LatticeClient.Tasks.StreamAsAgent(ctx, &agentStreamRequest)
	if err != nil {
		fmt.Printf("Error streaming tasks: %v\n", err)
		os.Exit(1)
	}

	// Process stream events
	for {
		select {
		case <-ctx.Done():
			log.Printf("Context canceled: %v", ctx.Err())
			return
		default:
			// Continue processing
		}

		event, err := stream.Recv()

		if errors.Is(err, io.EOF) {
			log.Println("Stream completed successfully.")
			return
		}

		if err != nil {
			log.Printf("Error receiving message: %v", err)
			continue
		}

		if event.Event == "heartbeat" {
			timestamp := *event.Heartbeat.Timestamp
			log.Printf("Heartbeat: %s", timestamp)
		} else {
			request := event.GetAgentRequest()
			if executeRequest := request.GetExecuteRequest(); executeRequest != nil {
				task := executeRequest.GetTask()
				if task != nil {
					taskId := *task.GetVersion().GetTaskID()
					taskStatusVersion := *task.GetVersion().GetStatusVersion()
					description := *task.GetDescription()

					log.Printf("Starting task %s, version %d: %s", taskId, taskStatusVersion, description)

					// Update task status to STATUS_EXECUTING
					result, err := executeTask(ctx, LatticeClient, taskId, int(taskStatusVersion), entityId)
					if err != nil {
						log.Printf("Error starting task: %v", err)
						continue
					}

					log.Printf("Started task with status version: %d", *result.StatusVersion)
				}
			} else if completeRequest := request.GetCompleteRequest(); completeRequest != nil {
				taskToComplete := completeRequest.GetTaskID()
				if taskToComplete != nil {
					log.Printf("Completing task: %s", *taskToComplete)
					err := completeTask(ctx, LatticeClient, *taskToComplete, entityId)
					if err != nil {
						log.Printf("Error completing task: %v", err)
					}
				}
			} else if cancelRequest := request.GetCancelRequest(); cancelRequest != nil {
				taskToCancel := cancelRequest.GetTaskID()
				if taskToCancel != nil {
					log.Printf("Cancelling task: %s", *taskToCancel)
					err := cancelTask(ctx, LatticeClient, *taskToCancel, entityId)
					if err != nil {
						log.Printf("Error cancelling task: %v", err)
					}
				}
			}
		}

		// Sleep briefly to prevent tight looping
		time.Sleep(100 * time.Millisecond)
	}
}

// executeTask updates the task status to STATUS_EXECUTING
func executeTask(ctx context.Context, client *client.Client, taskId string, taskStatusVersion int, agentEntityId string) (*Lattice.TaskVersion, error) {
	// Increment status version for the update
	taskStatusVersion++

	// Create system principal with the agent entity ID
	principal := Lattice.Principal{
		System: &Lattice.System{
			EntityID: &agentEntityId,
		},
	}

	taskStatus := Lattice.TaskStatus{
		Status: Lattice.TaskStatusStatusStatusExecuting.Ptr(),
	}

	// Create task status update request
	taskStatusUpdate := Lattice.TaskStatusUpdate{
		TaskID:        taskId,
		StatusVersion: &taskStatusVersion,
		NewStatus:     &taskStatus,
		Author:        &principal,
	}

	// Call the UpdateTaskStatus API
	task, err := client.Tasks.UpdateTaskStatus(ctx, &taskStatusUpdate)
	if err != nil {
		return nil, fmt.Errorf("error updating task status: %w", err)
	}

	return task.Version, nil
}

// cancelTask handles cancellation requests from Lattice
func cancelTask(ctx context.Context, client *client.Client, taskId string, entityId string) error {
	// Get current task to retrieve status_version
	getTaskRequest := Lattice.GetTaskRequest{
		TaskID: taskId,
	}
	task, err := client.Tasks.GetTask(ctx, &getTaskRequest)
	if err != nil {
		return fmt.Errorf("error getting task: %w", err)
	}
	if task.Status == nil || task.Version == nil || task.Version.StatusVersion == nil {
		return fmt.Errorf("task status or version is missing")
	}
	currentTaskStatus := task.Status.Status
	taskStatusVersion := *task.Version.StatusVersion
	taskStatusVersion++

	// Create system principal with the agent entity ID
	principal := Lattice.Principal{
		System: &Lattice.System{
			EntityID: &entityId,
		},
	}

	if taskActive {
		// Reject cancellation: task is active and cannot be cancelled
		rejectedMessage := "Task is already active, and cannot be cancelled."
		taskStatus := Lattice.TaskStatus{
			// Because the cancellation is being rejected, we do not
			// change the task status.
			Status: currentTaskStatus,
			TaskError: &Lattice.TaskError{
				Code:    Lattice.TaskErrorCodeErrorCodeRejected.Ptr(),
				Message: &rejectedMessage,
			},
		}

		taskStatusUpdate := Lattice.TaskStatusUpdate{
			TaskID:        taskId,
			StatusVersion: &taskStatusVersion,
			NewStatus:     &taskStatus,
			Author:        &principal,
		}

		_, err := client.Tasks.UpdateTaskStatus(ctx, &taskStatusUpdate)
		if err != nil {
			return fmt.Errorf("error updating task status: %w", err)
		}

		log.Println("Task could not be cancelled.")
	} else {
		// Accept cancellation
		cancelledMessage := "Task cancelled by agent."
		taskStatus := Lattice.TaskStatus{
			Status: Lattice.TaskStatusStatusStatusDoneNotOk.Ptr(),
			TaskError: &Lattice.TaskError{
				Code:    Lattice.TaskErrorCodeErrorCodeCancelled.Ptr(),
				Message: &cancelledMessage,
			},
		}

		taskStatusUpdate := Lattice.TaskStatusUpdate{
			TaskID:        taskId,
			StatusVersion: &taskStatusVersion,
			NewStatus:     &taskStatus,
			Author:        &principal,
		}

		_, err := client.Tasks.UpdateTaskStatus(ctx, &taskStatusUpdate)
		if err != nil {
			return fmt.Errorf("error updating task status: %w", err)
		}

		log.Println("Task has been cancelled.")
	}

	return nil
}

// completeTask updates the task status to STATUS_DONE_OK
func completeTask(ctx context.Context, client *client.Client, taskId string, entityId string) error {
	// Get current task to retrieve status_version
	getTaskRequest := Lattice.GetTaskRequest{
		TaskID: taskId,
	}
	task, err := client.Tasks.GetTask(ctx, &getTaskRequest)
	if err != nil {
		return fmt.Errorf("error getting task: %w", err)
	}

	if task.Version == nil || task.Version.StatusVersion == nil {
		return fmt.Errorf("task version is missing")
	}

	taskStatusVersion := *task.Version.StatusVersion
	// Increment version and update to terminal state
	taskStatusVersion++

	// Create system principal with the agent entity ID
	principal := Lattice.Principal{
		System: &Lattice.System{
			EntityID: &entityId,
		},
	}

	taskStatus := Lattice.TaskStatus{
		Status: Lattice.TaskStatusStatusStatusDoneOk.Ptr(),
	}

	taskStatusUpdate := Lattice.TaskStatusUpdate{
		TaskID:        taskId,
		StatusVersion: &taskStatusVersion,
		NewStatus:     &taskStatus,
		Author:        &principal,
	}

	_, err = client.Tasks.UpdateTaskStatus(ctx, &taskStatusUpdate)
	if err != nil {
		return fmt.Errorf("error updating task status: %w", err)
	}

	return nil
}

```

```java title={"Java (REST)"} startLine={201} maxLines={18}
package org.example;

import java.util.Arrays;
import java.util.concurrent.CompletableFuture;

import com.anduril.AsyncLattice;
import com.anduril.resources.tasks.requests.AgentStreamRequest;
import com.anduril.resources.tasks.requests.TaskStatusUpdate;
import com.anduril.resources.tasks.types.StreamAsAgentResponse;
import com.anduril.types.AgentStreamEvent;
import com.anduril.types.EntityIdsSelector;
import com.anduril.types.Principal;
import com.anduril.types.TaskError;
import com.anduril.types.TaskErrorCode;
import com.anduril.types.TaskStatus;
import com.anduril.types.TaskStatusStatus;
import com.anduril.types.TaskVersion;
public class ProcessTask {
    // Entity ID to listen for tasks - replace with your actual entity ID.
    private static final String ENTITY_ID = "<AGENT_ID>";

    // Sets whether the task is currently being processed. If so, it cannot be cancelled.
    private static boolean taskActive;

    public static void main(String[] args) {
        String endpoint = System.getenv("LATTICE_ENDPOINT");
        String environmentToken = System.getenv("ENVIRONMENT_TOKEN");
        String sandboxesToken = System.getenv("SANDBOXES_TOKEN");
        String taskActiveStr = System.getenv("TASK_ACTIVE");

        if (endpoint == null || environmentToken == null || sandboxesToken == null) {
            System.err.println("Missing required environment variables.");
            System.exit(1);
        }
        if (!endpoint.startsWith("https://")) endpoint = "https://" + endpoint;

        taskActive = "true".equalsIgnoreCase(taskActiveStr);

        try {
            AsyncLattice client = AsyncLattice.builder()
                .url(endpoint)
                .token(environmentToken)
                .addHeader("Anduril-Sandbox-Authorization", "Bearer " + sandboxesToken)
                .build();

            System.out.println("Streaming tasks for entity: " + ENTITY_ID + "...");

            // Create agent selector with the specified entity ID.
            EntityIdsSelector agentSelector = EntityIdsSelector.builder()
                .entityIds(Arrays.asList(ENTITY_ID))
                .build();

            // Create agent stream request.
            AgentStreamRequest agentRequest = AgentStreamRequest.builder()
                .agentSelector(agentSelector)
                .build();

            // Stream tasks from Lattice as an agent
            client.tasks().streamAsAgent(agentRequest)
                .thenAccept(requests -> {
                    for (StreamAsAgentResponse request : requests) {
                        if (request.isHeartbeat()) {
                            System.out.println("Heartbeat: " + request.getHeartbeat().get().getTimestamp().get());
                        } else {
                            AgentStreamEvent event = request.getAgentRequest().get();
                            if (event.getExecuteRequest().isPresent()) {
                                String taskId = event.getExecuteRequest().get()
                                    .getTask().get()
                                    .getVersion().get()
                                    .getTaskId().get();

                                int taskStatusVersion = event.getExecuteRequest().get()
                                    .getTask().get()
                                    .getVersion().get()
                                    .getStatusVersion().get();

                                String description = event.getExecuteRequest().get()
                                    .getTask().get()
                                    .getDescription().orElse("No description");

                                System.out.println("Starting task " + taskId + ", version " + taskStatusVersion + ": " + description);

                                try {
                                    TaskVersion result = startTask(client, taskId, taskStatusVersion, ENTITY_ID).get();
                                    System.out.println("Started task with status version: " + result.getStatusVersion().get());
                                } catch (Exception e) {
                                    System.err.println("Error starting task: " + e.getMessage());
                                }
                            } else if (event.getCompleteRequest().isPresent()) {
                                String taskId = event.getCompleteRequest().get().getTaskId().get();
                                System.out.println("Completing task: " + taskId);
                                try {
                                    completeTask(client, taskId, ENTITY_ID).get();
                                } catch (Exception e) {
                                    System.err.println("Error completing task: " + e.getMessage());
                                }
                            } else if (event.getCancelRequest().isPresent()) {
                                String taskId = event.getCancelRequest().get().getTaskId().get();
                                System.out.println("Cancelling task: " + taskId);
                                try {
                                    cancelTask(client, taskId, ENTITY_ID).get();
                                } catch (Exception e) {
                                    System.err.println("Error cancelling task: " + e.getMessage());
                                }
                            }
                        }
                    }
                })
                .exceptionally(ex -> {
                    System.err.println("Exception while streaming tasks: " + ex.getMessage());
                    return null;
                })
                .join();
        } catch (Exception e) {
            System.err.println("Error in streaming loop: " + e.getMessage());
        }
    }

    /**
     * Update task status to STATUS_EXECUTING.
     *
     * @param client Lattice client
     * @param taskId Task ID to update
     * @param taskStatusVersion Current task status version
     * @param agentEntityId Agent entity ID performing the update
     * @return CompletableFuture containing the updated TaskVersion
     */
    private static CompletableFuture<TaskVersion> startTask(AsyncLattice client, String taskId, Integer taskStatusVersion, String agentEntityId) {
        try {
            // Increment status version for the update
            taskStatusVersion += 1;

            // Create system principal with the agent entity ID.
            Principal principal = Principal.builder()
                .system(com.anduril.types.System.builder()
                    .entityId(agentEntityId)
                    .build())
                .build();

            // Create task status for update.
            TaskStatus taskStatus = TaskStatus.builder()
                .status(TaskStatusStatus.STATUS_EXECUTING)
                .build();

            // Create task status update request.
            TaskStatusUpdate taskStatusUpdate = TaskStatusUpdate.builder()
                .statusVersion(taskStatusVersion)
                .newStatus(taskStatus)
                .author(principal)
                .build();

            return client.tasks().updateTaskStatus(taskId, taskStatusUpdate)
                .thenApply(response -> response.getVersion().get());
        } catch (Exception e) {
            System.err.println("Encountered the following error while starting task: " + e.getMessage());
            return CompletableFuture.failedFuture(e);
        }
    }

    /**
     * Handle cancellation requests from Lattice.
     *
     * @param client Lattice client
     * @param taskId Task ID to cancel
     * @param entityId Agent entity ID performing the cancellation
     * @return CompletableFuture indicating completion
     */
    private static CompletableFuture<Void> cancelTask(AsyncLattice client, String taskId, String entityId) {
        // Get current task to retrieve status_version
        return client.tasks().getTask(taskId)
            .thenCompose(task -> {
                if (task.getStatus().isEmpty() || task.getVersion().isEmpty() || task.getVersion().get().getStatusVersion().isEmpty()) {
                    System.err.println("Task status or version is missing");
                    return CompletableFuture.failedFuture(new RuntimeException("Task status or version is missing"));
                }

                TaskStatusStatus currentTaskStatus = task.getStatus().get().getStatus().get();
                int taskStatusVersion = task.getVersion().get().getStatusVersion().get();
                taskStatusVersion += 1;

                // Create system principal with the agent entity ID.
                Principal principal = Principal.builder()
                    .system(com.anduril.types.System.builder()
                        .entityId(entityId)
                        .build())
                    .build();

                TaskStatus taskStatus;
                if (taskActive) {
                    // Reject cancellation: task is active and cannot be cancelled
                    taskStatus = TaskStatus.builder()
                        // Because the cancellation is being rejected, we do not
                        // change the task status.
                        .status(currentTaskStatus)
                        .taskError(TaskError.builder()
                            .code(TaskErrorCode.ERROR_CODE_REJECTED)
                            .message("Task is already active, and cannot be cancelled.")
                            .build())
                        .build();
                } else {
                    // Accept cancellation
                    taskStatus = TaskStatus.builder()
                        .status(TaskStatusStatus.STATUS_DONE_NOT_OK)
                        .taskError(TaskError.builder()
                            .code(TaskErrorCode.ERROR_CODE_CANCELLED)
                            .message("Task cancelled by agent.")
                            .build())
                        .build();
                }

                TaskStatusUpdate taskStatusUpdate = TaskStatusUpdate.builder()
                    .statusVersion(taskStatusVersion)
                    .newStatus(taskStatus)
                    .author(principal)
                    .build();

                return client.tasks().updateTaskStatus(taskId, taskStatusUpdate)
                    .thenAccept(response -> {
                        if (taskActive) {
                            System.out.println("Task could not be cancelled.");
                        } else {
                            System.out.println("Task has been cancelled.");
                        }
                    });
            })
            .exceptionally(ex -> {
                System.err.println("Encountered the following error while cancelling task: " + ex.getMessage());
                return null;
            });
    }

    /**
     * Update task status to STATUS_DONE_OK.
     *
     * @param client Lattice client
     * @param taskId Task ID to complete
     * @param entityId Agent entity ID performing the completion
     * @return CompletableFuture indicating completion
     */
    private static CompletableFuture<Void> completeTask(AsyncLattice client, String taskId, String entityId) {
        // Get current task to retrieve status_version
        return client.tasks().getTask(taskId)
            .thenCompose(task -> {
                if (task.getVersion().isEmpty() || task.getVersion().get().getStatusVersion().isEmpty()) {
                    System.err.println("Task version is missing");
                    return CompletableFuture.failedFuture(new RuntimeException("Task version is missing"));
                }

                int taskStatusVersion = task.getVersion().get().getStatusVersion().get();
                // Increment version and update to terminal state
                taskStatusVersion += 1;

                // Create system principal with the agent entity ID.
                Principal principal = Principal.builder()
                    .system(com.anduril.types.System.builder()
                        .entityId(entityId)
                        .build())
                    .build();

                TaskStatus taskStatus = TaskStatus.builder()
                    .status(TaskStatusStatus.STATUS_DONE_OK)
                    .build();

                TaskStatusUpdate taskStatusUpdate = TaskStatusUpdate.builder()
                    .statusVersion(taskStatusVersion)
                    .newStatus(taskStatus)
                    .author(principal)
                    .build();

                return client.tasks().updateTaskStatus(taskId, taskStatusUpdate)
                    .thenApply(response -> (Void) null);
            })
            .exceptionally(ex -> {
                System.err.println("Encountered the following error while completing task: " + ex.getMessage());
                return null;
            });
    }
}

```

```py title={"Python (REST)"} startLine={56} maxLines={18}
from anduril import AsyncLattice, EntityIdsSelector, TaskStatus, Principal, System, TaskError
import asyncio
import os
import sys

# Sets whether the task is currently being processed, if so, it cannot be cancelled.
task_active = os.getenv('TASK_ACTIVE', 'false').lower() == 'true'

lattice_endpoint = os.getenv('LATTICE_ENDPOINT')
environment_token = os.getenv('ENVIRONMENT_TOKEN')

# Remove sandboxes_token from the following statements if you are not developing on Sandboxes.
sandboxes_token = os.getenv('SANDBOXES_TOKEN')
if not environment_token or not lattice_endpoint or not sandboxes_token:
    print("Missing required environment variables.")
    sys.exit(1)

client = AsyncLattice(
    base_url=f"https://{lattice_endpoint}",
    token=lambda: str(environment_token),
    # Remove the following header if you are not developing on Sandboxes.
    headers={ "anduril-sandbox-authorization": f"Bearer {sandboxes_token}" }
)

async def start_task(task_id, task_status_version, entity_id):
    try:
        task_status_version += 1
        response = await client.tasks.update_task_status(
            task_id=task_id,
            status_version=task_status_version,
            new_status=TaskStatus(
                status="STATUS_EXECUTING"
            ),
            author=Principal(
                system=System(
                    entity_id=entity_id
                )
            )
        )
        return response
    except Exception as error:
        print(f"Encountered the following error while starting task: {error}")

async def cancel_task(task_id, entity_id):
    try:
        # Get current task to retrieve status_version
        task = await client.tasks.get_task(task_id=task_id)
        if not task.status or not task.version or task.version.status_version is None:
            print("Task status or version is missing")
            return

        current_task_status = task.status.status
        task_status_version = task.version.status_version
        task_status_version += 1
        
        if task_active:
            await client.tasks.update_task_status(
                task_id=task_id,
                status_version=task_status_version,
                new_status=TaskStatus(
                    # Because the cancellation is being rejected, we do not
                    # change the task status.
                    status=current_task_status,
                    task_error=TaskError(
                        code="ERROR_CODE_REJECTED",
                        message="Task is already active, and cannot be cancelled."
                    )
                ),
                author=Principal(
                    system=System(
                        entity_id=entity_id
                    )
                )
            )
            print("Task could not be cancelled.")
        else:
            await client.tasks.update_task_status(
                task_id=task_id,
                status_version=task_status_version,
                new_status=TaskStatus(
                    status="STATUS_DONE_NOT_OK",
                    task_error=TaskError(
                        code="ERROR_CODE_CANCELLED",
                        message="Task cancelled by agent."
                    )
                ),
                author=Principal(
                    system=System(
                        entity_id=entity_id
                    )
                )
            )
            print("Task has been cancelled.")
    except Exception as error:
        print(f"Encountered the following error while cancelling task: {error}")

async def complete_task(task_id, entity_id):
    try:
        # Get current task to retrieve status_version
        task = await client.tasks.get_task(task_id=task_id)
        if not task.version or task.version.status_version is None:
            print("Task version is missing")
            return

        task_status_version = task.version.status_version

        # Increment version and update to terminal state
        task_status_version += 1
        response = await client.tasks.update_task_status(
            task_id=task_id,
            status_version=task_status_version,
            new_status=TaskStatus(
                status="STATUS_DONE_OK"
            ),
            author=Principal(
                system=System(
                    entity_id=entity_id
                )
            )
        )
        return response
    except Exception as error:
        print(f"Encountered the following error while completing task: {error}")

async def main(entity_id):
    try:
        print(f"Streaming tasks for entity: {entity_id}...")
        requests = client.tasks.stream_as_agent(
            agent_selector=EntityIdsSelector(
                entity_ids=[entity_id]
            )
        )

        async for request in requests:
            if request.event == "heartbeat":
                print(f"Heartbeat: {request.timestamp}")
                continue

            if request.execute_request and request.execute_request.task and request.execute_request.task.version:
                version = request.execute_request.task.version
                task_id = version.task_id
                task_status_version = version.status_version
                task_description = request.execute_request.task.description

                print(f"Starting task {task_id}, version {task_status_version}: {task_description}")
                await start_task(task_id, task_status_version, entity_id)

            elif request.complete_request:
                task_id = request.complete_request.task_id
                print(f"Completing task: {task_id}")
                await complete_task(task_id, entity_id)

            elif request.cancel_request:
                task_id = request.cancel_request.task_id
                print(f"Cancelling task: {task_id}")
                await cancel_task(task_id, entity_id)

    except Exception as error:
        print(f"Exception: {error}")

if __name__ == "__main__":
    raise SystemExit(asyncio.run(main(entity_id="<AGENT_ID>")))

```

```ts title={"TypeScript (REST)"} startLine={84} maxLines={18}
import { LatticeClient } from "@anduril-industries/lattice-sdk";

// Entity ID to listen for tasks - replace with your actual entity ID.
const ENTITY_ID = "<AGENT_ID>";



const latticeEndpoint = process.env.LATTICE_ENDPOINT;
const environmentToken = process.env.ENVIRONMENT_TOKEN;
const sandboxesToken = process.env.SANDBOXES_TOKEN;
const taskActiveStr = process.env.TASK_ACTIVE;

if (!latticeEndpoint || !environmentToken || !sandboxesToken) {
    console.error("Missing required environment variables.");
    process.exit(1);
}

// Sets whether the task is currently being processed. If so, it cannot be cancelled.
const taskActive = taskActiveStr?.toLowerCase() === "true";

const client = new LatticeClient({
    baseUrl: latticeEndpoint.startsWith("https://") ? latticeEndpoint : `https://${latticeEndpoint}`,
    token: environmentToken,
    headers: { "Anduril-Sandbox-Authorization": `Bearer ${sandboxesToken}` }
});


// Update task status to STATUS_EXECUTING.
async function startTask(taskId: string, taskStatusVersion: number, agentEntityId: string) {
    try {
        // Increment status version for the update
        taskStatusVersion += 1;

        // Create system principal with the agent entity ID
        const principal = {
            system: {
                entityId: agentEntityId
            }
        };

        // Create task status for update
        const taskStatus = {
            status: "STATUS_EXECUTING" as const
        };

        // Update task status
        const response = await client.tasks.updateTaskStatus({
            taskId: taskId,
            statusVersion: taskStatusVersion,
            newStatus: taskStatus,
            author: principal
        });

        return response;
    } catch (error) {
        console.error(`Encountered the following error while starting task: ${error}`);
        throw error;
    }
}

// Handle cancellation requests from Lattice.
async function cancelTask(taskId: string, entityId: string) {
    try {
        // Get current task to retrieve status_version
        const task = await client.tasks.getTask({ taskId: taskId });

        if (!task.status || !task.version?.statusVersion) {
            console.error("Task status or version is missing");
            return;
        }

        const currentTaskStatus = task.status.status;
        let taskStatusVersion = task.version.statusVersion;
        taskStatusVersion += 1;

        // Create system principal with the agent entity ID
        const principal = {
            system: {
                entityId: entityId
            }
        };

        let taskStatus;
        if (taskActive) {
            // Reject cancellation: task is active and cannot be cancelled
            taskStatus = {
                // Because the cancellation is being rejected, we do not
                // change the task status.
                status: currentTaskStatus,
                taskError: {
                    code: "ERROR_CODE_REJECTED" as const,
                    message: "Task is already active, and cannot be cancelled."
                }
            };
        } else {
            // Accept cancellation
            taskStatus = {
                status: "STATUS_DONE_NOT_OK" as const,
                taskError: {
                    code: "ERROR_CODE_CANCELLED" as const,
                    message: "Task cancelled by agent."
                }
            };
        }

        await client.tasks.updateTaskStatus({
            taskId: taskId,
            statusVersion: taskStatusVersion,
            newStatus: taskStatus,
            author: principal
        });

        if (taskActive) {
            console.log("Task could not be cancelled.");
        } else {
            console.log("Task has been cancelled.");
        }
    } catch (error) {
        console.error(`Encountered the following error while cancelling task: ${error}`);
    }
}


// Update task status to STATUS_DONE_OK.
async function completeTask(taskId: string, entityId: string) {
    try {
        // Get current task to retrieve status_version
        const task = await client.tasks.getTask({ taskId: taskId });

        if (!task.version?.statusVersion) {
            console.error("Task version is missing");
            return;
        }

        let taskStatusVersion = task.version.statusVersion;
        // Increment version and update to terminal state
        taskStatusVersion += 1;

        // Create system principal with the agent entity ID
        const principal = {
            system: {
                entityId: entityId
            }
        };

        const taskStatus = {
            status: "STATUS_DONE_OK" as const
        };

        await client.tasks.updateTaskStatus({
            taskId: taskId,
            statusVersion: taskStatusVersion,
            newStatus: taskStatus,
            author: principal
        });

        console.log("Task has been completed.");
    } catch (error) {
        console.error(`Encountered the following error while completing task: ${error}`);
    }
}

async function App() {
    try {
        console.log(`Streaming tasks for entity: ${ENTITY_ID}...`);

        // Stream tasks from Lattice as an agent
        const requests = await client.tasks.streamAsAgent({
            agentSelector: {
                entityIds: [ENTITY_ID]
            }
        });

        for await (const request of requests) {
            if (request.event === "heartbeat") {
                console.log(`Heartbeat: ${request.timestamp}`);
                continue;
            }

            if (request.executeRequest) {
                const taskId = request.executeRequest.task?.version?.taskId;
                const taskStatusVersion = request.executeRequest.task?.version?.statusVersion;
                const description = request.executeRequest.task?.description || "No description";

                if (taskId && taskStatusVersion !== undefined) {
                    console.log(`Starting task ${taskId}, version ${taskStatusVersion}: ${description}`);

                    try {
                        const result = await startTask(taskId, taskStatusVersion, ENTITY_ID);
                        console.log(`Started task with status version: ${result.version?.statusVersion}`);
                    } catch (error) {
                        console.error(`Error starting task: ${error}`);
                    }
                }
            } else if (request.completeRequest) {
                const taskId = request.completeRequest.taskId;
                if (taskId) {
                    console.log(`Completing task: ${taskId}`);
                    try {
                        await completeTask(taskId, ENTITY_ID);
                    } catch (error) {
                        console.error(`Error completing task: ${error}`);
                    }
                }
            } else if (request.cancelRequest) {
                const taskId = request.cancelRequest.taskId;
                if (taskId) {
                    console.log(`Cancelling task: ${taskId}`);
                    try {
                        await cancelTask(taskId, ENTITY_ID);
                    } catch (error) {
                        console.error(`Error cancelling task: ${error}`);
                    }
                }
            }
        }
    } catch (error) {
        console.error(`Error in streaming loop: ${error}`);
    }
}

App();

```

```rs title={"Rust (REST)"} startLine={97} maxLines={18}
use anduril_lattice_sdk::prelude::*;

use std::env;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Sets whether the task is currently being processed; if so, it cannot be
    // cancelled.
    let task_active = env::var("TASK_ACTIVE")
        .map(|v| v.to_lowercase() == "true")
        .unwrap_or(false);

    let lattice_endpoint = env::var("LATTICE_ENDPOINT")
        .expect("LATTICE_ENDPOINT environment variable not set");
    let environment_token = env::var("ENVIRONMENT_TOKEN")
        .expect("ENVIRONMENT_TOKEN environment variable not set");
    // Remove sandboxes_token if you are not developing on Sandboxes.
    let sandboxes_token = env::var("SANDBOXES_TOKEN")
        .expect("SANDBOXES_TOKEN environment variable not set");

    if environment_token.is_empty() || lattice_endpoint.is_empty() || sandboxes_token.is_empty() {
        eprintln!("Missing required environment variables:");
        eprintln!("  LATTICE_ENDPOINT");
        eprintln!("  ENVIRONMENT_TOKEN");
        eprintln!("  SANDBOXES_TOKEN");
        std::process::exit(1);
    }

    let config = ClientConfig {
        base_url: format!("https://{}", lattice_endpoint),
        token: Some(environment_token),
        // Remove custom_headers if you are not developing on Sandboxes.
        custom_headers: HashMap::from([(
            "anduril-sandbox-authorization".to_string(),
            format!("Bearer {}", sandboxes_token),
        )]),
        ..Default::default()
    };
    let client = Lattice::new(config)?;

    // Replace with the entity ID of the agent to process tasks for.
    let entity_id = "<AGENT_ID>";
    run(&client, entity_id, task_active).await;

    Ok(())
}

/// Author principal for a system entity.
fn system_author(entity_id: &str) -> Principal {
    Principal {
        system: Some(System {
            entity_id: Some(entity_id.to_string()),
            ..Default::default()
        }),
        ..Default::default()
    }
}

/// Move a task into the executing state by advancing its status version.
async fn start_task(client: &Lattice, task_id: &str, task_status_version: i64, entity_id: &str) {
    let request = TaskStatusUpdate {
        status_version: Some(task_status_version + 1),
        new_status: Some(TaskStatus {
            status: Some(TaskStatusStatus::StatusExecuting),
            ..Default::default()
        }),
        author: Some(system_author(entity_id)),
    };

    if let Err(error) = client.tasks.update_task_status(task_id, &request, None).await {
        eprintln!("Encountered the following error while starting task: {}", error);
    }
}

/// Cancel a task, or reject the cancellation if the task is actively executing.
async fn cancel_task(client: &Lattice, task_id: &str, entity_id: &str, task_active: bool) {
    // Get the current task to retrieve its status_version.
    let task = match client.tasks.get_task(task_id, None).await {
        Ok(task) => task,
        Err(error) => {
            eprintln!("Encountered the following error while cancelling task: {}", error);
            return;
        }
    };

    let (Some(status), Some(version)) = (task.status.as_ref(), task.version.as_ref()) else {
        println!("Task status or version is missing");
        return;
    };
    let Some(current_status_version) = version.status_version else {
        println!("Task status or version is missing");
        return;
    };
    let current_task_status = status.status.clone();
    let next_status_version = current_status_version + 1;

    let request = if task_active {
        // Because the cancellation is being rejected, we do not change the task
        // status.
        TaskStatusUpdate {
            status_version: Some(next_status_version),
            new_status: Some(TaskStatus {
                status: current_task_status,
                task_error: Some(TaskError {
                    code: Some(TaskErrorCode::ErrorCodeRejected),
                    message: Some("Task is already active, and cannot be cancelled.".to_string()),
                    ..Default::default()
                }),
                ..Default::default()
            }),
            author: Some(system_author(entity_id)),
        }
    } else {
        TaskStatusUpdate {
            status_version: Some(next_status_version),
            new_status: Some(TaskStatus {
                status: Some(TaskStatusStatus::StatusDoneNotOk),
                task_error: Some(TaskError {
                    code: Some(TaskErrorCode::ErrorCodeCancelled),
                    message: Some("Task cancelled by agent.".to_string()),
                    ..Default::default()
                }),
                ..Default::default()
            }),
            author: Some(system_author(entity_id)),
        }
    };

    match client.tasks.update_task_status(task_id, &request, None).await {
        Ok(_) if task_active => println!("Task could not be cancelled."),
        Ok(_) => println!("Task has been cancelled."),
        Err(error) => eprintln!("Encountered the following error while cancelling task: {}", error),
    }
}

/// Complete a task by advancing it to the terminal STATUS_DONE_OK state.
async fn complete_task(client: &Lattice, task_id: &str, entity_id: &str) {
    // Get the current task to retrieve its status_version.
    let task = match client.tasks.get_task(task_id, None).await {
        Ok(task) => task,
        Err(error) => {
            eprintln!("Encountered the following error while completing task: {}", error);
            return;
        }
    };

    let Some(current_status_version) = task.version.and_then(|v| v.status_version) else {
        println!("Task version is missing");
        return;
    };

    let request = TaskStatusUpdate {
        // Increment version and update to a terminal state.
        status_version: Some(current_status_version + 1),
        new_status: Some(TaskStatus {
            status: Some(TaskStatusStatus::StatusDoneOk),
            ..Default::default()
        }),
        author: Some(system_author(entity_id)),
    };

    if let Err(error) = client.tasks.update_task_status(task_id, &request, None).await {
        eprintln!("Encountered the following error while completing task: {}", error);
    }
}

/// Stream tasks for the agent and route each request to the matching handler.
async fn run(client: &Lattice, entity_id: &str, task_active: bool) {
    println!("Streaming tasks for entity: {}...", entity_id);

    let request = AgentStreamRequest {
        agent_selector: Some(EntityIdsSelector {
            entity_ids: Some(vec![entity_id.to_string()]),
            ..Default::default()
        }),
        ..Default::default()
    };

    let stream = match client.tasks.stream_as_agent(&request, None).await {
        Ok(stream) => stream,
        Err(error) => {
            eprintln!("Exception: {}", error);
            return;
        }
    };
    let mut stream = std::pin::pin!(stream);

    while let Some(result) = stream.next().await {
        match result {
            Ok(StreamAsAgentResponse::Heartbeat { data, .. }) => {
                println!("Heartbeat: {:?}", data.heartbeat_object_fields.timestamp);
            }
            Ok(StreamAsAgentResponse::AgentRequest { data, .. }) => {
                let fields = &data.agent_task_request_fields;
                if let Some(execute) = &fields.execute_request {
                    if let Some(task) = &execute.task {
                        if let Some(version) = &task.version {
                            let task_id = version.task_id.clone().unwrap_or_default();
                            let task_status_version = version.status_version.unwrap_or(0);
                            println!(
                                "Starting task {}, version {}: {:?}",
                                task_id, task_status_version, task.description
                            );
                            start_task(client, &task_id, task_status_version, entity_id).await;
                        }
                    }
                } else if let Some(complete) = &fields.complete_request {
                    if let Some(task_id) = &complete.task_id {
                        println!("Completing task: {}", task_id);
                        complete_task(client, task_id, entity_id).await;
                    }
                } else if let Some(cancel) = &fields.cancel_request {
                    if let Some(task_id) = &cancel.task_id {
                        println!("Cancelling task: {}", task_id);
                        cancel_task(client, task_id, entity_id, task_active).await;
                    }
                }
            }
            Ok(other) => println!("Event: {:?}", other),
            Err(error) => eprintln!("Exception: {}", error),
        }
    }
}

```

```go title={"Go (gRPC)"} startLine={204} maxLines={18}
// This Go example is compatible with artifacts generated using
// the following grpc/go plugin: https://buf.build/anduril/lattice-sdk/sdks/main:grpc/go
package main

import (
	"context"
	"fmt"
	"io"
	"log"
	"os"
	"strings"
	"time"

	"schema-registry.developer.anduril.com/gen/go/anduril/lattice-sdk/grpc/go/anduril/taskmanager/v1/taskmanagerv1grpc"
	taskmanagerv1 "schema-registry.developer.anduril.com/gen/go/anduril/lattice-sdk/protocolbuffers/go/anduril/taskmanager/v1"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"
)

// Sets whether the task is currently being processed; if so, it cannot be cancelled.
var taskActive bool

func main() {
	environmentToken := os.Getenv("ENVIRONMENT_TOKEN")
	latticeEndpoint := os.Getenv("LATTICE_ENDPOINT")
	sandboxesToken := os.Getenv("SANDBOXES_TOKEN")
	taskActiveStr := os.Getenv("TASK_ACTIVE")

	if latticeEndpoint == "" || environmentToken == "" || sandboxesToken == "" {
		fmt.Println("Missing required environment variables")
		os.Exit(1)
	}

	// Parse TASK_ACTIVE environment variable
	taskActive = strings.ToLower(taskActiveStr) == "true"

	// Setup authentication
	auth := &BearerTokenAuth{
		Token:          environmentToken,
		SandboxesToken: sandboxesToken,
	}

	opts := []grpc.DialOption{
		grpc.WithTransportCredentials(credentials.NewClientTLSFromCert(nil, "")),
		grpc.WithPerRPCCredentials(auth),
	}

	conn, err := grpc.NewClient(latticeEndpoint, opts...)
	if err != nil {
		log.Fatalf("Did not connect: %v", err)
	}
	defer conn.Close()

	client := taskmanagerv1grpc.NewTaskManagerAPIClient(conn)

	// Set the entity ID to listen for tasks
	entityId := "<AGENT_ID>"
	fmt.Printf("Listening for tasks for entity %s...\n", entityId)

	for {
		try := func() bool {
			ctx := context.Background()

			// Create the ListenAsAgent request
			listenRequest := &taskmanagerv1.ListenAsAgentRequest{
				AgentSelector: &taskmanagerv1.ListenAsAgentRequest_EntityIds{
					EntityIds: &taskmanagerv1.EntityIds{
						EntityIds: []string{entityId},
					},
				},
			}

			// Call the ListenAsAgent method to get a stream of task requests
			stream, err := client.ListenAsAgent(ctx, listenRequest)
			if err != nil {
				fmt.Printf("Error starting task listener: %v\n", err)
				return false
			}

			for {
				response, err := stream.Recv()
				if err == io.EOF {
					fmt.Println("Stream ended")
					return false
				}
				if err != nil {
					fmt.Printf("Error receiving from stream: %v\n", err)
					return false
				}

				// Process the response based on the type of task request
				switch x := response.Request.(type) {
				case *taskmanagerv1.ListenAsAgentResponse_ExecuteRequest:
					if x.ExecuteRequest != nil && x.ExecuteRequest.Task != nil &&
						x.ExecuteRequest.Task.Version != nil && x.ExecuteRequest.Task.Version.TaskId != "" {
						// Extract task-specific information
						taskId := x.ExecuteRequest.Task.Version.TaskId
						taskStatusVersion := x.ExecuteRequest.Task.Version.StatusVersion

						fmt.Printf("Starting task %s, version %d\n", taskId, taskStatusVersion)

						// Update task status to STATUS_EXECUTING
						result, err := executeTask(ctx, client, taskId, taskStatusVersion, entityId)
						if err != nil {
							fmt.Printf("Error starting task: %v\n", err)
							return false
						}

						fmt.Printf("Started task with status version: %d\n", result.StatusVersion)
					}

				case *taskmanagerv1.ListenAsAgentResponse_CompleteRequest:
					if x.CompleteRequest != nil && x.CompleteRequest.TaskId != "" {
						fmt.Printf("Completing task: %s\n", x.CompleteRequest.TaskId)
						err := completeTask(ctx, client, x.CompleteRequest.TaskId, entityId)
						if err != nil {
							fmt.Printf("Error completing task: %v\n", err)
						}
					}

				case *taskmanagerv1.ListenAsAgentResponse_CancelRequest:
					if x.CancelRequest != nil && x.CancelRequest.TaskId != "" {
						fmt.Printf("Cancelling task: %s\n", x.CancelRequest.TaskId)
						err := cancelTask(ctx, client, x.CancelRequest.TaskId, entityId)
						if err != nil {
							fmt.Printf("Error cancelling task: %v\n", err)
						}
					}
				}
			}
		}()

		if !try {
			// If there was an error, wait a bit before retrying
			time.Sleep(1 * time.Second)
		}
	}
}

// executeTask updates the task status to STATUS_EXECUTING
func executeTask(ctx context.Context, client taskmanagerv1grpc.TaskManagerAPIClient, taskId string, taskStatusVersion uint32, agentEntityId string) (*taskmanagerv1.TaskVersion, error) {
	// Increment status version for the update
	taskStatusVersion++

	// Create system principal with the agent entity ID
	principal := &taskmanagerv1.Principal{
		Agent: &taskmanagerv1.Principal_System{
			System: &taskmanagerv1.System{
				EntityId: agentEntityId,
			},
		},
	}

	// Create task status update
	taskStatus := &taskmanagerv1.TaskStatus{
		Status: taskmanagerv1.Status_STATUS_EXECUTING,
	}

	// Create status update request
	statusUpdate := &taskmanagerv1.StatusUpdate{
		Version: &taskmanagerv1.TaskVersion{
			TaskId:            taskId,
			DefinitionVersion: 0,
			StatusVersion:     taskStatusVersion,
		},
		Status: taskStatus,
		Author: principal,
	}

	// Create the update status request
	updateRequest := &taskmanagerv1.UpdateStatusRequest{
		StatusUpdate: statusUpdate,
	}

	// Call the UpdateStatus API
	response, err := client.UpdateStatus(ctx, updateRequest)
	if err != nil {
		return nil, fmt.Errorf("error updating task status: %w", err)
	}

	return response.Task.Version, nil
}

// cancelTask handles cancellation requests from Lattice
func cancelTask(ctx context.Context, client taskmanagerv1grpc.TaskManagerAPIClient, taskId string, entityId string) error {
	// Get current task to retrieve status_version
	getTaskRequest := &taskmanagerv1.GetTaskRequest{
		TaskId: taskId,
	}
	taskResponse, err := client.GetTask(ctx, getTaskRequest)
	if err != nil {
		return fmt.Errorf("error getting task: %w", err)
	}
	if taskResponse.Task == nil || taskResponse.Task.Status == nil || taskResponse.Task.Version == nil {
		return fmt.Errorf("task status or version is missing")
	}
	currentTaskStatus := taskResponse.Task.Status.Status
	taskStatusVersion := taskResponse.Task.Version.StatusVersion
	taskStatusVersion++

	// Create system principal with the agent entity ID
	principal := &taskmanagerv1.Principal{
		Agent: &taskmanagerv1.Principal_System{
			System: &taskmanagerv1.System{
				EntityId: entityId,
			},
		},
	}

	if taskActive {
		// Reject cancellation: task is active and cannot be cancelled
		taskStatus := &taskmanagerv1.TaskStatus{
			// Because the cancellation is being rejected, we do not
			// change the task status.
			Status: currentTaskStatus,
			TaskError: &taskmanagerv1.TaskError{
				Code:    taskmanagerv1.ErrorCode_ERROR_CODE_REJECTED,
				Message: "Task is already active, and cannot be cancelled.",
			},
		}

		statusUpdate := &taskmanagerv1.StatusUpdate{
			Version: &taskmanagerv1.TaskVersion{
				TaskId:            taskId,
				DefinitionVersion: 0,
				StatusVersion:     taskStatusVersion,
			},
			Status: taskStatus,
			Author: principal,
		}

		updateRequest := &taskmanagerv1.UpdateStatusRequest{
			StatusUpdate: statusUpdate,
		}

		_, err := client.UpdateStatus(ctx, updateRequest)
		if err != nil {
			return fmt.Errorf("error updating task status: %w", err)
		}

		log.Println("Task could not be cancelled.")
	} else {
		// Accept cancellation
		taskStatus := &taskmanagerv1.TaskStatus{
			Status: taskmanagerv1.Status_STATUS_DONE_NOT_OK,
			TaskError: &taskmanagerv1.TaskError{
				Code:    taskmanagerv1.ErrorCode_ERROR_CODE_CANCELLED,
				Message: "Task cancelled by agent.",
			},
		}

		statusUpdate := &taskmanagerv1.StatusUpdate{
			Version: &taskmanagerv1.TaskVersion{
				TaskId:            taskId,
				DefinitionVersion: 0,
				StatusVersion:     taskStatusVersion,
			},
			Status: taskStatus,
			Author: principal,
		}

		updateRequest := &taskmanagerv1.UpdateStatusRequest{
			StatusUpdate: statusUpdate,
		}

		_, err := client.UpdateStatus(ctx, updateRequest)
		if err != nil {
			return fmt.Errorf("error updating task status: %w", err)
		}

		log.Println("Task has been cancelled.")
	}

	return nil
}

// completeTask updates the task status to STATUS_DONE_OK
func completeTask(ctx context.Context, client taskmanagerv1grpc.TaskManagerAPIClient, taskId string, entityId string) error {
	// Get current task to retrieve status_version
	getTaskRequest := &taskmanagerv1.GetTaskRequest{
		TaskId: taskId,
	}

	taskResponse, err := client.GetTask(ctx, getTaskRequest)
	if err != nil {
		return fmt.Errorf("error getting task: %w", err)
	}

	if taskResponse.Task == nil || taskResponse.Task.Version == nil {
		return fmt.Errorf("task version is missing")
	}

	taskStatusVersion := taskResponse.Task.Version.StatusVersion
	// Increment version and update to terminal state
	taskStatusVersion++

	// Create system principal with the agent entity ID
	principal := &taskmanagerv1.Principal{
		Agent: &taskmanagerv1.Principal_System{
			System: &taskmanagerv1.System{
				EntityId: entityId,
			},
		},
	}

	taskStatus := &taskmanagerv1.TaskStatus{
		Status: taskmanagerv1.Status_STATUS_DONE_OK,
	}

	statusUpdate := &taskmanagerv1.StatusUpdate{
		Version: &taskmanagerv1.TaskVersion{
			TaskId:            taskId,
			DefinitionVersion: 0,
			StatusVersion:     taskStatusVersion,
		},
		Status: taskStatus,
		Author: principal,
	}

	updateRequest := &taskmanagerv1.UpdateStatusRequest{
		StatusUpdate: statusUpdate,
	}

	_, err = client.UpdateStatus(ctx, updateRequest)
	if err != nil {
		return fmt.Errorf("error updating task status: %w", err)
	}

	return nil
}

```

```py title={"Python (gRPC)"} startLine={57} maxLines={18}
# This Python example is compatible with artifacts generated using the following
# Buf plugins: https://schema-registry.developer.anduril.com/anduril/lattice-sdk/sdks/main:bufbuild/py
# and https://schema-registry.developer.anduril.com/anduril/lattice-sdk/sdks/main:connectrpc/py

import os
import sys
import time
from bearer_auth import create_client
from protobuf import Oneof

from anduril.taskmanager.v1.task_pub_pb import (
    ErrorCode,
    Principal,
    Status,
    System,
    TaskError,
    TaskStatus,
    TaskVersion,
    StatusUpdate,
)
from anduril.taskmanager.v1.task_manager_api_pub_pb import (
    EntityIds,
    GetTaskRequest,
    ListenAsAgentRequest,
    UpdateStatusRequest,
)
from anduril.taskmanager.v1.task_manager_api_pub_connect import TaskManagerAPIClientSync


def execute_task(client, task_id, task_status_version, agent_entity_id):
    """Update the task status to STATUS_EXECUTING."""
    task_status_version += 1

    principal = Principal(agent=Oneof("system", System(entity_id=agent_entity_id)))
    task_status = TaskStatus(status=Status.EXECUTING)

    status_update = StatusUpdate(
        version=TaskVersion(task_id=task_id, definition_version=0, status_version=task_status_version),
        status=task_status,
        author=principal,
    )

    response = client.update_status(UpdateStatusRequest(status_update=status_update))
    return response.task.version


def cancel_task(client, task_id, entity_id, task_active):
    """Handle a cancellation request from Lattice."""
    task_response = client.get_task(GetTaskRequest(task_id=task_id))
    if not task_response.task or not task_response.task.status or not task_response.task.version:
        raise RuntimeError("task status or version is missing")

    current_task_status = task_response.task.status.status
    task_status_version = task_response.task.version.status_version + 1

    principal = Principal(agent=Oneof("system", System(entity_id=entity_id)))

    if task_active:
        # Reject cancellation: task is active and cannot be cancelled
        task_status = TaskStatus(
            # Because the cancellation is being rejected, we do not
            # change the task status.
            status=current_task_status,
            task_error=TaskError(
                code=ErrorCode.REJECTED,
                message="Task is already active, and cannot be cancelled.",
            ),
        )
        status_update = StatusUpdate(
            version=TaskVersion(task_id=task_id, definition_version=0, status_version=task_status_version),
            status=task_status,
            author=principal,
        )
        client.update_status(UpdateStatusRequest(status_update=status_update))
        print("Task could not be cancelled.")
    else:
        # Accept cancellation
        task_status = TaskStatus(
            status=Status.DONE_NOT_OK,
            task_error=TaskError(
                code=ErrorCode.CANCELLED,
                message="Task cancelled by agent.",
            ),
        )
        status_update = StatusUpdate(
            version=TaskVersion(task_id=task_id, definition_version=0, status_version=task_status_version),
            status=task_status,
            author=principal,
        )
        client.update_status(UpdateStatusRequest(status_update=status_update))
        print("Task has been cancelled.")


def complete_task(client, task_id, entity_id):
    """Update the task status to STATUS_DONE_OK."""
    task_response = client.get_task(GetTaskRequest(task_id=task_id))
    if not task_response.task or not task_response.task.version:
        raise RuntimeError("task version is missing")

    task_status_version = task_response.task.version.status_version + 1

    principal = Principal(agent=Oneof("system", System(entity_id=entity_id)))
    task_status = TaskStatus(status=Status.DONE_OK)

    status_update = StatusUpdate(
        version=TaskVersion(task_id=task_id, definition_version=0, status_version=task_status_version),
        status=task_status,
        author=principal,
    )
    client.update_status(UpdateStatusRequest(status_update=status_update))


def main():
    environment_token = os.getenv("ENVIRONMENT_TOKEN")
    lattice_endpoint = os.getenv("LATTICE_ENDPOINT")
    sandboxes_token = os.getenv("SANDBOXES_TOKEN")

    # Sets whether the task is currently being processed; if so, it cannot be cancelled.
    task_active = os.getenv("TASK_ACTIVE", "").lower() == "true"

    if not environment_token or not lattice_endpoint or not sandboxes_token:
        print("Missing required environment variables", file=sys.stderr)
        sys.exit(1)

    # Create the API client over Connect gRPC with authentication
    client = create_client(TaskManagerAPIClientSync, lattice_endpoint, environment_token, sandboxes_token)

    entity_id = "<AGENT_ID>"
    print(f"Listening for tasks for entity {entity_id}...")

    while True:
        try:
            listen_request = ListenAsAgentRequest(
                agent_selector=Oneof("entity_ids", EntityIds(entity_ids=[entity_id]))
            )
            stream = client.listen_as_agent(listen_request)

            for response in stream:
                request = response.request
                if request is None:
                    continue
                if request.field == "execute_request":
                    task = request.value.task
                    if task and task.version and task.version.task_id:
                        task_id = task.version.task_id
                        task_status_version = task.version.status_version
                        print(f"Starting task {task_id}, version {task_status_version}")

                        try:
                            result = execute_task(client, task_id, task_status_version, entity_id)
                            print(f"Started task with status version: {result.status_version}")
                        except Exception as error:
                            print(f"Error starting task: {error}", file=sys.stderr)
                            break
                elif request.field == "complete_request":
                    if request.value.task_id:
                        print(f"Completing task: {request.value.task_id}")
                        try:
                            complete_task(client, request.value.task_id, entity_id)
                        except Exception as error:
                            print(f"Error completing task: {error}", file=sys.stderr)
                elif request.field == "cancel_request":
                    if request.value.task_id:
                        print(f"Cancelling task: {request.value.task_id}")
                        try:
                            cancel_task(client, request.value.task_id, entity_id, task_active)
                        except Exception as error:
                            print(f"Error cancelling task: {error}", file=sys.stderr)
        except Exception as error:
            print(f"Error in stream: {error}", file=sys.stderr)

        # If the stream ended or errored, wait a bit before retrying
        time.sleep(1)


if __name__ == "__main__":
    main()

```

```rs title={"Rust (gRPC)"} startLine={227} maxLines={18}
// This Rust example is compatible with artifacts generated using
// the following grpc/rust plugin: https://buf.build/anduril/lattice-sdk/sdks/main:community/neoeinstein-tonic
mod bearer_auth;

use anduril_lattice_sdk_community_neoeinstein_prost::anduril::taskmanager::v1::{
    listen_as_agent_request, listen_as_agent_response, principal, EntityIds, ErrorCode,
    GetTaskRequest, ListenAsAgentRequest, Principal, Status, StatusUpdate, System, TaskError,
    TaskStatus, TaskVersion, UpdateStatusRequest,
};
use anduril_lattice_sdk_community_neoeinstein_tonic::anduril::taskmanager::v1::tonic::task_manager_api_client::TaskManagerApiClient;
use bearer_auth::EnvironmentTokenAuth;
use tonic::metadata::MetadataValue;
use tonic::service::interceptor::InterceptedService;
use tonic::transport::{Channel, ClientTlsConfig};
use tonic::Request;

use std::env;
use std::time::Duration;

type Interceptor = Box<dyn FnMut(Request<()>) -> Result<Request<()>, tonic::Status> + Send + Sync>;
type TaskClient = TaskManagerApiClient<InterceptedService<Channel, Interceptor>>;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let environment_token = env::var("ENVIRONMENT_TOKEN")
        .expect("ENVIRONMENT_TOKEN environment variable not set");
    let lattice_endpoint = env::var("LATTICE_ENDPOINT")
        .expect("LATTICE_ENDPOINT environment variable not set");
    let sandboxes_token = env::var("SANDBOXES_TOKEN")
        .expect("SANDBOXES_TOKEN environment variable not set");

    // When true, the agent cannot accept cancellations for an active task.
    let task_active = env::var("TASK_ACTIVE")
        .map(|v| v.eq_ignore_ascii_case("true"))
        .unwrap_or(false);

    let auth = EnvironmentTokenAuth::new(
        environment_token,
        sandboxes_token,
    );

    let tls_config = ClientTlsConfig::new().with_native_roots();
    let channel = Channel::from_shared(format!("https://{}", lattice_endpoint))?
        .tls_config(tls_config)?
        .connect()
        .await?;

    let access_token = auth.get_token().await?;
    let sandboxes_token = auth.sandboxes_token().to_string();
    let auth_header: MetadataValue<_> = format!("Bearer {}", access_token).parse()?;
    let sandbox_header: MetadataValue<_> = format!("Bearer {}", sandboxes_token).parse()?;

    let interceptor: Interceptor = Box::new(move |mut req: Request<()>| {
        req.metadata_mut()
            .insert("authorization", auth_header.clone());
        req.metadata_mut()
            .insert("anduril-sandbox-authorization", sandbox_header.clone());
        Ok(req)
    });
    let mut client: TaskClient = TaskManagerApiClient::with_interceptor(channel, interceptor);

    // Set the entity ID to listen for tasks
    let entity_id = "<AGENT_ID>".to_string();
    println!("Listening for tasks for entity {}...", entity_id);

    loop {
        let request = Request::new(ListenAsAgentRequest {
            agent_selector: Some(listen_as_agent_request::AgentSelector::EntityIds(EntityIds {
                entity_ids: vec![entity_id.clone()],
            })),
            ..Default::default()
        });

        let mut stream = match client.listen_as_agent(request).await {
            Ok(resp) => resp.into_inner(),
            Err(e) => {
                eprintln!("Error starting task listener: {}", e);
                tokio::time::sleep(Duration::from_secs(1)).await;
                continue;
            }
        };

        loop {
            let response = match stream.message().await {
                Ok(Some(r)) => r,
                Ok(None) => {
                    println!("Stream ended");
                    break;
                }
                Err(e) => {
                    eprintln!("Error receiving from stream: {}", e);
                    break;
                }
            };

            match response.request {
                Some(listen_as_agent_response::Request::ExecuteRequest(execute)) => {
                    if let Some(task) = execute.task {
                        if let Some(version) = task.version {
                            let task_id = version.task_id.clone();
                            let status_version = version.status_version;
                            println!("Starting task {}, version {}", task_id, status_version);

                            match execute_task(&mut client, &task_id, status_version, &entity_id)
                                .await
                            {
                                Ok(v) => println!(
                                    "Started task with status version: {}",
                                    v.status_version
                                ),
                                Err(e) => eprintln!("Error starting task: {}", e),
                            }
                        }
                    }
                }
                Some(listen_as_agent_response::Request::CompleteRequest(complete)) => {
                    if !complete.task_id.is_empty() {
                        println!("Completing task: {}", complete.task_id);
                        if let Err(e) =
                            complete_task(&mut client, &complete.task_id, &entity_id).await
                        {
                            eprintln!("Error completing task: {}", e);
                        }
                    }
                }
                Some(listen_as_agent_response::Request::CancelRequest(cancel)) => {
                    if !cancel.task_id.is_empty() {
                        println!("Cancelling task: {}", cancel.task_id);
                        if let Err(e) =
                            cancel_task(&mut client, &cancel.task_id, &entity_id, task_active)
                                .await
                        {
                            eprintln!("Error cancelling task: {}", e);
                        }
                    }
                }
                None => {}
            }
        }

        tokio::time::sleep(Duration::from_secs(1)).await;
    }
}

fn system_principal(entity_id: &str) -> Principal {
    Principal {
        agent: Some(principal::Agent::System(System {
            entity_id: entity_id.to_string(),
            ..Default::default()
        })),
        ..Default::default()
    }
}

// execute_task updates the task status to STATUS_EXECUTING.
async fn execute_task(
    client: &mut TaskClient,
    task_id: &str,
    task_status_version: u32,
    agent_entity_id: &str,
) -> Result<TaskVersion, tonic::Status> {
    let status_update = StatusUpdate {
        version: Some(TaskVersion {
            task_id: task_id.to_string(),
            status_version: task_status_version + 1,
            definition_version: 0,
            ..Default::default()
        }),
        status: Some(TaskStatus {
            status: Status::Executing as i32,
            ..Default::default()
        }),
        author: Some(system_principal(agent_entity_id)),
        ..Default::default()
    };

    let response = client
        .update_status(Request::new(UpdateStatusRequest {
            status_update: Some(status_update),
        }))
        .await?;

    response
        .into_inner()
        .task
        .and_then(|t| t.version)
        .ok_or_else(|| tonic::Status::internal("missing task version in response"))
}

// cancel_task handles cancellation requests from Lattice.
async fn cancel_task(
    client: &mut TaskClient,
    task_id: &str,
    entity_id: &str,
    task_active: bool,
) -> Result<(), tonic::Status> {
    // Get current task to retrieve status_version
    let task_response = client
        .get_task(Request::new(GetTaskRequest {
            task_id: task_id.to_string(),
            ..Default::default()
        }))
        .await?
        .into_inner()
        .task
        .ok_or_else(|| tonic::Status::internal("task is missing"))?;

    let current_status = task_response
        .status
        .as_ref()
        .map(|s| s.status)
        .unwrap_or(0);
    let version = task_response
        .version
        .ok_or_else(|| tonic::Status::internal("task version is missing"))?;
    let next_status_version = version.status_version + 1;

    let principal = system_principal(entity_id);

    let status_update = if task_active {
        // Reject cancellation: task is active and cannot be cancelled. Because
        // the cancellation is being rejected, we do not change the task status.
        StatusUpdate {
            version: Some(TaskVersion {
                task_id: task_id.to_string(),
                status_version: next_status_version,
                definition_version: 0,
                ..Default::default()
            }),
            status: Some(TaskStatus {
                status: current_status,
                task_error: Some(TaskError {
                    code: ErrorCode::Rejected as i32,
                    message: "Task is already active, and cannot be cancelled.".to_string(),
                    ..Default::default()
                }),
                ..Default::default()
            }),
            author: Some(principal),
            ..Default::default()
        }
    } else {
        // Accept cancellation
        StatusUpdate {
            version: Some(TaskVersion {
                task_id: task_id.to_string(),
                status_version: next_status_version,
                definition_version: 0,
                ..Default::default()
            }),
            status: Some(TaskStatus {
                status: Status::DoneNotOk as i32,
                task_error: Some(TaskError {
                    code: ErrorCode::Cancelled as i32,
                    message: "Task cancelled by agent.".to_string(),
                    ..Default::default()
                }),
                ..Default::default()
            }),
            author: Some(principal),
            ..Default::default()
        }
    };

    client
        .update_status(Request::new(UpdateStatusRequest {
            status_update: Some(status_update),
        }))
        .await?;

    if task_active {
        println!("Task could not be cancelled.");
    } else {
        println!("Task has been cancelled.");
    }
    Ok(())
}

// complete_task updates the task status to STATUS_DONE_OK.
async fn complete_task(
    client: &mut TaskClient,
    task_id: &str,
    entity_id: &str,
) -> Result<(), tonic::Status> {
    // Get current task to retrieve status_version
    let version = client
        .get_task(Request::new(GetTaskRequest {
            task_id: task_id.to_string(),
            ..Default::default()
        }))
        .await?
        .into_inner()
        .task
        .and_then(|t| t.version)
        .ok_or_else(|| tonic::Status::internal("task version is missing"))?;

    let status_update = StatusUpdate {
        version: Some(TaskVersion {
            task_id: task_id.to_string(),
            // Increment version and update to terminal state
            status_version: version.status_version + 1,
            definition_version: 0,
            ..Default::default()
        }),
        status: Some(TaskStatus {
            status: Status::DoneOk as i32,
            ..Default::default()
        }),
        author: Some(system_principal(entity_id)),
        ..Default::default()
    };

    client
        .update_status(Request::new(UpdateStatusRequest {
            status_update: Some(status_update),
        }))
        .await?;
    Ok(())
}

```

**Accepting cancellation:**

If the task can be cancelled, the agent accepts by setting the status to `STATUS_DONE_NOT_OK`
with a `TaskError` indicating `ERROR_CODE_CANCELLED`:

```go title={"Go (REST)"} startLine={226} maxLines={16}
package main

import (
	"context"
	"errors"
	"fmt"
	"io"
	"log"
	"net/http"
	"os"
	"strings"
	"time"

	Lattice "github.com/anduril/lattice-sdk-go/v5"
	"github.com/anduril/lattice-sdk-go/v5/client"
	"github.com/anduril/lattice-sdk-go/v5/option"
)

// Sets whether the task is currently being processed. If so, it cannot be cancelled.
var taskActive bool

func main() {
	// Get environment variables
	latticeEndpoint := os.Getenv("LATTICE_ENDPOINT")
	environmentToken := os.Getenv("ENVIRONMENT_TOKEN")
	sandboxesToken := os.Getenv("SANDBOXES_TOKEN")
	taskActiveStr := os.Getenv("TASK_ACTIVE")

	// Check required environment variables
	if latticeEndpoint == "" || environmentToken == "" || sandboxesToken == "" {
		fmt.Println("Missing required environment variables")
		os.Exit(1)
	}

	// Parse TASK_ACTIVE environment variable
	taskActive = strings.ToLower(taskActiveStr) == "true"

	// Initialize headers for sandbox authorization
	headers := http.Header{}
	headers.Add("Anduril-Sandbox-Authorization", fmt.Sprintf("Bearer %s", sandboxesToken))

	// Create the client
	LatticeClient := client.NewClient(
		option.WithToken(environmentToken),
		option.WithBaseURL(fmt.Sprintf("https://%s", latticeEndpoint)),
		option.WithHTTPHeader(headers),
	)

	// Set the entity ID to listen for tasks
	entityId := "<AGENT_ID>"
	fmt.Printf("Streaming tasks for entity: %s...\n", entityId)

	// Create context for the request
	ctx := context.Background()

	// Create agent stream request
	agentStreamRequest := Lattice.AgentStreamRequest{
		AgentSelector: &Lattice.EntityIDsSelector{
			EntityIDs: []string{entityId},
		},
	}

	// Stream tasks
	stream, err := LatticeClient.Tasks.StreamAsAgent(ctx, &agentStreamRequest)
	if err != nil {
		fmt.Printf("Error streaming tasks: %v\n", err)
		os.Exit(1)
	}

	// Process stream events
	for {
		select {
		case <-ctx.Done():
			log.Printf("Context canceled: %v", ctx.Err())
			return
		default:
			// Continue processing
		}

		event, err := stream.Recv()

		if errors.Is(err, io.EOF) {
			log.Println("Stream completed successfully.")
			return
		}

		if err != nil {
			log.Printf("Error receiving message: %v", err)
			continue
		}

		if event.Event == "heartbeat" {
			timestamp := *event.Heartbeat.Timestamp
			log.Printf("Heartbeat: %s", timestamp)
		} else {
			request := event.GetAgentRequest()
			if executeRequest := request.GetExecuteRequest(); executeRequest != nil {
				task := executeRequest.GetTask()
				if task != nil {
					taskId := *task.GetVersion().GetTaskID()
					taskStatusVersion := *task.GetVersion().GetStatusVersion()
					description := *task.GetDescription()

					log.Printf("Starting task %s, version %d: %s", taskId, taskStatusVersion, description)

					// Update task status to STATUS_EXECUTING
					result, err := executeTask(ctx, LatticeClient, taskId, int(taskStatusVersion), entityId)
					if err != nil {
						log.Printf("Error starting task: %v", err)
						continue
					}

					log.Printf("Started task with status version: %d", *result.StatusVersion)
				}
			} else if completeRequest := request.GetCompleteRequest(); completeRequest != nil {
				taskToComplete := completeRequest.GetTaskID()
				if taskToComplete != nil {
					log.Printf("Completing task: %s", *taskToComplete)
					err := completeTask(ctx, LatticeClient, *taskToComplete, entityId)
					if err != nil {
						log.Printf("Error completing task: %v", err)
					}
				}
			} else if cancelRequest := request.GetCancelRequest(); cancelRequest != nil {
				taskToCancel := cancelRequest.GetTaskID()
				if taskToCancel != nil {
					log.Printf("Cancelling task: %s", *taskToCancel)
					err := cancelTask(ctx, LatticeClient, *taskToCancel, entityId)
					if err != nil {
						log.Printf("Error cancelling task: %v", err)
					}
				}
			}
		}

		// Sleep briefly to prevent tight looping
		time.Sleep(100 * time.Millisecond)
	}
}

// executeTask updates the task status to STATUS_EXECUTING
func executeTask(ctx context.Context, client *client.Client, taskId string, taskStatusVersion int, agentEntityId string) (*Lattice.TaskVersion, error) {
	// Increment status version for the update
	taskStatusVersion++

	// Create system principal with the agent entity ID
	principal := Lattice.Principal{
		System: &Lattice.System{
			EntityID: &agentEntityId,
		},
	}

	taskStatus := Lattice.TaskStatus{
		Status: Lattice.TaskStatusStatusStatusExecuting.Ptr(),
	}

	// Create task status update request
	taskStatusUpdate := Lattice.TaskStatusUpdate{
		TaskID:        taskId,
		StatusVersion: &taskStatusVersion,
		NewStatus:     &taskStatus,
		Author:        &principal,
	}

	// Call the UpdateTaskStatus API
	task, err := client.Tasks.UpdateTaskStatus(ctx, &taskStatusUpdate)
	if err != nil {
		return nil, fmt.Errorf("error updating task status: %w", err)
	}

	return task.Version, nil
}

// cancelTask handles cancellation requests from Lattice
func cancelTask(ctx context.Context, client *client.Client, taskId string, entityId string) error {
	// Get current task to retrieve status_version
	getTaskRequest := Lattice.GetTaskRequest{
		TaskID: taskId,
	}
	task, err := client.Tasks.GetTask(ctx, &getTaskRequest)
	if err != nil {
		return fmt.Errorf("error getting task: %w", err)
	}
	if task.Status == nil || task.Version == nil || task.Version.StatusVersion == nil {
		return fmt.Errorf("task status or version is missing")
	}
	currentTaskStatus := task.Status.Status
	taskStatusVersion := *task.Version.StatusVersion
	taskStatusVersion++

	// Create system principal with the agent entity ID
	principal := Lattice.Principal{
		System: &Lattice.System{
			EntityID: &entityId,
		},
	}

	if taskActive {
		// Reject cancellation: task is active and cannot be cancelled
		rejectedMessage := "Task is already active, and cannot be cancelled."
		taskStatus := Lattice.TaskStatus{
			// Because the cancellation is being rejected, we do not
			// change the task status.
			Status: currentTaskStatus,
			TaskError: &Lattice.TaskError{
				Code:    Lattice.TaskErrorCodeErrorCodeRejected.Ptr(),
				Message: &rejectedMessage,
			},
		}

		taskStatusUpdate := Lattice.TaskStatusUpdate{
			TaskID:        taskId,
			StatusVersion: &taskStatusVersion,
			NewStatus:     &taskStatus,
			Author:        &principal,
		}

		_, err := client.Tasks.UpdateTaskStatus(ctx, &taskStatusUpdate)
		if err != nil {
			return fmt.Errorf("error updating task status: %w", err)
		}

		log.Println("Task could not be cancelled.")
	} else {
		// Accept cancellation
		cancelledMessage := "Task cancelled by agent."
		taskStatus := Lattice.TaskStatus{
			Status: Lattice.TaskStatusStatusStatusDoneNotOk.Ptr(),
			TaskError: &Lattice.TaskError{
				Code:    Lattice.TaskErrorCodeErrorCodeCancelled.Ptr(),
				Message: &cancelledMessage,
			},
		}

		taskStatusUpdate := Lattice.TaskStatusUpdate{
			TaskID:        taskId,
			StatusVersion: &taskStatusVersion,
			NewStatus:     &taskStatus,
			Author:        &principal,
		}

		_, err := client.Tasks.UpdateTaskStatus(ctx, &taskStatusUpdate)
		if err != nil {
			return fmt.Errorf("error updating task status: %w", err)
		}

		log.Println("Task has been cancelled.")
	}

	return nil
}

// completeTask updates the task status to STATUS_DONE_OK
func completeTask(ctx context.Context, client *client.Client, taskId string, entityId string) error {
	// Get current task to retrieve status_version
	getTaskRequest := Lattice.GetTaskRequest{
		TaskID: taskId,
	}
	task, err := client.Tasks.GetTask(ctx, &getTaskRequest)
	if err != nil {
		return fmt.Errorf("error getting task: %w", err)
	}

	if task.Version == nil || task.Version.StatusVersion == nil {
		return fmt.Errorf("task version is missing")
	}

	taskStatusVersion := *task.Version.StatusVersion
	// Increment version and update to terminal state
	taskStatusVersion++

	// Create system principal with the agent entity ID
	principal := Lattice.Principal{
		System: &Lattice.System{
			EntityID: &entityId,
		},
	}

	taskStatus := Lattice.TaskStatus{
		Status: Lattice.TaskStatusStatusStatusDoneOk.Ptr(),
	}

	taskStatusUpdate := Lattice.TaskStatusUpdate{
		TaskID:        taskId,
		StatusVersion: &taskStatusVersion,
		NewStatus:     &taskStatus,
		Author:        &principal,
	}

	_, err = client.Tasks.UpdateTaskStatus(ctx, &taskStatusUpdate)
	if err != nil {
		return fmt.Errorf("error updating task status: %w", err)
	}

	return nil
}

```

```java title={"Java (REST)"} startLine={221} maxLines={16}
package org.example;

import java.util.Arrays;
import java.util.concurrent.CompletableFuture;

import com.anduril.AsyncLattice;
import com.anduril.resources.tasks.requests.AgentStreamRequest;
import com.anduril.resources.tasks.requests.TaskStatusUpdate;
import com.anduril.resources.tasks.types.StreamAsAgentResponse;
import com.anduril.types.AgentStreamEvent;
import com.anduril.types.EntityIdsSelector;
import com.anduril.types.Principal;
import com.anduril.types.TaskError;
import com.anduril.types.TaskErrorCode;
import com.anduril.types.TaskStatus;
import com.anduril.types.TaskStatusStatus;
import com.anduril.types.TaskVersion;
public class ProcessTask {
    // Entity ID to listen for tasks - replace with your actual entity ID.
    private static final String ENTITY_ID = "<AGENT_ID>";

    // Sets whether the task is currently being processed. If so, it cannot be cancelled.
    private static boolean taskActive;

    public static void main(String[] args) {
        String endpoint = System.getenv("LATTICE_ENDPOINT");
        String environmentToken = System.getenv("ENVIRONMENT_TOKEN");
        String sandboxesToken = System.getenv("SANDBOXES_TOKEN");
        String taskActiveStr = System.getenv("TASK_ACTIVE");

        if (endpoint == null || environmentToken == null || sandboxesToken == null) {
            System.err.println("Missing required environment variables.");
            System.exit(1);
        }
        if (!endpoint.startsWith("https://")) endpoint = "https://" + endpoint;

        taskActive = "true".equalsIgnoreCase(taskActiveStr);

        try {
            AsyncLattice client = AsyncLattice.builder()
                .url(endpoint)
                .token(environmentToken)
                .addHeader("Anduril-Sandbox-Authorization", "Bearer " + sandboxesToken)
                .build();

            System.out.println("Streaming tasks for entity: " + ENTITY_ID + "...");

            // Create agent selector with the specified entity ID.
            EntityIdsSelector agentSelector = EntityIdsSelector.builder()
                .entityIds(Arrays.asList(ENTITY_ID))
                .build();

            // Create agent stream request.
            AgentStreamRequest agentRequest = AgentStreamRequest.builder()
                .agentSelector(agentSelector)
                .build();

            // Stream tasks from Lattice as an agent
            client.tasks().streamAsAgent(agentRequest)
                .thenAccept(requests -> {
                    for (StreamAsAgentResponse request : requests) {
                        if (request.isHeartbeat()) {
                            System.out.println("Heartbeat: " + request.getHeartbeat().get().getTimestamp().get());
                        } else {
                            AgentStreamEvent event = request.getAgentRequest().get();
                            if (event.getExecuteRequest().isPresent()) {
                                String taskId = event.getExecuteRequest().get()
                                    .getTask().get()
                                    .getVersion().get()
                                    .getTaskId().get();

                                int taskStatusVersion = event.getExecuteRequest().get()
                                    .getTask().get()
                                    .getVersion().get()
                                    .getStatusVersion().get();

                                String description = event.getExecuteRequest().get()
                                    .getTask().get()
                                    .getDescription().orElse("No description");

                                System.out.println("Starting task " + taskId + ", version " + taskStatusVersion + ": " + description);

                                try {
                                    TaskVersion result = startTask(client, taskId, taskStatusVersion, ENTITY_ID).get();
                                    System.out.println("Started task with status version: " + result.getStatusVersion().get());
                                } catch (Exception e) {
                                    System.err.println("Error starting task: " + e.getMessage());
                                }
                            } else if (event.getCompleteRequest().isPresent()) {
                                String taskId = event.getCompleteRequest().get().getTaskId().get();
                                System.out.println("Completing task: " + taskId);
                                try {
                                    completeTask(client, taskId, ENTITY_ID).get();
                                } catch (Exception e) {
                                    System.err.println("Error completing task: " + e.getMessage());
                                }
                            } else if (event.getCancelRequest().isPresent()) {
                                String taskId = event.getCancelRequest().get().getTaskId().get();
                                System.out.println("Cancelling task: " + taskId);
                                try {
                                    cancelTask(client, taskId, ENTITY_ID).get();
                                } catch (Exception e) {
                                    System.err.println("Error cancelling task: " + e.getMessage());
                                }
                            }
                        }
                    }
                })
                .exceptionally(ex -> {
                    System.err.println("Exception while streaming tasks: " + ex.getMessage());
                    return null;
                })
                .join();
        } catch (Exception e) {
            System.err.println("Error in streaming loop: " + e.getMessage());
        }
    }

    /**
     * Update task status to STATUS_EXECUTING.
     *
     * @param client Lattice client
     * @param taskId Task ID to update
     * @param taskStatusVersion Current task status version
     * @param agentEntityId Agent entity ID performing the update
     * @return CompletableFuture containing the updated TaskVersion
     */
    private static CompletableFuture<TaskVersion> startTask(AsyncLattice client, String taskId, Integer taskStatusVersion, String agentEntityId) {
        try {
            // Increment status version for the update
            taskStatusVersion += 1;

            // Create system principal with the agent entity ID.
            Principal principal = Principal.builder()
                .system(com.anduril.types.System.builder()
                    .entityId(agentEntityId)
                    .build())
                .build();

            // Create task status for update.
            TaskStatus taskStatus = TaskStatus.builder()
                .status(TaskStatusStatus.STATUS_EXECUTING)
                .build();

            // Create task status update request.
            TaskStatusUpdate taskStatusUpdate = TaskStatusUpdate.builder()
                .statusVersion(taskStatusVersion)
                .newStatus(taskStatus)
                .author(principal)
                .build();

            return client.tasks().updateTaskStatus(taskId, taskStatusUpdate)
                .thenApply(response -> response.getVersion().get());
        } catch (Exception e) {
            System.err.println("Encountered the following error while starting task: " + e.getMessage());
            return CompletableFuture.failedFuture(e);
        }
    }

    /**
     * Handle cancellation requests from Lattice.
     *
     * @param client Lattice client
     * @param taskId Task ID to cancel
     * @param entityId Agent entity ID performing the cancellation
     * @return CompletableFuture indicating completion
     */
    private static CompletableFuture<Void> cancelTask(AsyncLattice client, String taskId, String entityId) {
        // Get current task to retrieve status_version
        return client.tasks().getTask(taskId)
            .thenCompose(task -> {
                if (task.getStatus().isEmpty() || task.getVersion().isEmpty() || task.getVersion().get().getStatusVersion().isEmpty()) {
                    System.err.println("Task status or version is missing");
                    return CompletableFuture.failedFuture(new RuntimeException("Task status or version is missing"));
                }

                TaskStatusStatus currentTaskStatus = task.getStatus().get().getStatus().get();
                int taskStatusVersion = task.getVersion().get().getStatusVersion().get();
                taskStatusVersion += 1;

                // Create system principal with the agent entity ID.
                Principal principal = Principal.builder()
                    .system(com.anduril.types.System.builder()
                        .entityId(entityId)
                        .build())
                    .build();

                TaskStatus taskStatus;
                if (taskActive) {
                    // Reject cancellation: task is active and cannot be cancelled
                    taskStatus = TaskStatus.builder()
                        // Because the cancellation is being rejected, we do not
                        // change the task status.
                        .status(currentTaskStatus)
                        .taskError(TaskError.builder()
                            .code(TaskErrorCode.ERROR_CODE_REJECTED)
                            .message("Task is already active, and cannot be cancelled.")
                            .build())
                        .build();
                } else {
                    // Accept cancellation
                    taskStatus = TaskStatus.builder()
                        .status(TaskStatusStatus.STATUS_DONE_NOT_OK)
                        .taskError(TaskError.builder()
                            .code(TaskErrorCode.ERROR_CODE_CANCELLED)
                            .message("Task cancelled by agent.")
                            .build())
                        .build();
                }

                TaskStatusUpdate taskStatusUpdate = TaskStatusUpdate.builder()
                    .statusVersion(taskStatusVersion)
                    .newStatus(taskStatus)
                    .author(principal)
                    .build();

                return client.tasks().updateTaskStatus(taskId, taskStatusUpdate)
                    .thenAccept(response -> {
                        if (taskActive) {
                            System.out.println("Task could not be cancelled.");
                        } else {
                            System.out.println("Task has been cancelled.");
                        }
                    });
            })
            .exceptionally(ex -> {
                System.err.println("Encountered the following error while cancelling task: " + ex.getMessage());
                return null;
            });
    }

    /**
     * Update task status to STATUS_DONE_OK.
     *
     * @param client Lattice client
     * @param taskId Task ID to complete
     * @param entityId Agent entity ID performing the completion
     * @return CompletableFuture indicating completion
     */
    private static CompletableFuture<Void> completeTask(AsyncLattice client, String taskId, String entityId) {
        // Get current task to retrieve status_version
        return client.tasks().getTask(taskId)
            .thenCompose(task -> {
                if (task.getVersion().isEmpty() || task.getVersion().get().getStatusVersion().isEmpty()) {
                    System.err.println("Task version is missing");
                    return CompletableFuture.failedFuture(new RuntimeException("Task version is missing"));
                }

                int taskStatusVersion = task.getVersion().get().getStatusVersion().get();
                // Increment version and update to terminal state
                taskStatusVersion += 1;

                // Create system principal with the agent entity ID.
                Principal principal = Principal.builder()
                    .system(com.anduril.types.System.builder()
                        .entityId(entityId)
                        .build())
                    .build();

                TaskStatus taskStatus = TaskStatus.builder()
                    .status(TaskStatusStatus.STATUS_DONE_OK)
                    .build();

                TaskStatusUpdate taskStatusUpdate = TaskStatusUpdate.builder()
                    .statusVersion(taskStatusVersion)
                    .newStatus(taskStatus)
                    .author(principal)
                    .build();

                return client.tasks().updateTaskStatus(taskId, taskStatusUpdate)
                    .thenApply(response -> (Void) null);
            })
            .exceptionally(ex -> {
                System.err.println("Encountered the following error while completing task: " + ex.getMessage());
                return null;
            });
    }
}

```

```py title={"Python (REST)"} startLine={77} maxLines={16}
from anduril import AsyncLattice, EntityIdsSelector, TaskStatus, Principal, System, TaskError
import asyncio
import os
import sys

# Sets whether the task is currently being processed, if so, it cannot be cancelled.
task_active = os.getenv('TASK_ACTIVE', 'false').lower() == 'true'

lattice_endpoint = os.getenv('LATTICE_ENDPOINT')
environment_token = os.getenv('ENVIRONMENT_TOKEN')

# Remove sandboxes_token from the following statements if you are not developing on Sandboxes.
sandboxes_token = os.getenv('SANDBOXES_TOKEN')
if not environment_token or not lattice_endpoint or not sandboxes_token:
    print("Missing required environment variables.")
    sys.exit(1)

client = AsyncLattice(
    base_url=f"https://{lattice_endpoint}",
    token=lambda: str(environment_token),
    # Remove the following header if you are not developing on Sandboxes.
    headers={ "anduril-sandbox-authorization": f"Bearer {sandboxes_token}" }
)

async def start_task(task_id, task_status_version, entity_id):
    try:
        task_status_version += 1
        response = await client.tasks.update_task_status(
            task_id=task_id,
            status_version=task_status_version,
            new_status=TaskStatus(
                status="STATUS_EXECUTING"
            ),
            author=Principal(
                system=System(
                    entity_id=entity_id
                )
            )
        )
        return response
    except Exception as error:
        print(f"Encountered the following error while starting task: {error}")

async def cancel_task(task_id, entity_id):
    try:
        # Get current task to retrieve status_version
        task = await client.tasks.get_task(task_id=task_id)
        if not task.status or not task.version or task.version.status_version is None:
            print("Task status or version is missing")
            return

        current_task_status = task.status.status
        task_status_version = task.version.status_version
        task_status_version += 1
        
        if task_active:
            await client.tasks.update_task_status(
                task_id=task_id,
                status_version=task_status_version,
                new_status=TaskStatus(
                    # Because the cancellation is being rejected, we do not
                    # change the task status.
                    status=current_task_status,
                    task_error=TaskError(
                        code="ERROR_CODE_REJECTED",
                        message="Task is already active, and cannot be cancelled."
                    )
                ),
                author=Principal(
                    system=System(
                        entity_id=entity_id
                    )
                )
            )
            print("Task could not be cancelled.")
        else:
            await client.tasks.update_task_status(
                task_id=task_id,
                status_version=task_status_version,
                new_status=TaskStatus(
                    status="STATUS_DONE_NOT_OK",
                    task_error=TaskError(
                        code="ERROR_CODE_CANCELLED",
                        message="Task cancelled by agent."
                    )
                ),
                author=Principal(
                    system=System(
                        entity_id=entity_id
                    )
                )
            )
            print("Task has been cancelled.")
    except Exception as error:
        print(f"Encountered the following error while cancelling task: {error}")

async def complete_task(task_id, entity_id):
    try:
        # Get current task to retrieve status_version
        task = await client.tasks.get_task(task_id=task_id)
        if not task.version or task.version.status_version is None:
            print("Task version is missing")
            return

        task_status_version = task.version.status_version

        # Increment version and update to terminal state
        task_status_version += 1
        response = await client.tasks.update_task_status(
            task_id=task_id,
            status_version=task_status_version,
            new_status=TaskStatus(
                status="STATUS_DONE_OK"
            ),
            author=Principal(
                system=System(
                    entity_id=entity_id
                )
            )
        )
        return response
    except Exception as error:
        print(f"Encountered the following error while completing task: {error}")

async def main(entity_id):
    try:
        print(f"Streaming tasks for entity: {entity_id}...")
        requests = client.tasks.stream_as_agent(
            agent_selector=EntityIdsSelector(
                entity_ids=[entity_id]
            )
        )

        async for request in requests:
            if request.event == "heartbeat":
                print(f"Heartbeat: {request.timestamp}")
                continue

            if request.execute_request and request.execute_request.task and request.execute_request.task.version:
                version = request.execute_request.task.version
                task_id = version.task_id
                task_status_version = version.status_version
                task_description = request.execute_request.task.description

                print(f"Starting task {task_id}, version {task_status_version}: {task_description}")
                await start_task(task_id, task_status_version, entity_id)

            elif request.complete_request:
                task_id = request.complete_request.task_id
                print(f"Completing task: {task_id}")
                await complete_task(task_id, entity_id)

            elif request.cancel_request:
                task_id = request.cancel_request.task_id
                print(f"Cancelling task: {task_id}")
                await cancel_task(task_id, entity_id)

    except Exception as error:
        print(f"Exception: {error}")

if __name__ == "__main__":
    raise SystemExit(asyncio.run(main(entity_id="<AGENT_ID>")))

```

```ts title={"TypeScript (REST)"} startLine={96} maxLines={16}
import { LatticeClient } from "@anduril-industries/lattice-sdk";

// Entity ID to listen for tasks - replace with your actual entity ID.
const ENTITY_ID = "<AGENT_ID>";



const latticeEndpoint = process.env.LATTICE_ENDPOINT;
const environmentToken = process.env.ENVIRONMENT_TOKEN;
const sandboxesToken = process.env.SANDBOXES_TOKEN;
const taskActiveStr = process.env.TASK_ACTIVE;

if (!latticeEndpoint || !environmentToken || !sandboxesToken) {
    console.error("Missing required environment variables.");
    process.exit(1);
}

// Sets whether the task is currently being processed. If so, it cannot be cancelled.
const taskActive = taskActiveStr?.toLowerCase() === "true";

const client = new LatticeClient({
    baseUrl: latticeEndpoint.startsWith("https://") ? latticeEndpoint : `https://${latticeEndpoint}`,
    token: environmentToken,
    headers: { "Anduril-Sandbox-Authorization": `Bearer ${sandboxesToken}` }
});


// Update task status to STATUS_EXECUTING.
async function startTask(taskId: string, taskStatusVersion: number, agentEntityId: string) {
    try {
        // Increment status version for the update
        taskStatusVersion += 1;

        // Create system principal with the agent entity ID
        const principal = {
            system: {
                entityId: agentEntityId
            }
        };

        // Create task status for update
        const taskStatus = {
            status: "STATUS_EXECUTING" as const
        };

        // Update task status
        const response = await client.tasks.updateTaskStatus({
            taskId: taskId,
            statusVersion: taskStatusVersion,
            newStatus: taskStatus,
            author: principal
        });

        return response;
    } catch (error) {
        console.error(`Encountered the following error while starting task: ${error}`);
        throw error;
    }
}

// Handle cancellation requests from Lattice.
async function cancelTask(taskId: string, entityId: string) {
    try {
        // Get current task to retrieve status_version
        const task = await client.tasks.getTask({ taskId: taskId });

        if (!task.status || !task.version?.statusVersion) {
            console.error("Task status or version is missing");
            return;
        }

        const currentTaskStatus = task.status.status;
        let taskStatusVersion = task.version.statusVersion;
        taskStatusVersion += 1;

        // Create system principal with the agent entity ID
        const principal = {
            system: {
                entityId: entityId
            }
        };

        let taskStatus;
        if (taskActive) {
            // Reject cancellation: task is active and cannot be cancelled
            taskStatus = {
                // Because the cancellation is being rejected, we do not
                // change the task status.
                status: currentTaskStatus,
                taskError: {
                    code: "ERROR_CODE_REJECTED" as const,
                    message: "Task is already active, and cannot be cancelled."
                }
            };
        } else {
            // Accept cancellation
            taskStatus = {
                status: "STATUS_DONE_NOT_OK" as const,
                taskError: {
                    code: "ERROR_CODE_CANCELLED" as const,
                    message: "Task cancelled by agent."
                }
            };
        }

        await client.tasks.updateTaskStatus({
            taskId: taskId,
            statusVersion: taskStatusVersion,
            newStatus: taskStatus,
            author: principal
        });

        if (taskActive) {
            console.log("Task could not be cancelled.");
        } else {
            console.log("Task has been cancelled.");
        }
    } catch (error) {
        console.error(`Encountered the following error while cancelling task: ${error}`);
    }
}


// Update task status to STATUS_DONE_OK.
async function completeTask(taskId: string, entityId: string) {
    try {
        // Get current task to retrieve status_version
        const task = await client.tasks.getTask({ taskId: taskId });

        if (!task.version?.statusVersion) {
            console.error("Task version is missing");
            return;
        }

        let taskStatusVersion = task.version.statusVersion;
        // Increment version and update to terminal state
        taskStatusVersion += 1;

        // Create system principal with the agent entity ID
        const principal = {
            system: {
                entityId: entityId
            }
        };

        const taskStatus = {
            status: "STATUS_DONE_OK" as const
        };

        await client.tasks.updateTaskStatus({
            taskId: taskId,
            statusVersion: taskStatusVersion,
            newStatus: taskStatus,
            author: principal
        });

        console.log("Task has been completed.");
    } catch (error) {
        console.error(`Encountered the following error while completing task: ${error}`);
    }
}

async function App() {
    try {
        console.log(`Streaming tasks for entity: ${ENTITY_ID}...`);

        // Stream tasks from Lattice as an agent
        const requests = await client.tasks.streamAsAgent({
            agentSelector: {
                entityIds: [ENTITY_ID]
            }
        });

        for await (const request of requests) {
            if (request.event === "heartbeat") {
                console.log(`Heartbeat: ${request.timestamp}`);
                continue;
            }

            if (request.executeRequest) {
                const taskId = request.executeRequest.task?.version?.taskId;
                const taskStatusVersion = request.executeRequest.task?.version?.statusVersion;
                const description = request.executeRequest.task?.description || "No description";

                if (taskId && taskStatusVersion !== undefined) {
                    console.log(`Starting task ${taskId}, version ${taskStatusVersion}: ${description}`);

                    try {
                        const result = await startTask(taskId, taskStatusVersion, ENTITY_ID);
                        console.log(`Started task with status version: ${result.version?.statusVersion}`);
                    } catch (error) {
                        console.error(`Error starting task: ${error}`);
                    }
                }
            } else if (request.completeRequest) {
                const taskId = request.completeRequest.taskId;
                if (taskId) {
                    console.log(`Completing task: ${taskId}`);
                    try {
                        await completeTask(taskId, ENTITY_ID);
                    } catch (error) {
                        console.error(`Error completing task: ${error}`);
                    }
                }
            } else if (request.cancelRequest) {
                const taskId = request.cancelRequest.taskId;
                if (taskId) {
                    console.log(`Cancelling task: ${taskId}`);
                    try {
                        await cancelTask(taskId, ENTITY_ID);
                    } catch (error) {
                        console.error(`Error cancelling task: ${error}`);
                    }
                }
            }
        }
    } catch (error) {
        console.error(`Error in streaming loop: ${error}`);
    }
}

App();

```

```rs title={"Rust (REST)"} startLine={113} maxLines={16}
use anduril_lattice_sdk::prelude::*;

use std::env;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Sets whether the task is currently being processed; if so, it cannot be
    // cancelled.
    let task_active = env::var("TASK_ACTIVE")
        .map(|v| v.to_lowercase() == "true")
        .unwrap_or(false);

    let lattice_endpoint = env::var("LATTICE_ENDPOINT")
        .expect("LATTICE_ENDPOINT environment variable not set");
    let environment_token = env::var("ENVIRONMENT_TOKEN")
        .expect("ENVIRONMENT_TOKEN environment variable not set");
    // Remove sandboxes_token if you are not developing on Sandboxes.
    let sandboxes_token = env::var("SANDBOXES_TOKEN")
        .expect("SANDBOXES_TOKEN environment variable not set");

    if environment_token.is_empty() || lattice_endpoint.is_empty() || sandboxes_token.is_empty() {
        eprintln!("Missing required environment variables:");
        eprintln!("  LATTICE_ENDPOINT");
        eprintln!("  ENVIRONMENT_TOKEN");
        eprintln!("  SANDBOXES_TOKEN");
        std::process::exit(1);
    }

    let config = ClientConfig {
        base_url: format!("https://{}", lattice_endpoint),
        token: Some(environment_token),
        // Remove custom_headers if you are not developing on Sandboxes.
        custom_headers: HashMap::from([(
            "anduril-sandbox-authorization".to_string(),
            format!("Bearer {}", sandboxes_token),
        )]),
        ..Default::default()
    };
    let client = Lattice::new(config)?;

    // Replace with the entity ID of the agent to process tasks for.
    let entity_id = "<AGENT_ID>";
    run(&client, entity_id, task_active).await;

    Ok(())
}

/// Author principal for a system entity.
fn system_author(entity_id: &str) -> Principal {
    Principal {
        system: Some(System {
            entity_id: Some(entity_id.to_string()),
            ..Default::default()
        }),
        ..Default::default()
    }
}

/// Move a task into the executing state by advancing its status version.
async fn start_task(client: &Lattice, task_id: &str, task_status_version: i64, entity_id: &str) {
    let request = TaskStatusUpdate {
        status_version: Some(task_status_version + 1),
        new_status: Some(TaskStatus {
            status: Some(TaskStatusStatus::StatusExecuting),
            ..Default::default()
        }),
        author: Some(system_author(entity_id)),
    };

    if let Err(error) = client.tasks.update_task_status(task_id, &request, None).await {
        eprintln!("Encountered the following error while starting task: {}", error);
    }
}

/// Cancel a task, or reject the cancellation if the task is actively executing.
async fn cancel_task(client: &Lattice, task_id: &str, entity_id: &str, task_active: bool) {
    // Get the current task to retrieve its status_version.
    let task = match client.tasks.get_task(task_id, None).await {
        Ok(task) => task,
        Err(error) => {
            eprintln!("Encountered the following error while cancelling task: {}", error);
            return;
        }
    };

    let (Some(status), Some(version)) = (task.status.as_ref(), task.version.as_ref()) else {
        println!("Task status or version is missing");
        return;
    };
    let Some(current_status_version) = version.status_version else {
        println!("Task status or version is missing");
        return;
    };
    let current_task_status = status.status.clone();
    let next_status_version = current_status_version + 1;

    let request = if task_active {
        // Because the cancellation is being rejected, we do not change the task
        // status.
        TaskStatusUpdate {
            status_version: Some(next_status_version),
            new_status: Some(TaskStatus {
                status: current_task_status,
                task_error: Some(TaskError {
                    code: Some(TaskErrorCode::ErrorCodeRejected),
                    message: Some("Task is already active, and cannot be cancelled.".to_string()),
                    ..Default::default()
                }),
                ..Default::default()
            }),
            author: Some(system_author(entity_id)),
        }
    } else {
        TaskStatusUpdate {
            status_version: Some(next_status_version),
            new_status: Some(TaskStatus {
                status: Some(TaskStatusStatus::StatusDoneNotOk),
                task_error: Some(TaskError {
                    code: Some(TaskErrorCode::ErrorCodeCancelled),
                    message: Some("Task cancelled by agent.".to_string()),
                    ..Default::default()
                }),
                ..Default::default()
            }),
            author: Some(system_author(entity_id)),
        }
    };

    match client.tasks.update_task_status(task_id, &request, None).await {
        Ok(_) if task_active => println!("Task could not be cancelled."),
        Ok(_) => println!("Task has been cancelled."),
        Err(error) => eprintln!("Encountered the following error while cancelling task: {}", error),
    }
}

/// Complete a task by advancing it to the terminal STATUS_DONE_OK state.
async fn complete_task(client: &Lattice, task_id: &str, entity_id: &str) {
    // Get the current task to retrieve its status_version.
    let task = match client.tasks.get_task(task_id, None).await {
        Ok(task) => task,
        Err(error) => {
            eprintln!("Encountered the following error while completing task: {}", error);
            return;
        }
    };

    let Some(current_status_version) = task.version.and_then(|v| v.status_version) else {
        println!("Task version is missing");
        return;
    };

    let request = TaskStatusUpdate {
        // Increment version and update to a terminal state.
        status_version: Some(current_status_version + 1),
        new_status: Some(TaskStatus {
            status: Some(TaskStatusStatus::StatusDoneOk),
            ..Default::default()
        }),
        author: Some(system_author(entity_id)),
    };

    if let Err(error) = client.tasks.update_task_status(task_id, &request, None).await {
        eprintln!("Encountered the following error while completing task: {}", error);
    }
}

/// Stream tasks for the agent and route each request to the matching handler.
async fn run(client: &Lattice, entity_id: &str, task_active: bool) {
    println!("Streaming tasks for entity: {}...", entity_id);

    let request = AgentStreamRequest {
        agent_selector: Some(EntityIdsSelector {
            entity_ids: Some(vec![entity_id.to_string()]),
            ..Default::default()
        }),
        ..Default::default()
    };

    let stream = match client.tasks.stream_as_agent(&request, None).await {
        Ok(stream) => stream,
        Err(error) => {
            eprintln!("Exception: {}", error);
            return;
        }
    };
    let mut stream = std::pin::pin!(stream);

    while let Some(result) = stream.next().await {
        match result {
            Ok(StreamAsAgentResponse::Heartbeat { data, .. }) => {
                println!("Heartbeat: {:?}", data.heartbeat_object_fields.timestamp);
            }
            Ok(StreamAsAgentResponse::AgentRequest { data, .. }) => {
                let fields = &data.agent_task_request_fields;
                if let Some(execute) = &fields.execute_request {
                    if let Some(task) = &execute.task {
                        if let Some(version) = &task.version {
                            let task_id = version.task_id.clone().unwrap_or_default();
                            let task_status_version = version.status_version.unwrap_or(0);
                            println!(
                                "Starting task {}, version {}: {:?}",
                                task_id, task_status_version, task.description
                            );
                            start_task(client, &task_id, task_status_version, entity_id).await;
                        }
                    }
                } else if let Some(complete) = &fields.complete_request {
                    if let Some(task_id) = &complete.task_id {
                        println!("Completing task: {}", task_id);
                        complete_task(client, task_id, entity_id).await;
                    }
                } else if let Some(cancel) = &fields.cancel_request {
                    if let Some(task_id) = &cancel.task_id {
                        println!("Cancelling task: {}", task_id);
                        cancel_task(client, task_id, entity_id, task_active).await;
                    }
                }
            }
            Ok(other) => println!("Event: {:?}", other),
            Err(error) => eprintln!("Exception: {}", error),
        }
    }
}

```

```go title={"Go (gRPC)"} startLine={236} maxLines={16}
// This Go example is compatible with artifacts generated using
// the following grpc/go plugin: https://buf.build/anduril/lattice-sdk/sdks/main:grpc/go
package main

import (
	"context"
	"fmt"
	"io"
	"log"
	"os"
	"strings"
	"time"

	"schema-registry.developer.anduril.com/gen/go/anduril/lattice-sdk/grpc/go/anduril/taskmanager/v1/taskmanagerv1grpc"
	taskmanagerv1 "schema-registry.developer.anduril.com/gen/go/anduril/lattice-sdk/protocolbuffers/go/anduril/taskmanager/v1"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"
)

// Sets whether the task is currently being processed; if so, it cannot be cancelled.
var taskActive bool

func main() {
	environmentToken := os.Getenv("ENVIRONMENT_TOKEN")
	latticeEndpoint := os.Getenv("LATTICE_ENDPOINT")
	sandboxesToken := os.Getenv("SANDBOXES_TOKEN")
	taskActiveStr := os.Getenv("TASK_ACTIVE")

	if latticeEndpoint == "" || environmentToken == "" || sandboxesToken == "" {
		fmt.Println("Missing required environment variables")
		os.Exit(1)
	}

	// Parse TASK_ACTIVE environment variable
	taskActive = strings.ToLower(taskActiveStr) == "true"

	// Setup authentication
	auth := &BearerTokenAuth{
		Token:          environmentToken,
		SandboxesToken: sandboxesToken,
	}

	opts := []grpc.DialOption{
		grpc.WithTransportCredentials(credentials.NewClientTLSFromCert(nil, "")),
		grpc.WithPerRPCCredentials(auth),
	}

	conn, err := grpc.NewClient(latticeEndpoint, opts...)
	if err != nil {
		log.Fatalf("Did not connect: %v", err)
	}
	defer conn.Close()

	client := taskmanagerv1grpc.NewTaskManagerAPIClient(conn)

	// Set the entity ID to listen for tasks
	entityId := "<AGENT_ID>"
	fmt.Printf("Listening for tasks for entity %s...\n", entityId)

	for {
		try := func() bool {
			ctx := context.Background()

			// Create the ListenAsAgent request
			listenRequest := &taskmanagerv1.ListenAsAgentRequest{
				AgentSelector: &taskmanagerv1.ListenAsAgentRequest_EntityIds{
					EntityIds: &taskmanagerv1.EntityIds{
						EntityIds: []string{entityId},
					},
				},
			}

			// Call the ListenAsAgent method to get a stream of task requests
			stream, err := client.ListenAsAgent(ctx, listenRequest)
			if err != nil {
				fmt.Printf("Error starting task listener: %v\n", err)
				return false
			}

			for {
				response, err := stream.Recv()
				if err == io.EOF {
					fmt.Println("Stream ended")
					return false
				}
				if err != nil {
					fmt.Printf("Error receiving from stream: %v\n", err)
					return false
				}

				// Process the response based on the type of task request
				switch x := response.Request.(type) {
				case *taskmanagerv1.ListenAsAgentResponse_ExecuteRequest:
					if x.ExecuteRequest != nil && x.ExecuteRequest.Task != nil &&
						x.ExecuteRequest.Task.Version != nil && x.ExecuteRequest.Task.Version.TaskId != "" {
						// Extract task-specific information
						taskId := x.ExecuteRequest.Task.Version.TaskId
						taskStatusVersion := x.ExecuteRequest.Task.Version.StatusVersion

						fmt.Printf("Starting task %s, version %d\n", taskId, taskStatusVersion)

						// Update task status to STATUS_EXECUTING
						result, err := executeTask(ctx, client, taskId, taskStatusVersion, entityId)
						if err != nil {
							fmt.Printf("Error starting task: %v\n", err)
							return false
						}

						fmt.Printf("Started task with status version: %d\n", result.StatusVersion)
					}

				case *taskmanagerv1.ListenAsAgentResponse_CompleteRequest:
					if x.CompleteRequest != nil && x.CompleteRequest.TaskId != "" {
						fmt.Printf("Completing task: %s\n", x.CompleteRequest.TaskId)
						err := completeTask(ctx, client, x.CompleteRequest.TaskId, entityId)
						if err != nil {
							fmt.Printf("Error completing task: %v\n", err)
						}
					}

				case *taskmanagerv1.ListenAsAgentResponse_CancelRequest:
					if x.CancelRequest != nil && x.CancelRequest.TaskId != "" {
						fmt.Printf("Cancelling task: %s\n", x.CancelRequest.TaskId)
						err := cancelTask(ctx, client, x.CancelRequest.TaskId, entityId)
						if err != nil {
							fmt.Printf("Error cancelling task: %v\n", err)
						}
					}
				}
			}
		}()

		if !try {
			// If there was an error, wait a bit before retrying
			time.Sleep(1 * time.Second)
		}
	}
}

// executeTask updates the task status to STATUS_EXECUTING
func executeTask(ctx context.Context, client taskmanagerv1grpc.TaskManagerAPIClient, taskId string, taskStatusVersion uint32, agentEntityId string) (*taskmanagerv1.TaskVersion, error) {
	// Increment status version for the update
	taskStatusVersion++

	// Create system principal with the agent entity ID
	principal := &taskmanagerv1.Principal{
		Agent: &taskmanagerv1.Principal_System{
			System: &taskmanagerv1.System{
				EntityId: agentEntityId,
			},
		},
	}

	// Create task status update
	taskStatus := &taskmanagerv1.TaskStatus{
		Status: taskmanagerv1.Status_STATUS_EXECUTING,
	}

	// Create status update request
	statusUpdate := &taskmanagerv1.StatusUpdate{
		Version: &taskmanagerv1.TaskVersion{
			TaskId:            taskId,
			DefinitionVersion: 0,
			StatusVersion:     taskStatusVersion,
		},
		Status: taskStatus,
		Author: principal,
	}

	// Create the update status request
	updateRequest := &taskmanagerv1.UpdateStatusRequest{
		StatusUpdate: statusUpdate,
	}

	// Call the UpdateStatus API
	response, err := client.UpdateStatus(ctx, updateRequest)
	if err != nil {
		return nil, fmt.Errorf("error updating task status: %w", err)
	}

	return response.Task.Version, nil
}

// cancelTask handles cancellation requests from Lattice
func cancelTask(ctx context.Context, client taskmanagerv1grpc.TaskManagerAPIClient, taskId string, entityId string) error {
	// Get current task to retrieve status_version
	getTaskRequest := &taskmanagerv1.GetTaskRequest{
		TaskId: taskId,
	}
	taskResponse, err := client.GetTask(ctx, getTaskRequest)
	if err != nil {
		return fmt.Errorf("error getting task: %w", err)
	}
	if taskResponse.Task == nil || taskResponse.Task.Status == nil || taskResponse.Task.Version == nil {
		return fmt.Errorf("task status or version is missing")
	}
	currentTaskStatus := taskResponse.Task.Status.Status
	taskStatusVersion := taskResponse.Task.Version.StatusVersion
	taskStatusVersion++

	// Create system principal with the agent entity ID
	principal := &taskmanagerv1.Principal{
		Agent: &taskmanagerv1.Principal_System{
			System: &taskmanagerv1.System{
				EntityId: entityId,
			},
		},
	}

	if taskActive {
		// Reject cancellation: task is active and cannot be cancelled
		taskStatus := &taskmanagerv1.TaskStatus{
			// Because the cancellation is being rejected, we do not
			// change the task status.
			Status: currentTaskStatus,
			TaskError: &taskmanagerv1.TaskError{
				Code:    taskmanagerv1.ErrorCode_ERROR_CODE_REJECTED,
				Message: "Task is already active, and cannot be cancelled.",
			},
		}

		statusUpdate := &taskmanagerv1.StatusUpdate{
			Version: &taskmanagerv1.TaskVersion{
				TaskId:            taskId,
				DefinitionVersion: 0,
				StatusVersion:     taskStatusVersion,
			},
			Status: taskStatus,
			Author: principal,
		}

		updateRequest := &taskmanagerv1.UpdateStatusRequest{
			StatusUpdate: statusUpdate,
		}

		_, err := client.UpdateStatus(ctx, updateRequest)
		if err != nil {
			return fmt.Errorf("error updating task status: %w", err)
		}

		log.Println("Task could not be cancelled.")
	} else {
		// Accept cancellation
		taskStatus := &taskmanagerv1.TaskStatus{
			Status: taskmanagerv1.Status_STATUS_DONE_NOT_OK,
			TaskError: &taskmanagerv1.TaskError{
				Code:    taskmanagerv1.ErrorCode_ERROR_CODE_CANCELLED,
				Message: "Task cancelled by agent.",
			},
		}

		statusUpdate := &taskmanagerv1.StatusUpdate{
			Version: &taskmanagerv1.TaskVersion{
				TaskId:            taskId,
				DefinitionVersion: 0,
				StatusVersion:     taskStatusVersion,
			},
			Status: taskStatus,
			Author: principal,
		}

		updateRequest := &taskmanagerv1.UpdateStatusRequest{
			StatusUpdate: statusUpdate,
		}

		_, err := client.UpdateStatus(ctx, updateRequest)
		if err != nil {
			return fmt.Errorf("error updating task status: %w", err)
		}

		log.Println("Task has been cancelled.")
	}

	return nil
}

// completeTask updates the task status to STATUS_DONE_OK
func completeTask(ctx context.Context, client taskmanagerv1grpc.TaskManagerAPIClient, taskId string, entityId string) error {
	// Get current task to retrieve status_version
	getTaskRequest := &taskmanagerv1.GetTaskRequest{
		TaskId: taskId,
	}

	taskResponse, err := client.GetTask(ctx, getTaskRequest)
	if err != nil {
		return fmt.Errorf("error getting task: %w", err)
	}

	if taskResponse.Task == nil || taskResponse.Task.Version == nil {
		return fmt.Errorf("task version is missing")
	}

	taskStatusVersion := taskResponse.Task.Version.StatusVersion
	// Increment version and update to terminal state
	taskStatusVersion++

	// Create system principal with the agent entity ID
	principal := &taskmanagerv1.Principal{
		Agent: &taskmanagerv1.Principal_System{
			System: &taskmanagerv1.System{
				EntityId: entityId,
			},
		},
	}

	taskStatus := &taskmanagerv1.TaskStatus{
		Status: taskmanagerv1.Status_STATUS_DONE_OK,
	}

	statusUpdate := &taskmanagerv1.StatusUpdate{
		Version: &taskmanagerv1.TaskVersion{
			TaskId:            taskId,
			DefinitionVersion: 0,
			StatusVersion:     taskStatusVersion,
		},
		Status: taskStatus,
		Author: principal,
	}

	updateRequest := &taskmanagerv1.UpdateStatusRequest{
		StatusUpdate: statusUpdate,
	}

	_, err = client.UpdateStatus(ctx, updateRequest)
	if err != nil {
		return fmt.Errorf("error updating task status: %w", err)
	}

	return nil
}

```

```py title={"Python (gRPC)"} startLine={76} maxLines={16}
# This Python example is compatible with artifacts generated using the following
# Buf plugins: https://schema-registry.developer.anduril.com/anduril/lattice-sdk/sdks/main:bufbuild/py
# and https://schema-registry.developer.anduril.com/anduril/lattice-sdk/sdks/main:connectrpc/py

import os
import sys
import time
from bearer_auth import create_client
from protobuf import Oneof

from anduril.taskmanager.v1.task_pub_pb import (
    ErrorCode,
    Principal,
    Status,
    System,
    TaskError,
    TaskStatus,
    TaskVersion,
    StatusUpdate,
)
from anduril.taskmanager.v1.task_manager_api_pub_pb import (
    EntityIds,
    GetTaskRequest,
    ListenAsAgentRequest,
    UpdateStatusRequest,
)
from anduril.taskmanager.v1.task_manager_api_pub_connect import TaskManagerAPIClientSync


def execute_task(client, task_id, task_status_version, agent_entity_id):
    """Update the task status to STATUS_EXECUTING."""
    task_status_version += 1

    principal = Principal(agent=Oneof("system", System(entity_id=agent_entity_id)))
    task_status = TaskStatus(status=Status.EXECUTING)

    status_update = StatusUpdate(
        version=TaskVersion(task_id=task_id, definition_version=0, status_version=task_status_version),
        status=task_status,
        author=principal,
    )

    response = client.update_status(UpdateStatusRequest(status_update=status_update))
    return response.task.version


def cancel_task(client, task_id, entity_id, task_active):
    """Handle a cancellation request from Lattice."""
    task_response = client.get_task(GetTaskRequest(task_id=task_id))
    if not task_response.task or not task_response.task.status or not task_response.task.version:
        raise RuntimeError("task status or version is missing")

    current_task_status = task_response.task.status.status
    task_status_version = task_response.task.version.status_version + 1

    principal = Principal(agent=Oneof("system", System(entity_id=entity_id)))

    if task_active:
        # Reject cancellation: task is active and cannot be cancelled
        task_status = TaskStatus(
            # Because the cancellation is being rejected, we do not
            # change the task status.
            status=current_task_status,
            task_error=TaskError(
                code=ErrorCode.REJECTED,
                message="Task is already active, and cannot be cancelled.",
            ),
        )
        status_update = StatusUpdate(
            version=TaskVersion(task_id=task_id, definition_version=0, status_version=task_status_version),
            status=task_status,
            author=principal,
        )
        client.update_status(UpdateStatusRequest(status_update=status_update))
        print("Task could not be cancelled.")
    else:
        # Accept cancellation
        task_status = TaskStatus(
            status=Status.DONE_NOT_OK,
            task_error=TaskError(
                code=ErrorCode.CANCELLED,
                message="Task cancelled by agent.",
            ),
        )
        status_update = StatusUpdate(
            version=TaskVersion(task_id=task_id, definition_version=0, status_version=task_status_version),
            status=task_status,
            author=principal,
        )
        client.update_status(UpdateStatusRequest(status_update=status_update))
        print("Task has been cancelled.")


def complete_task(client, task_id, entity_id):
    """Update the task status to STATUS_DONE_OK."""
    task_response = client.get_task(GetTaskRequest(task_id=task_id))
    if not task_response.task or not task_response.task.version:
        raise RuntimeError("task version is missing")

    task_status_version = task_response.task.version.status_version + 1

    principal = Principal(agent=Oneof("system", System(entity_id=entity_id)))
    task_status = TaskStatus(status=Status.DONE_OK)

    status_update = StatusUpdate(
        version=TaskVersion(task_id=task_id, definition_version=0, status_version=task_status_version),
        status=task_status,
        author=principal,
    )
    client.update_status(UpdateStatusRequest(status_update=status_update))


def main():
    environment_token = os.getenv("ENVIRONMENT_TOKEN")
    lattice_endpoint = os.getenv("LATTICE_ENDPOINT")
    sandboxes_token = os.getenv("SANDBOXES_TOKEN")

    # Sets whether the task is currently being processed; if so, it cannot be cancelled.
    task_active = os.getenv("TASK_ACTIVE", "").lower() == "true"

    if not environment_token or not lattice_endpoint or not sandboxes_token:
        print("Missing required environment variables", file=sys.stderr)
        sys.exit(1)

    # Create the API client over Connect gRPC with authentication
    client = create_client(TaskManagerAPIClientSync, lattice_endpoint, environment_token, sandboxes_token)

    entity_id = "<AGENT_ID>"
    print(f"Listening for tasks for entity {entity_id}...")

    while True:
        try:
            listen_request = ListenAsAgentRequest(
                agent_selector=Oneof("entity_ids", EntityIds(entity_ids=[entity_id]))
            )
            stream = client.listen_as_agent(listen_request)

            for response in stream:
                request = response.request
                if request is None:
                    continue
                if request.field == "execute_request":
                    task = request.value.task
                    if task and task.version and task.version.task_id:
                        task_id = task.version.task_id
                        task_status_version = task.version.status_version
                        print(f"Starting task {task_id}, version {task_status_version}")

                        try:
                            result = execute_task(client, task_id, task_status_version, entity_id)
                            print(f"Started task with status version: {result.status_version}")
                        except Exception as error:
                            print(f"Error starting task: {error}", file=sys.stderr)
                            break
                elif request.field == "complete_request":
                    if request.value.task_id:
                        print(f"Completing task: {request.value.task_id}")
                        try:
                            complete_task(client, request.value.task_id, entity_id)
                        except Exception as error:
                            print(f"Error completing task: {error}", file=sys.stderr)
                elif request.field == "cancel_request":
                    if request.value.task_id:
                        print(f"Cancelling task: {request.value.task_id}")
                        try:
                            cancel_task(client, request.value.task_id, entity_id, task_active)
                        except Exception as error:
                            print(f"Error cancelling task: {error}", file=sys.stderr)
        except Exception as error:
            print(f"Error in stream: {error}", file=sys.stderr)

        # If the stream ended or errored, wait a bit before retrying
        time.sleep(1)


if __name__ == "__main__":
    main()

```

```rs title={"Rust (gRPC)"} startLine={248} maxLines={16}
// This Rust example is compatible with artifacts generated using
// the following grpc/rust plugin: https://buf.build/anduril/lattice-sdk/sdks/main:community/neoeinstein-tonic
mod bearer_auth;

use anduril_lattice_sdk_community_neoeinstein_prost::anduril::taskmanager::v1::{
    listen_as_agent_request, listen_as_agent_response, principal, EntityIds, ErrorCode,
    GetTaskRequest, ListenAsAgentRequest, Principal, Status, StatusUpdate, System, TaskError,
    TaskStatus, TaskVersion, UpdateStatusRequest,
};
use anduril_lattice_sdk_community_neoeinstein_tonic::anduril::taskmanager::v1::tonic::task_manager_api_client::TaskManagerApiClient;
use bearer_auth::EnvironmentTokenAuth;
use tonic::metadata::MetadataValue;
use tonic::service::interceptor::InterceptedService;
use tonic::transport::{Channel, ClientTlsConfig};
use tonic::Request;

use std::env;
use std::time::Duration;

type Interceptor = Box<dyn FnMut(Request<()>) -> Result<Request<()>, tonic::Status> + Send + Sync>;
type TaskClient = TaskManagerApiClient<InterceptedService<Channel, Interceptor>>;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let environment_token = env::var("ENVIRONMENT_TOKEN")
        .expect("ENVIRONMENT_TOKEN environment variable not set");
    let lattice_endpoint = env::var("LATTICE_ENDPOINT")
        .expect("LATTICE_ENDPOINT environment variable not set");
    let sandboxes_token = env::var("SANDBOXES_TOKEN")
        .expect("SANDBOXES_TOKEN environment variable not set");

    // When true, the agent cannot accept cancellations for an active task.
    let task_active = env::var("TASK_ACTIVE")
        .map(|v| v.eq_ignore_ascii_case("true"))
        .unwrap_or(false);

    let auth = EnvironmentTokenAuth::new(
        environment_token,
        sandboxes_token,
    );

    let tls_config = ClientTlsConfig::new().with_native_roots();
    let channel = Channel::from_shared(format!("https://{}", lattice_endpoint))?
        .tls_config(tls_config)?
        .connect()
        .await?;

    let access_token = auth.get_token().await?;
    let sandboxes_token = auth.sandboxes_token().to_string();
    let auth_header: MetadataValue<_> = format!("Bearer {}", access_token).parse()?;
    let sandbox_header: MetadataValue<_> = format!("Bearer {}", sandboxes_token).parse()?;

    let interceptor: Interceptor = Box::new(move |mut req: Request<()>| {
        req.metadata_mut()
            .insert("authorization", auth_header.clone());
        req.metadata_mut()
            .insert("anduril-sandbox-authorization", sandbox_header.clone());
        Ok(req)
    });
    let mut client: TaskClient = TaskManagerApiClient::with_interceptor(channel, interceptor);

    // Set the entity ID to listen for tasks
    let entity_id = "<AGENT_ID>".to_string();
    println!("Listening for tasks for entity {}...", entity_id);

    loop {
        let request = Request::new(ListenAsAgentRequest {
            agent_selector: Some(listen_as_agent_request::AgentSelector::EntityIds(EntityIds {
                entity_ids: vec![entity_id.clone()],
            })),
            ..Default::default()
        });

        let mut stream = match client.listen_as_agent(request).await {
            Ok(resp) => resp.into_inner(),
            Err(e) => {
                eprintln!("Error starting task listener: {}", e);
                tokio::time::sleep(Duration::from_secs(1)).await;
                continue;
            }
        };

        loop {
            let response = match stream.message().await {
                Ok(Some(r)) => r,
                Ok(None) => {
                    println!("Stream ended");
                    break;
                }
                Err(e) => {
                    eprintln!("Error receiving from stream: {}", e);
                    break;
                }
            };

            match response.request {
                Some(listen_as_agent_response::Request::ExecuteRequest(execute)) => {
                    if let Some(task) = execute.task {
                        if let Some(version) = task.version {
                            let task_id = version.task_id.clone();
                            let status_version = version.status_version;
                            println!("Starting task {}, version {}", task_id, status_version);

                            match execute_task(&mut client, &task_id, status_version, &entity_id)
                                .await
                            {
                                Ok(v) => println!(
                                    "Started task with status version: {}",
                                    v.status_version
                                ),
                                Err(e) => eprintln!("Error starting task: {}", e),
                            }
                        }
                    }
                }
                Some(listen_as_agent_response::Request::CompleteRequest(complete)) => {
                    if !complete.task_id.is_empty() {
                        println!("Completing task: {}", complete.task_id);
                        if let Err(e) =
                            complete_task(&mut client, &complete.task_id, &entity_id).await
                        {
                            eprintln!("Error completing task: {}", e);
                        }
                    }
                }
                Some(listen_as_agent_response::Request::CancelRequest(cancel)) => {
                    if !cancel.task_id.is_empty() {
                        println!("Cancelling task: {}", cancel.task_id);
                        if let Err(e) =
                            cancel_task(&mut client, &cancel.task_id, &entity_id, task_active)
                                .await
                        {
                            eprintln!("Error cancelling task: {}", e);
                        }
                    }
                }
                None => {}
            }
        }

        tokio::time::sleep(Duration::from_secs(1)).await;
    }
}

fn system_principal(entity_id: &str) -> Principal {
    Principal {
        agent: Some(principal::Agent::System(System {
            entity_id: entity_id.to_string(),
            ..Default::default()
        })),
        ..Default::default()
    }
}

// execute_task updates the task status to STATUS_EXECUTING.
async fn execute_task(
    client: &mut TaskClient,
    task_id: &str,
    task_status_version: u32,
    agent_entity_id: &str,
) -> Result<TaskVersion, tonic::Status> {
    let status_update = StatusUpdate {
        version: Some(TaskVersion {
            task_id: task_id.to_string(),
            status_version: task_status_version + 1,
            definition_version: 0,
            ..Default::default()
        }),
        status: Some(TaskStatus {
            status: Status::Executing as i32,
            ..Default::default()
        }),
        author: Some(system_principal(agent_entity_id)),
        ..Default::default()
    };

    let response = client
        .update_status(Request::new(UpdateStatusRequest {
            status_update: Some(status_update),
        }))
        .await?;

    response
        .into_inner()
        .task
        .and_then(|t| t.version)
        .ok_or_else(|| tonic::Status::internal("missing task version in response"))
}

// cancel_task handles cancellation requests from Lattice.
async fn cancel_task(
    client: &mut TaskClient,
    task_id: &str,
    entity_id: &str,
    task_active: bool,
) -> Result<(), tonic::Status> {
    // Get current task to retrieve status_version
    let task_response = client
        .get_task(Request::new(GetTaskRequest {
            task_id: task_id.to_string(),
            ..Default::default()
        }))
        .await?
        .into_inner()
        .task
        .ok_or_else(|| tonic::Status::internal("task is missing"))?;

    let current_status = task_response
        .status
        .as_ref()
        .map(|s| s.status)
        .unwrap_or(0);
    let version = task_response
        .version
        .ok_or_else(|| tonic::Status::internal("task version is missing"))?;
    let next_status_version = version.status_version + 1;

    let principal = system_principal(entity_id);

    let status_update = if task_active {
        // Reject cancellation: task is active and cannot be cancelled. Because
        // the cancellation is being rejected, we do not change the task status.
        StatusUpdate {
            version: Some(TaskVersion {
                task_id: task_id.to_string(),
                status_version: next_status_version,
                definition_version: 0,
                ..Default::default()
            }),
            status: Some(TaskStatus {
                status: current_status,
                task_error: Some(TaskError {
                    code: ErrorCode::Rejected as i32,
                    message: "Task is already active, and cannot be cancelled.".to_string(),
                    ..Default::default()
                }),
                ..Default::default()
            }),
            author: Some(principal),
            ..Default::default()
        }
    } else {
        // Accept cancellation
        StatusUpdate {
            version: Some(TaskVersion {
                task_id: task_id.to_string(),
                status_version: next_status_version,
                definition_version: 0,
                ..Default::default()
            }),
            status: Some(TaskStatus {
                status: Status::DoneNotOk as i32,
                task_error: Some(TaskError {
                    code: ErrorCode::Cancelled as i32,
                    message: "Task cancelled by agent.".to_string(),
                    ..Default::default()
                }),
                ..Default::default()
            }),
            author: Some(principal),
            ..Default::default()
        }
    };

    client
        .update_status(Request::new(UpdateStatusRequest {
            status_update: Some(status_update),
        }))
        .await?;

    if task_active {
        println!("Task could not be cancelled.");
    } else {
        println!("Task has been cancelled.");
    }
    Ok(())
}

// complete_task updates the task status to STATUS_DONE_OK.
async fn complete_task(
    client: &mut TaskClient,
    task_id: &str,
    entity_id: &str,
) -> Result<(), tonic::Status> {
    // Get current task to retrieve status_version
    let version = client
        .get_task(Request::new(GetTaskRequest {
            task_id: task_id.to_string(),
            ..Default::default()
        }))
        .await?
        .into_inner()
        .task
        .and_then(|t| t.version)
        .ok_or_else(|| tonic::Status::internal("task version is missing"))?;

    let status_update = StatusUpdate {
        version: Some(TaskVersion {
            task_id: task_id.to_string(),
            // Increment version and update to terminal state
            status_version: version.status_version + 1,
            definition_version: 0,
            ..Default::default()
        }),
        status: Some(TaskStatus {
            status: Status::DoneOk as i32,
            ..Default::default()
        }),
        author: Some(system_principal(entity_id)),
        ..Default::default()
    };

    client
        .update_status(Request::new(UpdateStatusRequest {
            status_update: Some(status_update),
        }))
        .await?;
    Ok(())
}

```

The agent must first retrieve the current task using [`GetTask`](/reference/rest/tasks/get-task) to
obtain the current [`statusVersion`](/reference/rest/tasks/get-task#response.body.version.statusVersion),
then increment it before calling [`UpdateTaskStatus`](/reference/rest/tasks/update-task-status).

## What's next

* To create, monitor, and cancel tasks as an operator, see [Operate on tasks](/guides/tasks/command-and-operate).
* To author your own task definitions, see [Define a task](/guides/tasks/define-a-task).
* See tasks in action with [the auto-reconnaissance](/samples/overview#task-an-asset)
  sample app.