SDK Reference Guide

This comprehensive guide covers the Maitento .NET SDK, providing everything you need to programmatically interact with the Maitento platform. The SDK offers an async-first API for managing AI agents, applications, interactions, capsules, virtual file systems, and real-time notifications.


Table of Contents

  1. SDK Overview
  2. Getting Started
  3. Services Summary
  4. Real-Time Notifications
  5. Virtual File System Operations
  6. File and Partition Operations
  7. Complete Code Examples
  8. Error Handling

1. SDK Overview

Architecture

The Maitento SDK is built around a central client class (MaitentoClient) that provides access to all platform services through dedicated service interfaces. Each service handles a specific domain of functionality.

IMaitentoClient
    |
    +-- IAiModelService          (AI models)
    +-- IAgentService            (AI agents)
    +-- IAppService              (Applications)
    +-- IInteractionService      (Multi-agent workflows)
    +-- IInteractionProcessService (Interaction execution)
    +-- ICapsuleService          (Container definitions)
    +-- ICapsuleProcessService   (Container instances)
    +-- IVirtualFileSystemService (VFS operations)
    +-- IFileService             (File management)
    +-- IPartitionService        (Storage partitions)
    +-- INotificationClient      (Real-time WebSocket)
    +-- ISecretService           (Secret management)
    +-- IConnectorService        (External API connectors)
    +-- IAutomatedActionService  (Scheduled automation)
    +-- ... (additional services)

Key Features

  • Async-First API: All operations are asynchronous with proper cancellation token support
  • Multiple Authentication Methods: API key, JWT credentials, or pre-existing tokens
  • Real-Time Notifications: WebSocket-based notifications with automatic reconnection
  • Comprehensive VFS Operations: 60+ methods for file system manipulation
  • Semantic Search: Built-in embeddings generation and similarity search
  • Dependency Injection Support: Seamless ASP.NET Core integration
  • Automatic Token Refresh: JWT tokens are refreshed automatically before expiration
  • Configurable Retry Policies: Built-in retry logic for transient failures

Requirements

  • .NET 8.0 or later
  • NuGet Package: Maitento.Sdk

2. Getting Started

Installation

Install the SDK via NuGet:

dotnet add package Maitento.Sdk

Authentication Methods

The SDK supports three authentication methods:

var client = MaitentoClient.CreateWithApiKey(
    "your-client-id",
    "your-client-secret"
);

Username/Password Authentication

var client = MaitentoClient.CreateWithCredentials(
    "user@example.com",
    "your-password"
);

Pre-Existing JWT Token

var client = MaitentoClient.CreateWithJwtToken(
    "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
);

Client Initialization

Using Factory Methods (Simple)

using Maitento.Sdk;

// API key authentication
await using var client = MaitentoClient.CreateWithApiKey(
    "your-client-id",
    "your-client-secret"
);

// Make API calls
var namespaces = await client.Namespaces.GetAllAsync();

Using Constructor (Advanced)

using Maitento.Sdk;

var options = new MaitentoClientOptions
{
    BaseUrl = "https://api.maitento.com",
    ApiKeyClientId = "your-client-id",
    ApiKeyClientSecret = "your-client-secret",
    Timeout = TimeSpan.FromMinutes(2),
    RetryPolicyMaxAttempts = 3,
    RetryPolicyDelay = TimeSpan.FromSeconds(2)
};

using var httpClient = new HttpClient();
using var loggerFactory = LoggerFactory.Create(builder =>
{
    builder.AddConsole().SetMinimumLevel(LogLevel.Debug);
});

await using var client = new MaitentoClient(options, httpClient, loggerFactory);

Dependency Injection

For ASP.NET Core applications, use the built-in DI extension:

using Maitento.Sdk.Extensions;

public void ConfigureServices(IServiceCollection services)
{
    services.AddMaitentoClient(options =>
    {
        options.BaseUrl = "https://api.maitento.com";
        options.ApiKeyClientId = Configuration["Maitento:ClientId"];
        options.ApiKeyClientSecret = Configuration["Maitento:ClientSecret"];
        options.Timeout = TimeSpan.FromMinutes(2);
        options.RetryPolicyMaxAttempts = 3;
    });
}

// Inject into your services
public class MyService
{
    private readonly IMaitentoClient _client;

    public MyService(IMaitentoClient maitentoClient)
    {
        _client = maitentoClient;
    }

    public async Task DoWorkAsync()
    {
        var namespaces = await _client.Namespaces.GetAllAsync();
        // ...
    }
}

Configuration Options

The MaitentoClientOptions class provides comprehensive configuration:

PropertyTypeDefaultDescription
BaseUrlstringhttps://api.maitento.comAPI endpoint URL
ApiKeyClientIdstring?nullAPI key client identifier
ApiKeyClientSecretstring?nullAPI key client secret
Usernamestring?nullEmail for JWT auth
Passwordstring?nullPassword for JWT auth
JwtTokenstring?nullPre-existing JWT token
TimeoutTimeSpan5 minutesHTTP request timeout
RetryPolicyMaxAttemptsint1Maximum retry attempts
RetryPolicyDelayTimeSpan1 secondDelay between retries
NotificationReconnectTimeoutTimeSpan30 secondsWebSocket reconnect timeout
NotificationErrorReconnectTimeoutTimeSpan2 secondsReconnect timeout after error
NotificationServerPingTimeoutTimeSpan30 secondsServer ping timeout

3. Services Summary

Core Services

INamespaceService

Organize resources into logical namespaces.

