V17 / EMAIL

email_read_operation

Application-bound scenario operation. Named XML inputs control execution; this is not a main-agent tool.

See source registrationIntroduced in V13V17

What it does

Read the exact source snapshot below for its parameters, behavior, and registration.

Read the implementation

This page describes the selected release. The source link points to the same exact commit; it cannot silently open a newer implementation.

Lilith/Core/Email/EmailOperations.cs ↗

Implementation code

Read the implementation from this version's exact source snapshot. Helper links below stay on the same release.

Lilith/Core/Email/EmailOperations.cs

C#
using System.Text.Json;
namespace Lilith.Core.Email;

internal static class EmailOperations
{
    internal static readonly HashSet<string> Names = ["email_check_operation", "email_accounts_operation", "email_greeting_operation", "email_batch_operation", "email_read_operation", "email_assessment_operation", "email_spam_operation", "email_sections_operation"];
    internal static async Task<string> ExecuteAsync(ProviderClient provider, string name, IReadOnlyDictionary<string, string> inputs, CancellationToken ct)
    {
        string Get(string key) => inputs.GetValueOrDefault(key) ?? "";
        string nickname = Get("nickname");
        switch (name)
        {
            case "email_greeting_operation": return await EmailClient.GreetingAsync(ct);
            case "email_accounts_operation": return await EmailClient.RequestAsync("accounts", null, provider.EmailMode, ct);
            case "email_sections_operation": return await EmailClient.SendAsync("sections", new { nickname }, ct);
            case "email_check_operation":
                string batch = await EmailClient.RequestAsync("check", nickname, provider.EmailMode, ct);
                provider.EmailBatches[nickname] = batch;
                return batch;
            case "email_batch_operation":
                string current = provider.EmailBatches.GetValueOrDefault(nickname) ?? await EmailClient.RequestAsync("check", nickname, provider.EmailMode, ct);
                provider.EmailBatches[nickname] = current;
                using (var data = JsonDocument.Parse(current))
                using (var sections = JsonDocument.Parse(await EmailClient.SendAsync("sections", new { nickname }, ct)))
                {
                    var root = data.RootElement;
                    var blocked = sections.RootElement.GetProperty("blockedSenders").EnumerateArray().Select(x => x.GetString()).ToHashSet(StringComparer.OrdinalIgnoreCase);
                    var all = root.GetProperty("messages").EnumerateArray().ToArray();
                    return JsonSerializer.Serialize(new { nickname, validity = root.GetProperty("validity").GetUInt32(), source = root.GetProperty("source").GetString(), moreAvailable = root.GetProperty("moreAvailable").GetBoolean(), batchCount = all.Length, selectedCount = all.Count(x => !blocked.Contains(x.GetProperty("senderAddress").GetString())), omittedSpam = all.Count(x => blocked.Contains(x.GetProperty("senderAddress").GetString())), messages = all.Where(x => !blocked.Contains(x.GetProperty("senderAddress").GetString())).Select(x => x.Clone()).ToArray() });
                }
            case "email_read_operation":
                try { return await EmailClient.SendAsync("read", new { nickname, validity = uint.Parse(Get("validity")), uid = uint.Parse(Get("uid")) }, ct); }
                catch (Exception ex) when ((ex is IOException or HttpRequestException or TaskCanceledException) && !ct.IsCancellationRequested)
                { return JsonSerializer.Serialize(new { status = "unreadable", nickname, error = "Message could not be read; do not invent a summary." }); }
            case "email_assessment_operation":
                try
                {
                    using (var assessment = JsonDocument.Parse(Get("assessment")))
                    using (var message = JsonDocument.Parse(Get("message")))
                    {
                        if (message.RootElement.GetProperty("status").GetString() != "completed") return JsonSerializer.Serialize(new { readable = false, error = "Message unavailable" });
                        string section = assessment.RootElement.GetProperty("section").GetString() ?? "";
                        string summary = assessment.RootElement.GetProperty("summary").GetString() ?? "";
                        if (section is not ("inbox" or "shopping") || string.IsNullOrWhiteSpace(summary) || summary.Length > 1800) throw new InvalidDataException("Invalid email assessment.");
                        var mail = message.RootElement;
                        await EmailClient.SendAsync("section", new { nickname, validity = mail.GetProperty("validity").GetUInt32(), uid = mail.GetProperty("message").GetProperty("uid").GetUInt32(), section }, ct);
                        return JsonSerializer.Serialize(new { readable = true, section, summary, message = mail.GetProperty("message").Clone(), source = mail.GetProperty("source").GetString(), truncated = mail.GetProperty("truncated").GetBoolean() });
                    }
                }
                catch (Exception ex) when (ex is JsonException or InvalidDataException or KeyNotFoundException or InvalidOperationException)
                { return JsonSerializer.Serialize(new { readable = false, error = "Reader assessment was invalid; this message has no reliable briefing summary." }); }
            case "email_spam_operation":
                if (Get("blocked") is not ("true" or "false")) throw new ArgumentException("blocked must be true or false.");
                if (!System.Net.Mail.MailAddress.TryCreate(Get("sender"), out var sender) || sender.Address != Get("sender")) throw new ArgumentException("Specify an exact sender address.");
                string id = Guid.NewGuid().ToString("N");
                provider.PendingEmailRule = (id, nickname, sender.Address, Get("blocked") == "true");
                return $"Proposed local sender rule for {nickname}: {(Get("blocked") == "true" ? "block" : "unblock")} {sender.Address}. Applies to existing and future messages from this exact sender in this mailbox; Gmail stays unchanged. To apply it, enter /email-confirm {id}.";
            default: throw new ArgumentException("Unknown email operation.");
        }
    }
}

Scenarios using this tool