Documentation
¶
Overview ¶
Package task provides task lifecycle management for long-running operations across all protocols.
This package enables tracking of task state, progress, and results regardless of the underlying protocol (MCP, A2A, ACP).
Ecosystem Position ¶
task manages long-running operations with progress tracking:
┌─────────────────────────────────────────────────────────────────┐ │ Task Management Flow │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ Protocol task Subscriber │ │ ┌─────────┐ ┌───────────┐ ┌─────────┐ │ │ │ Request │────────│ Create │─────────│ Handler │ │ │ │ │ │ │ │ │ │ │ └─────────┘ │ ┌───────┐ │ └─────────┘ │ │ │ │ │Manager│ │ │ │ │ │ │ │ │ │ │ │ │ ▼ │ └───────┘ │ │ │ │ ┌─────────┐ │ │ │ │ │ │ │ Update │────────│─────┼─────│──────────────▼ │ │ │Progress │ │ │ │ ┌─────────┐ │ │ └─────────┘ │ ▼ │ │Subscribe│ │ │ │ │ ┌───────┐ │ │ Channel │ │ │ ▼ │ │ Store │ │ └─────────┘ │ │ ┌─────────┐ │ │(memory)│ │ ▲ │ │ │Complete │────────│ └───────┘ │──────────────┘ │ │ │/Fail │ └───────────┘ notify │ │ └─────────┘ │ │ │ └─────────────────────────────────────────────────────────────────┘
State Machine ¶
Tasks follow a state machine with the following states and transitions:
┌─────────────────────────────────────┐
│ State Machine │
└─────────────────────────────────────┘
┌─────────┐ Update ┌─────────┐
│ pending │─────────────────▶│ running │
└────┬────┘ └────┬────┘
│ │
│ Cancel │ Complete/Fail/Cancel
│ │
▼ ▼
┌───────────┐ ┌───────────┐
│ cancelled │ │ complete │
└───────────┘ │ failed │
│ cancelled │
└───────────┘
(terminal)
Terminal states (complete, failed, cancelled) allow no further transitions.
Core Components ¶
- Task: Task with ID, state, progress, message, result, and timestamps
- State: Task state constants (pending, running, complete, failed, cancelled)
- Manager: Interface for task lifecycle (Create/Get/Update/Complete/Fail/Cancel)
- DefaultManager: Thread-safe Manager implementation with subscription support
- Store: Interface for task persistence
- MemoryStore: Thread-safe in-memory Store implementation
Quick Start ¶
// Create a manager
mgr := task.NewManager()
// Create a task
t, err := mgr.Create(ctx, "task-1")
if err != nil {
return err
}
// Update progress (transitions pending → running)
err = mgr.Update(ctx, "task-1", 0.5, "Processing...")
// Subscribe to updates
ch, _ := mgr.Subscribe(ctx, "task-1")
go func() {
for update := range ch {
fmt.Printf("Progress: %.0f%%\n", update.Progress*100)
}
}()
// Complete the task (closes subscription channel)
err = mgr.Complete(ctx, "task-1", result)
Subscriptions ¶
Subscribe returns a channel that receives task updates:
ch, err := mgr.Subscribe(ctx, "task-1")
if err != nil {
return err
}
for update := range ch {
// Process update
if update.State.IsTerminal() {
break
}
}
The channel is closed when:
- Task reaches terminal state (complete, failed, cancelled)
- Context is cancelled
Thread Safety ¶
All exported types are safe for concurrent use:
- DefaultManager: sync.RWMutex protects all operations
- Get/List: Uses RLock for concurrent reads
- Create/Update/Complete/Fail/Cancel: Uses Lock for exclusive access
- MemoryStore: sync.RWMutex protects all operations
- Task: Not thread-safe; use Manager methods for safe mutations
- Subscription channels: Buffered (10) for non-blocking sends
Error Handling ¶
Sentinel errors (use errors.Is for checking):
- ErrTaskNotFound: Task does not exist
- ErrTaskExists: Task with ID already exists
- ErrInvalidState: Invalid task state
- ErrInvalidTransition: Cannot transition from current state
- ErrEmptyID: Task ID is empty
The TaskError type wraps errors with task context:
err := &TaskError{
TaskID: "task-123",
Op: "update",
Err: ErrTaskNotFound,
}
// errors.Is(err, ErrTaskNotFound) = true
Configuration Options ¶
DefaultManager supports functional options:
- WithStore: Use custom Store implementation
Integration with ApertureStack ¶
task integrates with other ApertureStack packages:
- session: Tasks may be associated with client sessions
- stream: Progress updates may be streamed to clients
- wire: Task status maps to protocol-specific formats (MCP progress, A2A status)
Index ¶
- Variables
- type DefaultManager
- func (m *DefaultManager) Cancel(ctx context.Context, id string) error
- func (m *DefaultManager) Complete(ctx context.Context, id string, result any) error
- func (m *DefaultManager) Create(ctx context.Context, id string) (*Task, error)
- func (m *DefaultManager) Fail(ctx context.Context, id string, err error) error
- func (m *DefaultManager) Get(ctx context.Context, id string) (*Task, error)
- func (m *DefaultManager) List(ctx context.Context) ([]*Task, error)
- func (m *DefaultManager) Subscribe(ctx context.Context, id string) (<-chan *Task, error)
- func (m *DefaultManager) Update(ctx context.Context, id string, progress float64, message string) error
- type Manager
- type MemoryStore
- type Option
- type State
- type Store
- type Task
- type TaskError
Examples ¶
- DefaultManager.Cancel
- DefaultManager.Cancel (Terminal)
- DefaultManager.Complete
- DefaultManager.Create
- DefaultManager.Create (Duplicate)
- DefaultManager.Create (EmptyID)
- DefaultManager.Fail
- DefaultManager.Get
- DefaultManager.Get (NotFound)
- DefaultManager.List
- DefaultManager.Subscribe
- DefaultManager.Update
- DefaultManager.Update (NotFound)
- MemoryStore.Delete
- MemoryStore.Load
- MemoryStore.Load (NotFound)
- MemoryStore.LoadAll
- MemoryStore.Save
- NewManager
- NewManager (WithStore)
- NewMemoryStore
- State.IsTerminal
- State.String
- State.Valid
- Task.Clone
- TaskError
- TaskError (NoUnderlying)
- WithStore
Constants ¶
This section is empty.
Variables ¶
var ( // ErrTaskNotFound is returned when a task cannot be found. ErrTaskNotFound = errors.New("task: not found") // ErrTaskExists is returned when creating a task that already exists. ErrTaskExists = errors.New("task: already exists") // ErrInvalidState is returned for invalid task states. ErrInvalidState = errors.New("task: invalid state") // ErrInvalidTransition is returned for invalid state transitions. ErrInvalidTransition = errors.New("task: invalid transition") // ErrEmptyID is returned when a task ID is empty. ErrEmptyID = errors.New("task: empty ID") )
Sentinel errors for task operations. All errors use the "task: " prefix for consistent error identification.
Functions ¶
This section is empty.
Types ¶
type DefaultManager ¶
type DefaultManager struct {
// contains filtered or unexported fields
}
DefaultManager is the default implementation of Manager.
func NewManager ¶
func NewManager(opts ...Option) *DefaultManager
NewManager creates a new task manager with default configuration.
Example ¶
package main
import (
"context"
"fmt"
"github.com/jonwraymond/toolprotocol/task"
)
func main() {
mgr := task.NewManager()
ctx := context.Background()
t, err := mgr.Create(ctx, "task-1")
if err != nil {
fmt.Println("Error:", err)
return
}
fmt.Println("Task created:", t.ID)
fmt.Println("Initial state:", t.State)
}
Output: Task created: task-1 Initial state: pending
Example (WithStore) ¶
package main
import (
"context"
"fmt"
"github.com/jonwraymond/toolprotocol/task"
)
func main() {
// Use custom store
store := task.NewMemoryStore()
mgr := task.NewManager(task.WithStore(store))
ctx := context.Background()
t, _ := mgr.Create(ctx, "task-1")
fmt.Println("Task ID:", t.ID)
}
Output: Task ID: task-1
func (*DefaultManager) Cancel ¶
func (m *DefaultManager) Cancel(ctx context.Context, id string) error
Cancel cancels the task.
Example ¶
package main
import (
"context"
"fmt"
"github.com/jonwraymond/toolprotocol/task"
)
func main() {
mgr := task.NewManager()
ctx := context.Background()
_, _ = mgr.Create(ctx, "task-1")
// Cancel the task
err := mgr.Cancel(ctx, "task-1")
fmt.Println("Cancel error:", err)
// Check state
t, _ := mgr.Get(ctx, "task-1")
fmt.Println("State:", t.State)
fmt.Println("Is terminal:", t.State.IsTerminal())
}
Output: Cancel error: <nil> State: cancelled Is terminal: true
Example (Terminal) ¶
package main
import (
"context"
"errors"
"fmt"
"github.com/jonwraymond/toolprotocol/task"
)
func main() {
mgr := task.NewManager()
ctx := context.Background()
_, _ = mgr.Create(ctx, "task-1")
_ = mgr.Complete(ctx, "task-1", nil) // Already complete
// Try to cancel terminal task
err := mgr.Cancel(ctx, "task-1")
fmt.Println("Error is ErrInvalidTransition:", errors.Is(err, task.ErrInvalidTransition))
}
Output: Error is ErrInvalidTransition: true
func (*DefaultManager) Complete ¶
Complete marks the task as complete with the given result.
Example ¶
package main
import (
"context"
"fmt"
"github.com/jonwraymond/toolprotocol/task"
)
func main() {
mgr := task.NewManager()
ctx := context.Background()
_, _ = mgr.Create(ctx, "task-1")
_ = mgr.Update(ctx, "task-1", 0.5, "Processing")
// Complete the task
result := map[string]string{"status": "done"}
err := mgr.Complete(ctx, "task-1", result)
fmt.Println("Complete error:", err)
// Check state
t, _ := mgr.Get(ctx, "task-1")
fmt.Println("State:", t.State)
fmt.Println("Progress:", t.Progress)
fmt.Println("Is terminal:", t.State.IsTerminal())
}
Output: Complete error: <nil> State: complete Progress: 1 Is terminal: true
func (*DefaultManager) Create ¶
Create creates a new task with the given ID.
Example ¶
package main
import (
"context"
"fmt"
"github.com/jonwraymond/toolprotocol/task"
)
func main() {
mgr := task.NewManager()
ctx := context.Background()
t, err := mgr.Create(ctx, "my-task")
if err != nil {
fmt.Println("Error:", err)
return
}
fmt.Println("ID:", t.ID)
fmt.Println("State:", t.State)
fmt.Println("Progress:", t.Progress)
}
Output: ID: my-task State: pending Progress: 0
Example (Duplicate) ¶
package main
import (
"context"
"errors"
"fmt"
"github.com/jonwraymond/toolprotocol/task"
)
func main() {
mgr := task.NewManager()
ctx := context.Background()
_, _ = mgr.Create(ctx, "task-1")
_, err := mgr.Create(ctx, "task-1")
fmt.Println("Error is ErrTaskExists:", errors.Is(err, task.ErrTaskExists))
}
Output: Error is ErrTaskExists: true
Example (EmptyID) ¶
package main
import (
"context"
"errors"
"fmt"
"github.com/jonwraymond/toolprotocol/task"
)
func main() {
mgr := task.NewManager()
ctx := context.Background()
_, err := mgr.Create(ctx, "")
fmt.Println("Error is ErrEmptyID:", errors.Is(err, task.ErrEmptyID))
}
Output: Error is ErrEmptyID: true
func (*DefaultManager) Fail ¶
Fail marks the task as failed with the given error.
Example ¶
package main
import (
"context"
"errors"
"fmt"
"github.com/jonwraymond/toolprotocol/task"
)
func main() {
mgr := task.NewManager()
ctx := context.Background()
_, _ = mgr.Create(ctx, "task-1")
_ = mgr.Update(ctx, "task-1", 0.5, "Processing")
// Fail the task
err := mgr.Fail(ctx, "task-1", errors.New("something went wrong"))
fmt.Println("Fail error:", err)
// Check state
t, _ := mgr.Get(ctx, "task-1")
fmt.Println("State:", t.State)
fmt.Println("Task error:", t.Error)
fmt.Println("Is terminal:", t.State.IsTerminal())
}
Output: Fail error: <nil> State: failed Task error: something went wrong Is terminal: true
func (*DefaultManager) Get ¶
Get retrieves a task by ID.
Example ¶
package main
import (
"context"
"fmt"
"github.com/jonwraymond/toolprotocol/task"
)
func main() {
mgr := task.NewManager()
ctx := context.Background()
_, _ = mgr.Create(ctx, "task-1")
t, err := mgr.Get(ctx, "task-1")
if err != nil {
fmt.Println("Error:", err)
return
}
fmt.Println("ID:", t.ID)
fmt.Println("State:", t.State)
}
Output: ID: task-1 State: pending
Example (NotFound) ¶
package main
import (
"context"
"errors"
"fmt"
"github.com/jonwraymond/toolprotocol/task"
)
func main() {
mgr := task.NewManager()
ctx := context.Background()
_, err := mgr.Get(ctx, "nonexistent")
fmt.Println("Error is ErrTaskNotFound:", errors.Is(err, task.ErrTaskNotFound))
}
Output: Error is ErrTaskNotFound: true
func (*DefaultManager) List ¶
func (m *DefaultManager) List(ctx context.Context) ([]*Task, error)
List returns all tasks.
Example ¶
package main
import (
"context"
"fmt"
"github.com/jonwraymond/toolprotocol/task"
)
func main() {
mgr := task.NewManager()
ctx := context.Background()
_, _ = mgr.Create(ctx, "task-1")
_, _ = mgr.Create(ctx, "task-2")
_, _ = mgr.Create(ctx, "task-3")
tasks, err := mgr.List(ctx)
if err != nil {
fmt.Println("Error:", err)
return
}
fmt.Println("Task count:", len(tasks))
}
Output: Task count: 3
func (*DefaultManager) Subscribe ¶
Subscribe returns a channel that receives task updates. The channel is closed when the task reaches a terminal state.
Example ¶
package main
import (
"context"
"fmt"
"github.com/jonwraymond/toolprotocol/task"
)
func main() {
mgr := task.NewManager()
ctx := context.Background()
_, _ = mgr.Create(ctx, "task-1")
// Subscribe to updates
ch, err := mgr.Subscribe(ctx, "task-1")
if err != nil {
fmt.Println("Error:", err)
return
}
fmt.Println("Subscribed:", ch != nil)
// Complete task (sends update then closes channel)
_ = mgr.Complete(ctx, "task-1", "done")
// Drain the channel to get the final update
count := 0
for range ch {
count++
}
fmt.Println("Received updates before close:", count > 0)
}
Output: Subscribed: true Received updates before close: true
func (*DefaultManager) Update ¶
func (m *DefaultManager) Update(ctx context.Context, id string, progress float64, message string) error
Update updates task progress and message. Transitions pending → running on first update.
Example ¶
package main
import (
"context"
"fmt"
"github.com/jonwraymond/toolprotocol/task"
)
func main() {
mgr := task.NewManager()
ctx := context.Background()
_, _ = mgr.Create(ctx, "task-1")
// Update progress
err := mgr.Update(ctx, "task-1", 0.5, "Processing...")
fmt.Println("Update error:", err)
// Check state changed to running
t, _ := mgr.Get(ctx, "task-1")
fmt.Println("State after update:", t.State)
fmt.Println("Progress:", t.Progress)
fmt.Println("Message:", t.Message)
}
Output: Update error: <nil> State after update: running Progress: 0.5 Message: Processing...
Example (NotFound) ¶
package main
import (
"context"
"errors"
"fmt"
"github.com/jonwraymond/toolprotocol/task"
)
func main() {
mgr := task.NewManager()
ctx := context.Background()
err := mgr.Update(ctx, "nonexistent", 0.5, "...")
fmt.Println("Error is ErrTaskNotFound:", errors.Is(err, task.ErrTaskNotFound))
}
Output: Error is ErrTaskNotFound: true
type Manager ¶
type Manager interface {
// Create creates a new task with the given ID.
Create(ctx context.Context, id string) (*Task, error)
// Get retrieves a task by ID.
Get(ctx context.Context, id string) (*Task, error)
// List returns all tasks.
List(ctx context.Context) ([]*Task, error)
// Update updates task progress and message.
// Transitions pending → running on first update.
Update(ctx context.Context, id string, progress float64, message string) error
// Complete marks the task as complete with the given result.
Complete(ctx context.Context, id string, result any) error
// Fail marks the task as failed with the given error.
Fail(ctx context.Context, id string, err error) error
// Cancel cancels the task.
Cancel(ctx context.Context, id string) error
// Subscribe returns a channel that receives task updates.
// The channel is closed when the task reaches a terminal state.
Subscribe(ctx context.Context, id string) (<-chan *Task, error)
}
Manager manages task lifecycle.
Contract:
- Concurrency: All methods are safe for concurrent use via sync.RWMutex.
- Context: All methods accept context but do not currently block on I/O.
- State Machine: pending→running→complete|failed|cancelled
- Errors: Returns ErrTaskNotFound, ErrTaskExists, ErrInvalidTransition, ErrEmptyID. Use errors.Is for checking.
- Ownership: Returned *Task is a copy; internal state is protected.
- Subscriptions: Channels closed on terminal state or context cancellation.
type MemoryStore ¶
type MemoryStore struct {
// contains filtered or unexported fields
}
MemoryStore is an in-memory implementation of Store.
func NewMemoryStore ¶
func NewMemoryStore() *MemoryStore
NewMemoryStore creates a new in-memory task store.
Example ¶
package main
import (
"context"
"fmt"
"github.com/jonwraymond/toolprotocol/task"
)
func main() {
store := task.NewMemoryStore()
ctx := context.Background()
// Save a task
t := &task.Task{ID: "task-1", State: task.StatePending}
err := store.Save(ctx, t)
fmt.Println("Save error:", err)
// Load it back
loaded, err := store.Load(ctx, "task-1")
if err != nil {
fmt.Println("Load error:", err)
return
}
fmt.Println("Loaded ID:", loaded.ID)
}
Output: Save error: <nil> Loaded ID: task-1
func (*MemoryStore) Delete ¶
func (s *MemoryStore) Delete(ctx context.Context, id string) error
Delete removes a task by ID.
Example ¶
package main
import (
"context"
"errors"
"fmt"
"github.com/jonwraymond/toolprotocol/task"
)
func main() {
store := task.NewMemoryStore()
ctx := context.Background()
_ = store.Save(ctx, &task.Task{ID: "task-1"})
err := store.Delete(ctx, "task-1")
fmt.Println("Delete error:", err)
// Verify deleted
_, err = store.Load(ctx, "task-1")
fmt.Println("After delete, error is ErrTaskNotFound:", errors.Is(err, task.ErrTaskNotFound))
}
Output: Delete error: <nil> After delete, error is ErrTaskNotFound: true
func (*MemoryStore) Load ¶
Load retrieves a task by ID.
Example ¶
package main
import (
"context"
"fmt"
"github.com/jonwraymond/toolprotocol/task"
)
func main() {
store := task.NewMemoryStore()
ctx := context.Background()
// Save first
_ = store.Save(ctx, &task.Task{ID: "task-1", State: task.StatePending})
// Load
t, err := store.Load(ctx, "task-1")
if err != nil {
fmt.Println("Error:", err)
return
}
fmt.Println("ID:", t.ID)
fmt.Println("State:", t.State)
}
Output: ID: task-1 State: pending
Example (NotFound) ¶
package main
import (
"context"
"errors"
"fmt"
"github.com/jonwraymond/toolprotocol/task"
)
func main() {
store := task.NewMemoryStore()
ctx := context.Background()
_, err := store.Load(ctx, "nonexistent")
fmt.Println("Error is ErrTaskNotFound:", errors.Is(err, task.ErrTaskNotFound))
}
Output: Error is ErrTaskNotFound: true
func (*MemoryStore) LoadAll ¶
func (s *MemoryStore) LoadAll(ctx context.Context) ([]*Task, error)
LoadAll retrieves all tasks.
Example ¶
package main
import (
"context"
"fmt"
"github.com/jonwraymond/toolprotocol/task"
)
func main() {
store := task.NewMemoryStore()
ctx := context.Background()
_ = store.Save(ctx, &task.Task{ID: "task-1"})
_ = store.Save(ctx, &task.Task{ID: "task-2"})
tasks, err := store.LoadAll(ctx)
if err != nil {
fmt.Println("Error:", err)
return
}
fmt.Println("Task count:", len(tasks))
}
Output: Task count: 2
func (*MemoryStore) Save ¶
func (s *MemoryStore) Save(ctx context.Context, task *Task) error
Save saves or updates a task.
Example ¶
package main
import (
"context"
"fmt"
"github.com/jonwraymond/toolprotocol/task"
)
func main() {
store := task.NewMemoryStore()
ctx := context.Background()
t := &task.Task{ID: "task-1", State: task.StatePending}
err := store.Save(ctx, t)
fmt.Println("Save error:", err)
}
Output: Save error: <nil>
type Option ¶
type Option func(*DefaultManager)
Option configures a DefaultManager.
func WithStore ¶
WithStore configures the manager to use the given store.
Example ¶
package main
import (
"context"
"fmt"
"github.com/jonwraymond/toolprotocol/task"
)
func main() {
// Create custom store
store := task.NewMemoryStore()
// Use it with manager
mgr := task.NewManager(task.WithStore(store))
ctx := context.Background()
_, _ = mgr.Create(ctx, "task-1")
// Verify task is in our custom store
t, _ := store.Load(ctx, "task-1")
fmt.Println("Task in custom store:", t != nil)
}
Output: Task in custom store: true
type State ¶
type State string
State represents task execution state.
const ( // StatePending indicates the task is created but not yet started. StatePending State = "pending" // StateRunning indicates the task is actively executing. StateRunning State = "running" // StateComplete indicates the task finished successfully. StateComplete State = "complete" // StateFailed indicates the task finished with an error. StateFailed State = "failed" // StateCancelled indicates the task was cancelled. StateCancelled State = "cancelled" )
func (State) IsTerminal ¶
IsTerminal returns true if the state is a terminal state (complete, failed, or cancelled) that allows no further transitions.
Example ¶
package main
import (
"fmt"
"github.com/jonwraymond/toolprotocol/task"
)
func main() {
fmt.Println("pending terminal:", task.StatePending.IsTerminal())
fmt.Println("running terminal:", task.StateRunning.IsTerminal())
fmt.Println("complete terminal:", task.StateComplete.IsTerminal())
fmt.Println("failed terminal:", task.StateFailed.IsTerminal())
fmt.Println("cancelled terminal:", task.StateCancelled.IsTerminal())
}
Output: pending terminal: false running terminal: false complete terminal: true failed terminal: true cancelled terminal: true
func (State) String ¶
String returns the string representation of the state.
Example ¶
package main
import (
"fmt"
"github.com/jonwraymond/toolprotocol/task"
)
func main() {
states := []task.State{
task.StatePending,
task.StateRunning,
task.StateComplete,
task.StateFailed,
task.StateCancelled,
}
for _, s := range states {
fmt.Println(s.String())
}
}
Output: pending running complete failed cancelled
func (State) Valid ¶
Valid returns true if the state is a known valid state.
Example ¶
package main
import (
"fmt"
"github.com/jonwraymond/toolprotocol/task"
)
func main() {
fmt.Println("pending valid:", task.StatePending.Valid())
fmt.Println("running valid:", task.StateRunning.Valid())
fmt.Println("invalid valid:", task.State("unknown").Valid())
}
Output: pending valid: true running valid: true invalid valid: false
type Store ¶
type Store interface {
// Save saves or updates a task.
Save(ctx context.Context, task *Task) error
// Load retrieves a task by ID.
Load(ctx context.Context, id string) (*Task, error)
// LoadAll retrieves all tasks.
LoadAll(ctx context.Context) ([]*Task, error)
// Delete removes a task by ID.
Delete(ctx context.Context, id string) error
}
Store provides task persistence.
Contract:
- Concurrency: Implementations must be safe for concurrent use.
- Ownership: Save stores a clone; Load returns a clone.
- Errors: Returns ErrTaskNotFound for missing tasks.
type Task ¶
type Task struct {
// ID is the unique identifier for the task.
ID string
// State is the current execution state.
State State
// Progress is the completion percentage (0.0 to 1.0).
Progress float64
// Message is the current status message.
Message string
// Result is the final result (set when complete).
Result any
// Error is the error (set when failed).
Error error
// CreatedAt is when the task was created.
CreatedAt time.Time
// UpdatedAt is when the task was last updated.
UpdatedAt time.Time
// CompletedAt is when the task reached a terminal state.
CompletedAt *time.Time
}
Task represents a long-running operation.
func (*Task) Clone ¶
Clone returns a deep copy of the task.
Example ¶
package main
import (
"fmt"
"github.com/jonwraymond/toolprotocol/task"
)
func main() {
t := &task.Task{
ID: "task-1",
State: task.StateRunning,
Progress: 0.5,
Message: "Processing",
}
clone := t.Clone()
fmt.Println("Same ID:", clone.ID == t.ID)
fmt.Println("Same State:", clone.State == t.State)
fmt.Println("Same Progress:", clone.Progress == t.Progress)
}
Output: Same ID: true Same State: true Same Progress: true
type TaskError ¶
type TaskError struct {
// TaskID is the ID of the task that caused the error.
TaskID string
// Op is the operation that failed.
Op string
// Err is the underlying error.
Err error
}
TaskError wraps an error with task context.
Example ¶
package main
import (
"errors"
"fmt"
"github.com/jonwraymond/toolprotocol/task"
)
func main() {
err := &task.TaskError{
TaskID: "task-123",
Op: "update",
Err: task.ErrTaskNotFound,
}
fmt.Println(err.Error())
fmt.Println("Unwraps to ErrTaskNotFound:", errors.Is(err, task.ErrTaskNotFound))
}
Output: task task-123: update: task: not found Unwraps to ErrTaskNotFound: true
Example (NoUnderlying) ¶
package main
import (
"fmt"
"github.com/jonwraymond/toolprotocol/task"
)
func main() {
err := &task.TaskError{
TaskID: "task-123",
Op: "validate",
}
fmt.Println(err.Error())
}
Output: task task-123: validate