Task<Namespace[]> GetAllAsync(CancellationToken cancellationToken = default);
Task<Namespace?> GetByIdAsync(Guid id, CancellationToken cancellationToken = default);
Task<Namespace?> GetByNameAsync(string name, CancellationToken cancellationToken = default);
Task<Namespace> CreateAsync(string name, CancellationToken cancellationToken = default);
Task<Namespace> UpdateAsync(Guid id, string name, CancellationToken cancellationToken = default);

IAiModelService

Access available AI models.

Task<AiModel[]> GetAllAsync(CancellationToken cancellationToken = default);

IApiKeyService

Manage API keys for authentication.

Task<UserApiKey[]> GetAllAsync(CancellationToken cancellationToken = default);
Task<UserApiCreateResponse> CreateAsync(string name, DateTime? dateExpires = null, CancellationToken cancellationToken = default);
Task DisableAsync(Guid id, CancellationToken cancellationToken = default);

ISecretService

Secure secret storage.

Task<Secret[]> GetAllAsync(CancellationToken cancellationToken = default);
Task<Secret?> GetByIdAsync(Guid id, CancellationToken cancellationToken = default);
Task<Secret?> GetByNameAsync(string name, CancellationToken cancellationToken = default);
Task<string> GetValueByNameAsync(string name, CancellationToken cancellationToken = default);
Task<Secret> CreateAsync(string name, string value, CancellationToken cancellationToken = default);
Task<Secret> UpdateAsync(Guid id, string name, string? value = null, CancellationToken cancellationToken = default);
Task DeleteAsync(Guid id, CancellationToken cancellationToken = default);

Agent and Interaction Services

IAgentService

Create and manage AI agents.

Task<Agent[]> GetAllAsync(CancellationToken cancellationToken = default);
Task<Agent[]> GetAllInNamespaceAsync(string namespaceName, CancellationToken cancellationToken = default);
Task<Agent?> GetByIdAsync(Guid id, CancellationToken cancellationToken = default);
Task<Agent?> GetByNameAsync(string namespaceName, string name, CancellationToken cancellationToken = default);
Task<Agent> CreateAsync(string namespaceName, string name, Guid aiModelId, string systemPrompt, string summaryForOtherAgents, CancellationToken cancellationToken = default);
Task<Agent> UpdateAsync(Guid id, string name, Guid aiModelId, string systemPrompt, string summaryForOtherAgents, CancellationToken cancellationToken = default);

IInteractionService

Create multi-agent interaction workflows. Supports four types:

  • RoundRobin: Agents take turns in a round-robin fashion
  • OneShot: Single agent responds to a query
  • Managed: Orchestrated by a manager agent
  • Routed: Messages routed to specific agents
// Retrieval
Task<Interaction[]> GetAllAsync(CancellationToken cancellationToken = default);
Task<Interaction[]> GetAllInNamespaceAsync(string namespaceName, CancellationToken cancellationToken = default);
Task<Interaction?> GetByIdAsync(Guid id, CancellationToken cancellationToken = default);
Task<Interaction?> GetByNameAsync(string namespaceName, string name, CancellationToken cancellationToken = default);

// Round Robin
Task<Interaction> CreateRoundRobinAsync(...);
Task<Interaction> UpdateRoundRobinAsync(...);

// One Shot
Task<Interaction> CreateOneShotAsync(...);
Task<Interaction> UpdateOneShotAsync(...);

// Managed
Task<Interaction> CreateManagedAsync(...);
Task<Interaction> UpdateManagedAsync(...);

// Routed
Task<Interaction> CreateRoutedAsync(...);
Task<Interaction> UpdateRoutedAsync(...);

IInteractionProcessService

Execute and participate in interactions.

Task<InteractionProcess[]> GetAllAsync(CancellationToken cancellationToken = default);
Task<InteractionProcess?> GetInstanceAsync(Guid id, CancellationToken cancellationToken = default);
Task<InteractionProcess> StartAsync(string namespaceName, string name, NameValuePair[] inputs, CancellationToken cancellationToken = default);
Task<InteractionProcess[]> GetAliveInNamespaceAsync(string namespaceName, CancellationToken cancellationToken = default);
Task AddMessageAsync(Guid id, string authorName, Guid authorId, string message, CancellationToken cancellationToken = default);
Task VoteYesAsync(Guid id, string authorName, Guid authorId, CancellationToken cancellationToken = default);
Task VoteNoAsync(Guid id, string authorName, Guid authorId, string reason, CancellationToken cancellationToken = default);

Process Services

IAppService

Manage applications and execution.

Task<App[]> GetAllAsync(CancellationToken cancellationToken = default);
Task<App[]> GetAllInNamespaceAsync(string namespaceName, CancellationToken cancellationToken = default);
Task<App?> GetByIdAsync(Guid id, CancellationToken cancellationToken = default);
Task<App?> GetByNameAsync(string namespaceName, string name, CancellationToken cancellationToken = default);
Task<App> CreateAsync(string namespaceName, string name, CancellationToken cancellationToken = default);
Task<AppProcess?> GetProcessAsync(Guid processId, CancellationToken cancellationToken = default);
Task<AppProcess> StartAsync(Guid appId, string version, NameValuePair[] inputs, CancellationToken cancellationToken = default);
Task ProcessPatchInputs(Guid id, NameValuePair[] inputs, CancellationToken cancellationToken = default);
Task<AppProcess[]> GetAliveInNamespaceAsync(string namespaceName, CancellationToken cancellationToken = default);

ICapsuleProcessService

Manage containerized environment instances.

Task<CapsuleProcess[]> GetAllAsync(CancellationToken cancellationToken = default);
Task<CapsuleProcess[]> GetAliveInNamespaceAsync(string namespaceName, CancellationToken cancellationToken = default);
Task<CapsuleProcess?> GetByIdAsync(Guid id, CancellationToken cancellationToken = default);
Task<CapsuleProcess> StartAsync(...);
Task TerminateAsync(Guid id, CancellationToken cancellationToken = default);
Task<RunCommandResponse> RunCommandAsync(Guid processId, RunCommandRequest request, CancellationToken cancellationToken = default);

// VFS mounting
Task MountVfsAsync(Guid processId, Guid vfsId, string path, CancellationToken cancellationToken = default);
Task UnmountVfsAsync(Guid processId, Guid vfsId, CancellationToken cancellationToken = default);

// Secret mounting
Task MountSecretAsync(Guid processId, Guid secretId, string path, CancellationToken cancellationToken = default);
Task UnmountSecretAsync(Guid processId, Guid secretId, CancellationToken cancellationToken = default);

// User and package management
Task AddUserAsync(Guid processId, string username, CancellationToken cancellationToken = default);
Task RemoveUserAsync(Guid processId, string username, CancellationToken cancellationToken = default);
Task InstallPackageAsync(Guid processId, string packageName, CancellationToken cancellationToken = default);
Task RemovePackageAsync(Guid processId, string packageName, CancellationToken cancellationToken = default);

ICodeGenerationProcessService

AI-powered code generation.

Task<CodeGenerationProcess> StartAsync(...);
Task<CodeGenerationProcess[]> GetAllAsync(CancellationToken cancellationToken = default);
Task<CodeGenerationProcess[]> GetAlive(CancellationToken cancellationToken = default);
Task<CodeGenerationProcess?> GetByIdAsync(Guid id, CancellationToken cancellationToken = default);
Task UpdateCredentialsAsync(Guid id, string username, string password, CancellationToken cancellationToken = default);
Task UpdateClaudeAuthentication(Guid id, string apiKey, CancellationToken cancellationToken = default);

IProcessService

Generic process management.

Task KillAsync(Guid processId, ProcessType processType, CancellationToken cancellationToken = default);

Infrastructure Services

IAutomatedActionService

Configure scheduled tasks and message-triggered automations.

// CRUD
Task<AutomatedAction[]> GetAllAsync(CancellationToken cancellationToken = default);
Task<AutomatedAction[]> GetAllInNamespaceAsync(string namespaceName, CancellationToken cancellationToken = default);
Task<AutomatedAction?> GetByIdAsync(Guid id, CancellationToken cancellationToken = default);
Task<AutomatedAction?> GetByNameAsync(string namespaceName, string name, CancellationToken cancellationToken = default);
Task<AutomatedAction> CreateAsync(string namespaceName, string name, string? description, AutomatedActionItem[] actions, CancellationToken cancellationToken = default);
Task<AutomatedAction> UpdateAsync(Guid id, string name, string? description, AutomatedActionItem[] actions, CancellationToken cancellationToken = default);
Task DeleteAsync(Guid id, CancellationToken cancellationToken = default);

// Schedule management
Task<AutomatedAction> AddScheduleAsync(Guid id, CronSchedule schedule, CancellationToken cancellationToken = default);
Task<AutomatedAction> UpdateScheduleAsync(Guid id, Guid scheduleId, CronSchedule schedule, CancellationToken cancellationToken = default);
Task<AutomatedAction> RemoveScheduleAsync(Guid id, Guid scheduleId, CancellationToken cancellationToken = default);
Task<AutomatedAction> EnableScheduleAsync(Guid id, Guid scheduleId, CancellationToken cancellationToken = default);
Task<AutomatedAction> DisableScheduleAsync(Guid id, Guid scheduleId, CancellationToken cancellationToken = default);

// Message trigger management
Task<AutomatedAction> AddMessageTriggerAsync(Guid id, MessageTrigger trigger, CancellationToken cancellationToken = default);
Task<AutomatedAction> UpdateMessageTriggerAsync(Guid id, Guid triggerId, MessageTrigger trigger, CancellationToken cancellationToken = default);
Task<AutomatedAction> RemoveMessageTriggerAsync(Guid id, Guid triggerId, CancellationToken cancellationToken = default);
Task<AutomatedAction> EnableMessageTriggerAsync(Guid id, Guid triggerId, CancellationToken cancellationToken = default);
Task<AutomatedAction> DisableMessageTriggerAsync(Guid id, Guid triggerId, CancellationToken cancellationToken = default);

// Execution
Task<Guid> TriggerAsync(Guid id, CancellationToken cancellationToken = default);
Task<ExecutionHistoryEntry[]> GetHistoryAsync(Guid id, int limit = 50, CancellationToken cancellationToken = default);

IConnectorService

Manage external API integrations.

Task<Connector[]> GetAllAsync(CancellationToken cancellationToken = default);
Task<Connector[]> GetAllInNamespaceAsync(string namespaceName, CancellationToken cancellationToken = default);
Task<Connector?> GetByIdAsync(Guid id, CancellationToken cancellationToken = default);
Task<Connector?> GetByNameAsync(string namespaceName, string name, CancellationToken cancellationToken = default);
Task<McpConnector> CreateMcpAsync(string namespaceName, bool? destructiveHint, bool? idempotentHint, bool? openWorldHint, bool? readOnlyHint, string name, string url, string description, string? requestSchemaJson, string? responseSchemaJson, ConnectorAuthenticationType authenticationType, bool postProcessResponseUnicodeCharacters, CancellationToken cancellationToken = default);
Task<OpenApiConnector> CreateOpenApiAsync(string namespaceName, HttpMethod httpMethod, OpenApiParameter[] pathParameters, OpenApiParameter[] queryParameters, string name, string url, string description, string? requestSchemaJson, string? responseSchemaJson, ConnectorAuthenticationType authenticationType, bool postProcessResponseUnicodeCharacters, CancellationToken cancellationToken = default);
Task DeleteByIdAsync(Guid id, CancellationToken cancellationToken = default);

IWebHookService

Configure webhook notifications.

Task<WebHookRegistration[]> GetAllAsync(CancellationToken cancellationToken = default);
Task<WebHookRegistration?> GetByIdAsync(Guid id, CancellationToken cancellationToken = default);
Task<WebHookRegistration> CreateAsync(WebHookType type, string url, string secret, Dictionary<string, string> headers, CancellationToken cancellationToken = default);
Task<WebHookRegistration> UpdateAsync(Guid id, string url, string? secret, Dictionary<string, string> headers, CancellationToken cancellationToken = default);
Task DeleteAsync(Guid id, CancellationToken cancellationToken = default);

4. Real-Time Notifications

The SDK provides a WebSocket-based notification system for receiving real-time updates from the platform.

WebSocket Connection

Starting the Connection

// Start the notification client
await client.Notifications.StartAsync();

// Check connection status
if (client.Notifications.IsConnected)
{
    Console.WriteLine("Connected to notifications");
}

Stopping the Connection

await client.Notifications.StopAsync();

Subscribing to Events

// Subscribe to all app process updates
var subscription = client.Notifications.Subscribe(myHandler);

// Subscribe to a specific process only
var filteredSubscription = client.Notifications.Subscribe(
    myHandler,
    filterId: specificProcessId
);

// Unsubscribe when done
client.Notifications.Unsubscribe(subscription);

Handler Interfaces

Implement these interfaces to receive specific notification types:

INotificationClientHandlerAppProcessUpdated

public class AppProcessHandler : INotificationClientHandlerAppProcessUpdated
{
    public Task OnAppProcessUpdated(AppProcess process)
    {
        Console.WriteLine($"App process {process.Id}: {process.Status}");
        return Task.CompletedTask;
    }
}

INotificationClientHandlerCodeGenerationProcessUpdated

public class CodeGenHandler : INotificationClientHandlerCodeGenerationProcessUpdated
{
    public Task OnCodeGenerationProcessUpdated(CodeGenerationProcess process)
    {
        Console.WriteLine($"Code generation {process.Id}: {process.Status}");
        return Task.CompletedTask;
    }
}

INotificationClientHandlerInteractionProcessUpdated

public class InteractionHandler : INotificationClientHandlerInteractionProcessUpdated
{
    public Task OnInteractionProcessUpdated(InteractionProcess process)
    {
        Console.WriteLine($"Interaction {process.Id}: {process.Status}");
        return Task.CompletedTask;
    }
}

INotificationClientHandlerCapsuleProcessUpdated

public class CapsuleHandler : INotificationClientHandlerCapsuleProcessUpdated
{
    public Task OnCapsuleProcessUpdated(CapsuleProcess process)
    {
        Console.WriteLine($"Capsule process {process.Id}: {process.Status}");
        return Task.CompletedTask;
    }
}

INotificationClientHandlerIngestedFileUpdated

public class FileHandler : INotificationClientHandlerIngestedFileUpdated
{
    public Task OnIngestedFileUpdated(IngestedFile file)
    {
        Console.WriteLine($"File {file.Name}: {file.Status}");
        return Task.CompletedTask;
    }
}

INotificationClientHandlerInteractionStream

public class StreamHandler : INotificationClientHandlerInteractionStream
{
    private readonly StringBuilder _buffer = new();

    public Task OnInteractionStream(InteractionStreamUserNotification notification)
    {
        _buffer.Append(notification.Stream);
        Console.Write(notification.Stream); // Real-time output
        return Task.CompletedTask;
    }
}

Connection Management

Connection Events

client.Notifications.Connected += (sender, args) =>
{
    Console.WriteLine("Connected to notification server");
};

client.Notifications.Disconnected += (sender, args) =>
{
    Console.WriteLine($"Disconnected: {args.DisconnectionType}");
    if (args.Exception != null)
    {
        Console.WriteLine($"Error: {args.Exception.Message}");
    }
    Console.WriteLine("Automatic reconnection will be attempted...");
};

Reconnection Behavior

The notification client includes automatic reconnection logic:

  1. When disconnection occurs, the Disconnected event fires
  2. Automatic reconnection attempts begin using configured timeouts
  3. On success, the Connected event fires
  4. All subscriptions remain active (handlers are preserved)

5. Virtual File System Operations

The VFS service provides comprehensive file system operations for virtual file systems that are isolated and namespace-scoped.

VFS Management

Creating and Retrieving VFS

// Get all VFS
VirtualFileSystem[] allVfs = await client.VirtualFileSystems.GetAllAsync();

// Get VFS in a namespace
VirtualFileSystem[] namespaceVfs = await client.VirtualFileSystems
    .GetAllInNamespaceAsync("production");

// Get by ID or name
VirtualFileSystem? vfs = await client.VirtualFileSystems.GetByIdAsync(vfsId);
VirtualFileSystem? vfs = await client.VirtualFileSystems
    .GetByNameAsync("production", "app-storage");

// Create a new VFS
VirtualFileSystem newVfs = await client.VirtualFileSystems
    .CreateAsync("development", "test-storage");

Destroying VFS

// By ID (irreversible)
await client.VirtualFileSystems.DestroyByIdAsync(vfsId);

// By name (irreversible)
await client.VirtualFileSystems.DestroyByNameAsync("development", "test-storage");

File Operations

All file operations have both ById and ByName variants.

Writing Files

// Write text
FileSystemFile file = await client.VirtualFileSystems
    .WriteTextById(vfsId, "/config/settings.json", jsonContent);

// Write lines
string[] lines = new[] { "line1", "line2", "line3" };
FileSystemFile file = await client.VirtualFileSystems
    .WriteLinesById(vfsId, "/logs/app.log", lines);

// Write binary
byte[] imageData = await File.ReadAllBytesAsync("logo.png");
FileSystemFile file = await client.VirtualFileSystems
    .WriteBytesById(vfsId, "/assets/logo.png", imageData);

Reading Files

// Read text
string content = await client.VirtualFileSystems
    .ReadTextById(vfsId, "/config/settings.json");

// Read lines
string[] lines = await client.VirtualFileSystems
    .ReadLinesById(vfsId, "/logs/app.log");

// Read binary
FileContent imageContent = await client.VirtualFileSystems
    .ReadBytesById(vfsId, "/assets/logo.png");
await File.WriteAllBytesAsync("downloaded.png", imageContent.Content);

Appending to Files

// Append text
await client.VirtualFileSystems
    .AppendTextById(vfsId, "/logs/app.log", "\nNew log entry");

// Append lines
string[] newLines = new[] { "entry1", "entry2" };
await client.VirtualFileSystems
    .AppendLinesById(vfsId, "/logs/app.log", newLines);

File Management

// Check if file exists
bool exists = await client.VirtualFileSystems
    .ExistsFileById(vfsId, "/config/settings.json");

// Delete file
await client.VirtualFileSystems
    .DeleteFileById(vfsId, "/temp/cache.dat");

// Copy file
FileSystemEntry copied = await client.VirtualFileSystems
    .CopyById(vfsId, "/config/settings.json", "/config/settings.backup.json");

// Move/rename file
FileSystemEntry moved = await client.VirtualFileSystems
    .MoveById(vfsId, "/docs/draft.md", "/docs/final.md");

Directory Operations

// Create directory
FileSystemDirectory dir = await client.VirtualFileSystems
    .CreateDirectoryById(vfsId, "/data/exports/2024");

// Check if directory exists
bool exists = await client.VirtualFileSystems
    .ExistsDirectoryById(vfsId, "/config");

// List directory contents
FileSystemEntry[] entries = await client.VirtualFileSystems
    .ListById(vfsId, "/");

foreach (var entry in entries)
{
    if (entry is FileSystemFile file)
        Console.WriteLine($"File: {file.Name} ({file.Size} bytes)");
    else if (entry is FileSystemDirectory dir)
        Console.WriteLine($"Directory: {dir.Name}/");
}

// Delete directory
await client.VirtualFileSystems
    .DeleteDirectoryById(vfsId, "/temp", recursive: true);

Advanced Operations

Find (Search with Glob and Grep)

// Find all C# files
var findRequest = new FindRequest
{
    Path = "/src",
    Pattern = "**/*.cs",
    MaxResults = 50
};
FindResponse results = await client.VirtualFileSystems.FindById(vfsId, findRequest);

// Find files containing "TODO"
var grepRequest = new FindRequest
{
    Path = "/src",
    Pattern = "**/*.cs",
    Grep = "TODO",
    IgnoreCase = true,
    ContextLines = 2,
    MaxResults = 100
};
FindResponse todos = await client.VirtualFileSystems.FindById(vfsId, grepRequest);

foreach (var result in todos.Results)
{
    Console.WriteLine($"\n{result.Path}:");
    foreach (var match in result.GrepMatches ?? Array.Empty<GrepMatch>())
    {
        Console.WriteLine($"  Line {match.Line}: {match.Content}");
    }
}

Edit (Programmatic File Modifications)

// Find and replace
var editRequest = new EditRequest
{
    Path = "/config/settings.json",
    Operations = new[]
    {
        new EditOperation
        {
            Type = "replace",
            Find = "\"debug\": true",
            Replace = "\"debug\": false"
        }
    }
};
EditResponse result = await client.VirtualFileSystems.EditById(vfsId, editRequest);

// Replace all occurrences
var replaceAllRequest = new EditRequest
{
    Path = "/src/module.cs",
    Operations = new[]
    {
        new EditOperation
        {
            Type = "replace",
            Find = "OldClassName",
            Replace = "NewClassName",
            ReplaceAll = true
        }
    }
};
await client.VirtualFileSystems.EditById(vfsId, replaceAllRequest);

// Insert after line
var insertRequest = new EditRequest
{
    Path = "/src/file.cs",
    Operations = new[]
    {
        new EditOperation
        {
            Type = "insertAfter",
            InsertAfterLine = 10,
            Content = "    // New code inserted\n    DoSomething();"
        }
    }
};
await client.VirtualFileSystems.EditById(vfsId, insertRequest);

Batch Operations

// Batch read multiple files
var batchReadRequest = new BatchReadRequest
{
    Paths = new[] { "/config/settings.json", "/config/database.yaml", "/.env" },
    MaxLinesPerFile = 500
};
BatchReadResponse readResult = await client.VirtualFileSystems.BatchReadById(vfsId, batchReadRequest);

// Atomic batch edit across multiple files
var batchEditRequest = new BatchEditRequest
{
    Atomic = true, // All-or-nothing
    Edits = new[]
    {
        new BatchEditFileRequest
        {
            Path = "/src/UserService.cs",
            Operations = new[]
            {
                new EditOperation { Type = "replace", Find = "IUserRepository", Replace = "IUserDataStore", ReplaceAll = true }
            }
        },
        new BatchEditFileRequest
        {
            Path = "/src/IUserRepository.cs",
            Operations = new[]
            {
                new EditOperation { Type = "replace", Find = "interface IUserRepository", Replace = "interface IUserDataStore" }
            }
        }
    }
};
BatchEditResponse editResult = await client.VirtualFileSystems.BatchEditById(vfsId, batchEditRequest);

Tree (Directory Structure)

var treeRequest = new TreeRequest
{
    Path = "/",
    MaxDepth = -1, // Unlimited
    IncludeFiles = true,
    IncludeHidden = false
};
TreeResponse tree = await client.VirtualFileSystems.TreeById(vfsId, treeRequest);

Console.WriteLine($"Total files: {tree.FileCount}");
Console.WriteLine($"Total directories: {tree.DirectoryCount}");

// Print tree structure
void PrintNode(TreeNode node, int indent = 0)
{
    string prefix = new string(' ', indent * 2);
    string icon = node.Type == "directory" ? "[D]" : "[F]";
    Console.WriteLine($"{prefix}{icon} {node.Name}");
    foreach (var child in node.Children)
        PrintNode(child, indent + 1);
}

if (tree.Root != null)
    PrintNode(tree.Root);

6. File and Partition Operations

Partition Management

Partitions are storage containers for organizing files and enabling scoped semantic search.

// Get all partitions
Partition[] partitions = await client.Partitions.GetAllAsync();

// Get by ID
Partition? partition = await client.Partitions.GetByIdAsync(partitionId);

// Create partition
Partition newPartition = await client.Partitions.CreateAsync("documents");

// Delete partition
await client.Partitions.DeleteAsync(partitionId);

File Ingestion

// Read file from disk
byte[] fileBytes = await File.ReadAllBytesAsync("document.pdf");

// Check for duplicates using checksum
string checksum = ComputeSha256(fileBytes);
IngestedFileSummary? existing = await client.Files.GetByChecksumAsync(checksum);

if (existing == null)
{
    // Ingest the file
    IngestedFile file = await client.Files.IngestFileAsync(
        partitionId,
        fileBytes,
        "document.pdf",
        "application/pdf"
    );

    Console.WriteLine($"Ingested: {file.Id}");
    Console.WriteLine($"Vectors Ready: {file.IsVectorGenerated}");
}

File Operations

// Get file details
IngestedFile? details = await client.Files.GetDetailsAsync(fileId);

// Get extracted text
string? text = await client.Files.GetTextAsync(fileId);

// Download raw content
FileContent? content = await client.Files.GetRawContentAsync(fileId);
if (content != null)
{
    await File.WriteAllBytesAsync(content.FileName, content.Content);
}

// Delete file
await client.Files.DeleteAsync(fileId);
// Generate embeddings for text
string text = "This is a sample document about machine learning.";
float[] embedding = await client.Files.GenerateEmbeddingsAsync(text);
Console.WriteLine($"Generated {embedding.Length} dimensions");

// Split text into chunks (for large documents)
string longDocument = await File.ReadAllTextAsync("long-document.txt");
string[] chunks = await client.Files.GenerateChunksAsync(longDocument);
Console.WriteLine($"Split into {chunks.Length} chunks");

// Semantic search
string query = "What are the quarterly sales figures?";
EmbeddingsSearchResult[] results = await client.Files.SearchEmbeddingsAsync(partitionId, query);

foreach (var result in results)
{
    Console.WriteLine($"Score: {result.Score:F4} - {result.Name}");
    Console.WriteLine($"  Matched: {result.Text.Substring(0, Math.Min(200, result.Text.Length))}...");
}

7. Complete Code Examples

Basic Client Usage

using Maitento.Sdk;

public async Task BasicUsageExample()
{
    // Create client
    await using var client = MaitentoClient.CreateWithApiKey(
        "your-client-id",
        "your-client-secret"
    );

    // List namespaces
    var namespaces = await client.Namespaces.GetAllAsync();
    foreach (var ns in namespaces)
    {
        Console.WriteLine($"Namespace: {ns.Name} (ID: {ns.Id})");
    }

    // Get available AI models
    var models = await client.AiModels.GetAllAsync();
    foreach (var model in models)
    {
        Console.WriteLine($"Model: {model.Name}");
    }
}

Agent and Interaction Workflow

using Maitento.Sdk;
using Maitento.Entities;

public async Task CreateAgentAndInteraction()
{
    await using var client = MaitentoClient.CreateWithApiKey("client-id", "client-secret");

    // Get an AI model
    var models = await client.AiModels.GetAllAsync();
    var claudeModel = models.First(m => m.Name.Contains("claude"));

    // Create an agent
    var agent = await client.Agents.CreateAsync(
        namespaceName: "my-namespace",
        name: "research-assistant",
        aiModelId: claudeModel.Id,
        systemPrompt: "You are a helpful research assistant specializing in technical topics.",
        summaryForOtherAgents: "Research assistant for technical queries"
    );
    Console.WriteLine($"Created agent: {agent.Id}");

    // Create a one-shot interaction
    var interaction = await client.Interactions.CreateOneShotAsync(
        namespaceName: "my-namespace",
        name: "quick-query",
        agentId: agent.Id,
        // ... other parameters
    );

    // Start an interaction process
    var process = await client.InteractionProcesses.StartAsync(
        namespaceName: "my-namespace",
        name: "quick-query",
        inputs: new[]
        {
            new NameValuePair { Name = "topic", Value = "Machine Learning" }
        }
    );
    Console.WriteLine($"Started process: {process.Id}");
}

Real-Time Process Monitoring

using Maitento.Sdk;
using Maitento.Sdk.Notifications;
using Maitento.Entities.Apps;

public class ProcessMonitor :
    INotificationClientHandlerAppProcessUpdated,
    INotificationClientHandlerCodeGenerationProcessUpdated
{
    private readonly List<NotificationSubscription> _subscriptions = new();
    private readonly TaskCompletionSource<bool> _completion = new();

    public async Task MonitorProcessesAsync(IMaitentoClient client, Guid processToWatch)
    {
        // Set up event handlers
        client.Notifications.Connected += (s, e) =>
            Console.WriteLine("Connected to notifications");
        client.Notifications.Disconnected += (s, e) =>
            Console.WriteLine($"Disconnected: {e.DisconnectionType}");

        // Start notifications
        await client.Notifications.StartAsync();

        // Subscribe with filter for specific process
        _subscriptions.Add(client.Notifications.Subscribe(
            (INotificationClientHandlerAppProcessUpdated)this,
            filterId: processToWatch));

        // Wait for completion
        await _completion.Task;

        // Cleanup
        foreach (var sub in _subscriptions)
            client.Notifications.Unsubscribe(sub);
        await client.Notifications.StopAsync();
    }

    public Task OnAppProcessUpdated(AppProcess process)
    {
        Console.WriteLine($"[{DateTime.Now:HH:mm:ss}] Process {process.Id}: {process.Status}");

        if (process.Status == ProcessStatus.Completed || process.Status == ProcessStatus.Failed)
        {
            _completion.TrySetResult(process.Status == ProcessStatus.Completed);
        }

        return Task.CompletedTask;
    }

    public Task OnCodeGenerationProcessUpdated(CodeGenerationProcess process)
    {
        Console.WriteLine($"[CODEGEN] {process.Id}: {process.Status}");
        return Task.CompletedTask;
    }
}

// Usage
var client = MaitentoClient.CreateWithApiKey("id", "secret");
var monitor = new ProcessMonitor();
await monitor.MonitorProcessesAsync(client, processId);

Document Search Application

using Maitento.Sdk;
using Maitento.Entities.IngestedFiles;

public class DocumentSearchService
{
    private readonly IMaitentoClient _client;
    private readonly Guid _partitionId;

    public DocumentSearchService(IMaitentoClient client, Guid partitionId)
    {
        _client = client;
        _partitionId = partitionId;
    }

    public async Task<IngestedFile> UploadDocumentAsync(string filePath)
    {
        byte[] content = await File.ReadAllBytesAsync(filePath);
        string fileName = Path.GetFileName(filePath);
        string contentType = GetContentType(fileName);

        // Check for duplicates
        string checksum = ComputeChecksum(content);
        var existing = await _client.Files.GetByChecksumAsync(checksum);
        if (existing != null)
        {
            throw new InvalidOperationException($"File already exists: {existing.Name}");
        }

        // Ingest
        return await _client.Files.IngestFileAsync(_partitionId, content, fileName, contentType);
    }

    public async Task<IEnumerable<SearchResult>> SearchAsync(string query)
    {
        var results = await _client.Files.SearchEmbeddingsAsync(_partitionId, query);

        return results.Select(r => new SearchResult
        {
            FileId = r.Id,
            FileName = r.Name,
            Score = r.Score,
            MatchedText = r.Text
        });
    }

    public async Task<byte[]?> DownloadAsync(Guid fileId)
    {
        var content = await _client.Files.GetRawContentAsync(fileId);
        return content?.Content;
    }

    private static string GetContentType(string fileName)
    {
        var ext = Path.GetExtension(fileName).ToLowerInvariant();
        return ext switch
        {
            ".pdf" => "application/pdf",
            ".docx" => "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
            ".txt" => "text/plain",
            ".md" => "text/markdown",
            ".json" => "application/json",
            _ => "application/octet-stream"
        };
    }

    private static string ComputeChecksum(byte[] content)
    {
        using var sha256 = System.Security.Cryptography.SHA256.Create();
        return Convert.ToBase64String(sha256.ComputeHash(content));
    }
}

public class SearchResult
{
    public Guid FileId { get; set; }
    public string FileName { get; set; } = "";
    public double Score { get; set; }
    public string MatchedText { get; set; } = "";
}

Virtual File System Management

using Maitento.Sdk;

public class VfsManager
{
    private readonly IMaitentoClient _client;

    public VfsManager(IMaitentoClient client)
    {
        _client = client;
    }

    public async Task<Guid> CreateProjectStructureAsync(string namespaceName, string vfsName)
    {
        // Create VFS
        var vfs = await _client.VirtualFileSystems.CreateAsync(namespaceName, vfsName);

        // Create directory structure
        await _client.VirtualFileSystems.CreateDirectoryById(vfs.Id, "/src");
        await _client.VirtualFileSystems.CreateDirectoryById(vfs.Id, "/src/services");
        await _client.VirtualFileSystems.CreateDirectoryById(vfs.Id, "/src/models");
        await _client.VirtualFileSystems.CreateDirectoryById(vfs.Id, "/tests");
        await _client.VirtualFileSystems.CreateDirectoryById(vfs.Id, "/docs");
        await _client.VirtualFileSystems.CreateDirectoryById(vfs.Id, "/config");

        // Create initial files
        await _client.VirtualFileSystems.WriteTextById(vfs.Id, "/README.md", "# Project\n\nProject documentation.");
        await _client.VirtualFileSystems.WriteTextById(vfs.Id, "/config/settings.json", "{\n  \"debug\": true\n}");

        return vfs.Id;
    }

    public async Task<string> GetProjectTreeAsync(Guid vfsId)
    {
        var tree = await _client.VirtualFileSystems.TreeById(vfsId, new TreeRequest
        {
            Path = "/",
            MaxDepth = -1,
            IncludeFiles = true
        });

        var sb = new StringBuilder();
        sb.AppendLine($"Files: {tree.FileCount}, Directories: {tree.DirectoryCount}");

        foreach (var entry in tree.Entries)
        {
            var indent = new string(' ', entry.Depth * 2);
            var icon = entry.Type == "directory" ? "[D]" : "[F]";
            sb.AppendLine($"{indent}{icon} {entry.Path}");
        }

        return sb.ToString();
    }

    public async Task<FindResponse> SearchCodeAsync(Guid vfsId, string pattern)
    {
        return await _client.VirtualFileSystems.FindById(vfsId, new FindRequest
        {
            Path = "/src",
            Pattern = "**/*.cs",
            Grep = pattern,
            IgnoreCase = true,
            ContextLines = 2,
            MaxResults = 100
        });
    }

    public async Task RefactorAsync(Guid vfsId, string oldName, string newName)
    {
        // Find all files containing the old name
        var findResult = await _client.VirtualFileSystems.FindById(vfsId, new FindRequest
        {
            Path = "/src",
            Pattern = "**/*.cs",
            Grep = oldName
        });

        // Build batch edit request
        var edits = findResult.Results.Select(r => new BatchEditFileRequest
        {
            Path = r.Path,
            Operations = new[]
            {
                new EditOperation
                {
                    Type = "replace",
                    Find = oldName,
                    Replace = newName,
                    ReplaceAll = true
                }
            }
        }).ToArray();

        // Execute atomic refactoring
        var result = await _client.VirtualFileSystems.BatchEditById(vfsId, new BatchEditRequest
        {
            Atomic = true,
            Edits = edits
        });

        if (!result.Success)
        {
            var failures = result.FileResults.Where(f => !f.Success);
            throw new Exception($"Refactoring failed: {string.Join(", ", failures.Select(f => f.Error))}");
        }
    }
}

8. Error Handling

Exception Hierarchy

The SDK uses a structured exception hierarchy:

MaitentoSdkException (base)
    |
    +-- MaitentoApiException
    |       +-- StatusCode: HttpStatusCode
    |       +-- ResponseBody: string
    |
    +-- MaitentoApiAuthenticationException
    |       (401/403 responses)
    |
    +-- MaitentoValidationException
    |       +-- ValidationErrors: Dictionary<string, string[]>
    |
    +-- MaitentoDeserializationException
    |       +-- TypeName: string
    |       +-- RawContent: string
    |
    +-- MaitentoUnexpectedNullResponseException
    |
    +-- MaitentoNotificationAuthenticationException
    |
    +-- MaitentoNotificationUnsupportedAuthenticationException

Error Handling Patterns

Basic Error Handling

try
{
    var agent = await client.Agents.GetByIdAsync(agentId);
}
catch (MaitentoApiAuthenticationException ex)
{
    // Authentication failed (401/403)
    Console.WriteLine($"Auth error: {ex.StatusCode} - {ex.ResponseBody}");
}
catch (MaitentoValidationException ex)
{
    // Validation errors (400 with details)
    foreach (var (field, errors) in ex.ValidationErrors)
    {
        Console.WriteLine($"Field '{field}': {string.Join(", ", errors)}");
    }
}
catch (MaitentoApiException ex)
{
    // Other API errors
    Console.WriteLine($"API error: {ex.StatusCode} - {ex.ResponseBody}");
}
catch (MaitentoDeserializationException ex)
{
    // Response parsing failed
    Console.WriteLine($"Failed to parse {ex.TypeName}: {ex.RawContent}");
}
catch (MaitentoSdkException ex)
{
    // Base exception for all SDK errors
    Console.WriteLine($"SDK error: {ex.Message}");
}

Handling Specific HTTP Status Codes

try
{
    await client.Files.DeleteAsync(fileId);
}
catch (MaitentoApiException ex) when (ex.StatusCode == HttpStatusCode.NotFound)
{
    Console.WriteLine("File not found or already deleted");
}
catch (MaitentoApiException ex) when (ex.StatusCode == HttpStatusCode.Forbidden)
{
    Console.WriteLine("Insufficient permissions to delete this file");
}

Using Cancellation Tokens

using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(30));

try
{
    var result = await client.Namespaces.GetAllAsync(cts.Token);
}
catch (TaskCanceledException)
{
    Console.WriteLine("Operation timed out after 30 seconds");
}

Retry Pattern

The SDK has built-in retry support, but you can also implement custom retry logic:

public async Task<T> ExecuteWithRetryAsync<T>(
    Func<Task<T>> operation,
    int maxAttempts = 3,
    TimeSpan? delay = null)
{
    delay ??= TimeSpan.FromSeconds(1);

    for (int attempt = 1; attempt <= maxAttempts; attempt++)
    {
        try
        {
            return await operation();
        }
        catch (MaitentoApiException ex) when (
            attempt < maxAttempts &&
            (ex.StatusCode == HttpStatusCode.ServiceUnavailable ||
             ex.StatusCode == HttpStatusCode.GatewayTimeout))
        {
            Console.WriteLine($"Attempt {attempt} failed, retrying in {delay}...");
            await Task.Delay(delay.Value);
            delay = TimeSpan.FromMilliseconds(delay.Value.TotalMilliseconds * 2); // Exponential backoff
        }
    }

    throw new Exception("All retry attempts failed");
}

// Usage
var namespaces = await ExecuteWithRetryAsync(() => client.Namespaces.GetAllAsync());

Summary

The Maitento .NET SDK provides a comprehensive, type-safe interface for interacting with the Maitento platform. Key takeaways:

  • Use factory methods for simple client creation, or the constructor for advanced scenarios
  • Leverage dependency injection in ASP.NET Core applications
  • Handle exceptions appropriately using the structured exception hierarchy
  • Use real-time notifications for responsive applications
  • Organize files in partitions for scoped semantic search
  • Use VFS operations for isolated file management
  • Always dispose the client to clean up resources properly

For additional details on specific services, refer to the source code documentation and API reference.