@agenticmail/core 0.7.5 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.cts CHANGED
@@ -1843,4 +1843,254 @@ declare class SetupManager {
1843
1843
  isInitialized(): boolean;
1844
1844
  }
1845
1845
 
1846
- export { AGENT_ROLES, AccountManager, type AddressInfo, type Agent, AgentDeletionService, type AgentRole, AgenticMailClient, type AgenticMailClientOptions, type AgenticMailConfig, type ArchiveAndDeleteOptions, type ArchivedEmail, type Attachment, type AttachmentAdvisory, CloudflareClient, type CreateAgentOptions, DEFAULT_AGENT_NAME, DEFAULT_AGENT_ROLE, DNSConfigurator, type Database, type DeletionReport, type DeletionSummary, DependencyChecker, DependencyInstaller, type DependencyStatus, type DnsRecord, type DnsSetupResult, type DomainInfo, DomainManager, type DomainModeConfig, type DomainPurchaseResult, DomainPurchaser, type DomainSearchResult, type DomainSetupResult, type EmailEnvelope, type EmailRouteAction, type EmailRouteClass, type EmailRouteClassification, type EmailRouteInput, EmailSearchIndex, type FolderInfo, type GatewayConfig, GatewayManager, type GatewayManagerOptions, type GatewayMode, type GatewayStatus, type InboundEmail, type InboxEvent, type InboxExpungeEvent, type InboxFlagsEvent, type InboxNewEvent, InboxWatcher, type InboxWatcherOptions, type InstallProgress, type LinkAdvisory, type LocalSmtpConfig, MailReceiver, type MailReceiverOptions, MailSender, type MailSenderOptions, type MailboxInfo, type OutboundCategory, type OutboundScanInput, type OutboundScanResult, type OutboundWarning, type ParsedAttachment, type ParsedEmail, type ParsedSms, type PurchasedDomain, RELAY_PRESETS, RelayBridge, type RelayBridgeOptions, type RelayConfig, RelayGateway, type RelayProvider, type RelaySearchResult, SPAM_THRESHOLD, type SanitizeDetection, type SanitizeResult, type SearchCriteria, type SearchableEmail, type SecurityAdvisory, type SendMailOptions, type SendResult, type SendResultWithRaw, ServiceManager, type ServiceStatus, type SetupConfig, SetupManager, type SetupResult, type Severity, type SmsConfig, SmsManager, type SmsMessage, SmsPoller, type SpamCategory, type SpamResult, type SpamRuleMatch, StalwartAdmin, type StalwartAdminOptions, type StalwartPrincipal, type TunnelConfig, TunnelManager, WARNING_THRESHOLD, type WatcherOptions, buildInboundSecurityAdvisory, classifyEmailRoute, closeDatabase, createTestDatabase, debug, debugWarn, ensureDataDir, extractVerificationCode, flushTelemetry, getDatabase, isInternalEmail, isValidPhoneNumber, normalizePhoneNumber, parseEmail, parseGoogleVoiceSms, recordToolCall, resolveConfig, sanitizeEmail, saveConfig, scanOutboundEmail, scoreEmail, setTelemetryVersion, startRelayBridge };
1846
+ /**
1847
+ * Stable thread-id derivation.
1848
+ *
1849
+ * Two messages belong to the same thread if their normalized
1850
+ * `(subject, root-from)` tuple matches. Computing this on demand
1851
+ * from envelope data is cheap and avoids depending on the IMAP
1852
+ * `THREAD` extension (which Stalwart doesn't advertise) or on
1853
+ * reconstructing `In-Reply-To` / `References` chains (which agents
1854
+ * sometimes forge or strip).
1855
+ *
1856
+ * # Normalization rules
1857
+ *
1858
+ * - Strip every leading `Re:` / `Fwd:` / `Re[2]:` chain. Some
1859
+ * clients chain prefixes (`Re: Re: Fwd: Re: …`), which would
1860
+ * otherwise produce a different thread id for every hop.
1861
+ * - Collapse internal whitespace to single spaces.
1862
+ * - Trim leading + trailing whitespace.
1863
+ * - Lower-case for case-insensitive matching.
1864
+ * - Reply-on-thread coordination markers (`[FINAL]`, `[DONE]`,
1865
+ * `[CLOSED]`, `[WRAP]`) are stripped — a closing message
1866
+ * belongs to the SAME thread as the conversation it closes.
1867
+ *
1868
+ * # Identity hash
1869
+ *
1870
+ * SHA-256 of `<normalizedSubject>\n<rootFromLower>`, base64url
1871
+ * truncated to 16 chars (~12 bytes of entropy = ~10^28 distinct
1872
+ * threads; collision-free for any realistic deployment).
1873
+ *
1874
+ * The root sender is included so two unrelated conversations
1875
+ * that share a generic subject ("hello", "follow up") aren't
1876
+ * collapsed into one thread. We use the FIRST sender's address
1877
+ * on the thread — agents reading a reply pass their own
1878
+ * envelope's `from` value, but the thread id stays stable
1879
+ * because we re-derive `rootFromAddr` from the cache when
1880
+ * looking up an existing thread (see thread-cache.ts).
1881
+ */
1882
+ declare function normalizeSubject(subject: string | undefined | null): string;
1883
+ declare function normalizeAddress(addr: string | undefined | null): string;
1884
+ interface ThreadIdInput {
1885
+ subject?: string | null;
1886
+ /** Optional. Kept as a field so call sites that previously
1887
+ * passed it keep working, but NOT used in the hash. The thread
1888
+ * id is intentionally subject-only so a reply from a different
1889
+ * sender (the replier, not the root) still maps to the same
1890
+ * thread without needing a cache lookup first. The dispatcher's
1891
+ * legacy `threadIdFromSubject` uses the same convention; this
1892
+ * function is its disk-safe + hashed equivalent. */
1893
+ rootFromAddr?: string | null;
1894
+ }
1895
+ /**
1896
+ * Subject-only stable thread id. Collisions between unrelated
1897
+ * conversations that genuinely share the same normalized subject
1898
+ * ("hello", "follow up") are accepted as the tradeoff for stable
1899
+ * threading across replies. In practice agents on different
1900
+ * threads use different participants, so the wake-budget +
1901
+ * thread-close logic disambiguates downstream.
1902
+ */
1903
+ declare function threadIdFor(input: ThreadIdInput): string;
1904
+
1905
+ /**
1906
+ * Per-thread message cache — Layer 1 of the wake-context system.
1907
+ *
1908
+ * # What it stores
1909
+ *
1910
+ * Each thread (keyed by the stable `threadIdFor` hash) gets a JSON
1911
+ * file containing the last K message envelopes that were seen on
1912
+ * the thread:
1913
+ *
1914
+ * { threadId, subject, rootFromAddr, lastUpdated, messages: [
1915
+ * { uid, from, subject, preview, date }
1916
+ * ]}
1917
+ *
1918
+ * # What it does NOT store
1919
+ *
1920
+ * - Full message bodies. Storing the body would multiply the cache
1921
+ * size 20x and most agents don't need it on rehydration — they
1922
+ * need to see "who said what about what" at a glance, not the
1923
+ * exact prose. The dedicated `read_email(uid)` MCP tool serves
1924
+ * the body when the agent actually wants it.
1925
+ *
1926
+ * # Lifecycle
1927
+ *
1928
+ * - Built passively: the dispatcher calls `pushMessage(t, env)` on
1929
+ * every SSE new-mail event for the thread, even when no agent
1930
+ * actually wakes. Selective-wake skips, circuit-breaker mutes,
1931
+ * `[FINAL]` markers — none of them prevent the cache from being
1932
+ * populated. The cache is always up to date.
1933
+ *
1934
+ * - Read on every spawn: `readCache(t)` is called before the
1935
+ * dispatcher fires a worker, and the result is rendered into
1936
+ * the wake prompt.
1937
+ *
1938
+ * - Pruned on close: `cleanupThread(t)` is called when the
1939
+ * dispatcher detects a thread-close marker. Removes the file
1940
+ * immediately; 7-day grace via empty-tombstone is not needed
1941
+ * because the agent's own memory file handles "did this
1942
+ * thread actually close" semantics.
1943
+ *
1944
+ * - LRU-bounded: a directory-level LRU (default 5000 threads, ~25 MB)
1945
+ * runs at the head of every write to keep disk usage flat.
1946
+ */
1947
+ interface CachedMessage {
1948
+ uid: number;
1949
+ /** Display name OR raw address — whichever was on the envelope. */
1950
+ from: string;
1951
+ /** Sender's bare email (post-normalization). Same value used to
1952
+ * derive the thread root, so agents can spot "did I write this?"
1953
+ * with one equality check. */
1954
+ fromAddr: string;
1955
+ subject: string;
1956
+ /** Up to ~240 chars of plain-text body. */
1957
+ preview: string;
1958
+ /** ISO string. */
1959
+ date: string;
1960
+ }
1961
+ interface ThreadCacheEntry {
1962
+ threadId: string;
1963
+ /** First-seen normalized subject — kept on the entry so the wake
1964
+ * prompt can show it without re-normalizing. */
1965
+ subject: string;
1966
+ /** First-seen root sender. Used to keep `threadIdFor` stable on
1967
+ * subsequent replies (which carry the replier's `from`, not
1968
+ * the original). */
1969
+ rootFromAddr: string;
1970
+ /** ms timestamp of most recent write. Drives LRU eviction. */
1971
+ lastUpdated: number;
1972
+ /** Newest-first. We cap at K — drop oldest on push. */
1973
+ messages: CachedMessage[];
1974
+ }
1975
+ interface ThreadCacheOptions {
1976
+ /** Override the on-disk root. Mainly for tests. */
1977
+ cacheDir?: string;
1978
+ /** Newest-N messages kept per thread. */
1979
+ k?: number;
1980
+ /** Max threads on disk before LRU eviction kicks in. */
1981
+ lruCap?: number;
1982
+ }
1983
+ declare class ThreadCache {
1984
+ private readonly dir;
1985
+ private readonly k;
1986
+ private readonly lruCap;
1987
+ constructor(opts?: ThreadCacheOptions);
1988
+ private pathFor;
1989
+ read(threadId: string): ThreadCacheEntry | null;
1990
+ /**
1991
+ * Append a message to the thread's cache, pruning to the K
1992
+ * newest entries. Creates the cache entry on first write.
1993
+ *
1994
+ * `rootFromAddr` is the sender of the ROOT message on the
1995
+ * thread; on a brand-new thread this is just `env.fromAddr`,
1996
+ * on a reply it's read off the existing cache entry (callers
1997
+ * should pass the existing entry's rootFromAddr when known).
1998
+ */
1999
+ pushMessage(threadId: string, env: CachedMessage, meta: {
2000
+ subject: string;
2001
+ rootFromAddr: string;
2002
+ }): ThreadCacheEntry;
2003
+ /** Permanently remove a thread's cache (called on [FINAL] / [DONE] / [CLOSED] / [WRAP]). */
2004
+ delete(threadId: string): void;
2005
+ /**
2006
+ * Render the cache as a compact text block for the wake prompt.
2007
+ * One line per message, newest first. Empty string when the
2008
+ * cache is empty — caller decides whether to suppress the
2009
+ * header in that case.
2010
+ */
2011
+ renderForPrompt(entry: ThreadCacheEntry | null): string;
2012
+ private writeAtomic;
2013
+ /**
2014
+ * Best-effort LRU eviction. Runs at most every 256 writes (we
2015
+ * don't track a precise counter — `Math.random()` sampling keeps
2016
+ * the write path cheap). When the directory has more files than
2017
+ * `lruCap`, sort by mtime ascending and delete the oldest 10%.
2018
+ */
2019
+ private maybeEvict;
2020
+ }
2021
+
2022
+ /**
2023
+ * Per-agent thread memory — Layer 2 of the wake-context system.
2024
+ *
2025
+ * # What it stores
2026
+ *
2027
+ * Each `(agent, thread)` tuple gets a tiny markdown file
2028
+ * the AGENT writes at the end of its wake. The dispatcher
2029
+ * doesn't write this — it's the agent's own narrative about
2030
+ * what THEY committed to, what's open, and the last action
2031
+ * they took on the thread.
2032
+ *
2033
+ * # Why it's separate from the ThreadCache
2034
+ *
2035
+ * The cache is FACTS (whoever sent what when). The memory is
2036
+ * JUDGMENT (what does each agent intend to do about it). Two
2037
+ * agents on the same thread share the cache verbatim but each
2038
+ * has their own memory file; what Vesper thinks she committed
2039
+ * to is none of Orion's business.
2040
+ *
2041
+ * # Disk layout
2042
+ *
2043
+ * ~/.agenticmail/agent-memory/<agentId>/<threadId>.md
2044
+ *
2045
+ * The path is hierarchical (per-agent dir) so cleanup on agent
2046
+ * deletion is `rm -rf <agentDir>` and concurrent writers don't
2047
+ * step on each other across agents.
2048
+ *
2049
+ * # Format
2050
+ *
2051
+ * Tiny YAML frontmatter for structured fields the dispatcher
2052
+ * cares about (`updated_at`, `lastUid`); free-form markdown
2053
+ * body for the agent's prose. The agent passes these as
2054
+ * separate fields on the MCP tool and we render the file
2055
+ * deterministically — no Markdown-in-YAML parsing nightmare.
2056
+ */
2057
+ interface AgentMemoryFields {
2058
+ /** One-paragraph narrative of where the thread stands. */
2059
+ summary?: string;
2060
+ /** Things THIS agent has committed to doing. */
2061
+ commitments?: string[];
2062
+ /** Things THIS agent is waiting on / open questions. */
2063
+ openQuestions?: string[];
2064
+ /** Last action this agent took on the thread (e.g. "replied UID 41 asking for the raw counts"). */
2065
+ lastAction?: string;
2066
+ /** Last message UID this agent has digested. Used as a cursor
2067
+ * to detect "memory is older than the cache" on the dispatcher
2068
+ * side. */
2069
+ lastUid?: number;
2070
+ }
2071
+ interface AgentMemoryRead extends AgentMemoryFields {
2072
+ /** ISO timestamp of the most recent write. */
2073
+ updatedAt?: string;
2074
+ /** Raw file contents — useful for the wake prompt; rendered
2075
+ * verbatim into the "Your own memory" block. */
2076
+ raw: string;
2077
+ }
2078
+ interface AgentMemoryOptions {
2079
+ memoryDir?: string;
2080
+ }
2081
+ declare class AgentMemoryStore {
2082
+ private readonly dir;
2083
+ constructor(opts?: AgentMemoryOptions);
2084
+ private dirFor;
2085
+ private pathFor;
2086
+ read(agentId: string, threadId: string): AgentMemoryRead | null;
2087
+ write(agentId: string, threadId: string, fields: AgentMemoryFields): void;
2088
+ delete(agentId: string, threadId: string): void;
2089
+ /** Render an agent's memory for injection into a wake prompt.
2090
+ * Returns the raw markdown if present; empty string when there's
2091
+ * no prior memory (the caller decides whether to suppress the
2092
+ * whole "Your own memory" block). */
2093
+ renderForPrompt(memory: AgentMemoryRead | null): string;
2094
+ }
2095
+
2096
+ export { AGENT_ROLES, AccountManager, type AddressInfo, type Agent, AgentDeletionService, type AgentMemoryFields, type AgentMemoryOptions, type AgentMemoryRead, AgentMemoryStore, type AgentRole, AgenticMailClient, type AgenticMailClientOptions, type AgenticMailConfig, type ArchiveAndDeleteOptions, type ArchivedEmail, type Attachment, type AttachmentAdvisory, type CachedMessage, CloudflareClient, type CreateAgentOptions, DEFAULT_AGENT_NAME, DEFAULT_AGENT_ROLE, DNSConfigurator, type Database, type DeletionReport, type DeletionSummary, DependencyChecker, DependencyInstaller, type DependencyStatus, type DnsRecord, type DnsSetupResult, type DomainInfo, DomainManager, type DomainModeConfig, type DomainPurchaseResult, DomainPurchaser, type DomainSearchResult, type DomainSetupResult, type EmailEnvelope, type EmailRouteAction, type EmailRouteClass, type EmailRouteClassification, type EmailRouteInput, EmailSearchIndex, type FolderInfo, type GatewayConfig, GatewayManager, type GatewayManagerOptions, type GatewayMode, type GatewayStatus, type InboundEmail, type InboxEvent, type InboxExpungeEvent, type InboxFlagsEvent, type InboxNewEvent, InboxWatcher, type InboxWatcherOptions, type InstallProgress, type LinkAdvisory, type LocalSmtpConfig, MailReceiver, type MailReceiverOptions, MailSender, type MailSenderOptions, type MailboxInfo, type OutboundCategory, type OutboundScanInput, type OutboundScanResult, type OutboundWarning, type ParsedAttachment, type ParsedEmail, type ParsedSms, type PurchasedDomain, RELAY_PRESETS, RelayBridge, type RelayBridgeOptions, type RelayConfig, RelayGateway, type RelayProvider, type RelaySearchResult, SPAM_THRESHOLD, type SanitizeDetection, type SanitizeResult, type SearchCriteria, type SearchableEmail, type SecurityAdvisory, type SendMailOptions, type SendResult, type SendResultWithRaw, ServiceManager, type ServiceStatus, type SetupConfig, SetupManager, type SetupResult, type Severity, type SmsConfig, SmsManager, type SmsMessage, SmsPoller, type SpamCategory, type SpamResult, type SpamRuleMatch, StalwartAdmin, type StalwartAdminOptions, type StalwartPrincipal, ThreadCache, type ThreadCacheEntry, type ThreadCacheOptions, type ThreadIdInput, type TunnelConfig, TunnelManager, WARNING_THRESHOLD, type WatcherOptions, buildInboundSecurityAdvisory, classifyEmailRoute, closeDatabase, createTestDatabase, debug, debugWarn, ensureDataDir, extractVerificationCode, flushTelemetry, getDatabase, isInternalEmail, isValidPhoneNumber, normalizeAddress, normalizePhoneNumber, normalizeSubject, parseEmail, parseGoogleVoiceSms, recordToolCall, resolveConfig, sanitizeEmail, saveConfig, scanOutboundEmail, scoreEmail, setTelemetryVersion, startRelayBridge, threadIdFor };
package/dist/index.d.ts CHANGED
@@ -1843,4 +1843,254 @@ declare class SetupManager {
1843
1843
  isInitialized(): boolean;
1844
1844
  }
1845
1845
 
1846
- export { AGENT_ROLES, AccountManager, type AddressInfo, type Agent, AgentDeletionService, type AgentRole, AgenticMailClient, type AgenticMailClientOptions, type AgenticMailConfig, type ArchiveAndDeleteOptions, type ArchivedEmail, type Attachment, type AttachmentAdvisory, CloudflareClient, type CreateAgentOptions, DEFAULT_AGENT_NAME, DEFAULT_AGENT_ROLE, DNSConfigurator, type Database, type DeletionReport, type DeletionSummary, DependencyChecker, DependencyInstaller, type DependencyStatus, type DnsRecord, type DnsSetupResult, type DomainInfo, DomainManager, type DomainModeConfig, type DomainPurchaseResult, DomainPurchaser, type DomainSearchResult, type DomainSetupResult, type EmailEnvelope, type EmailRouteAction, type EmailRouteClass, type EmailRouteClassification, type EmailRouteInput, EmailSearchIndex, type FolderInfo, type GatewayConfig, GatewayManager, type GatewayManagerOptions, type GatewayMode, type GatewayStatus, type InboundEmail, type InboxEvent, type InboxExpungeEvent, type InboxFlagsEvent, type InboxNewEvent, InboxWatcher, type InboxWatcherOptions, type InstallProgress, type LinkAdvisory, type LocalSmtpConfig, MailReceiver, type MailReceiverOptions, MailSender, type MailSenderOptions, type MailboxInfo, type OutboundCategory, type OutboundScanInput, type OutboundScanResult, type OutboundWarning, type ParsedAttachment, type ParsedEmail, type ParsedSms, type PurchasedDomain, RELAY_PRESETS, RelayBridge, type RelayBridgeOptions, type RelayConfig, RelayGateway, type RelayProvider, type RelaySearchResult, SPAM_THRESHOLD, type SanitizeDetection, type SanitizeResult, type SearchCriteria, type SearchableEmail, type SecurityAdvisory, type SendMailOptions, type SendResult, type SendResultWithRaw, ServiceManager, type ServiceStatus, type SetupConfig, SetupManager, type SetupResult, type Severity, type SmsConfig, SmsManager, type SmsMessage, SmsPoller, type SpamCategory, type SpamResult, type SpamRuleMatch, StalwartAdmin, type StalwartAdminOptions, type StalwartPrincipal, type TunnelConfig, TunnelManager, WARNING_THRESHOLD, type WatcherOptions, buildInboundSecurityAdvisory, classifyEmailRoute, closeDatabase, createTestDatabase, debug, debugWarn, ensureDataDir, extractVerificationCode, flushTelemetry, getDatabase, isInternalEmail, isValidPhoneNumber, normalizePhoneNumber, parseEmail, parseGoogleVoiceSms, recordToolCall, resolveConfig, sanitizeEmail, saveConfig, scanOutboundEmail, scoreEmail, setTelemetryVersion, startRelayBridge };
1846
+ /**
1847
+ * Stable thread-id derivation.
1848
+ *
1849
+ * Two messages belong to the same thread if their normalized
1850
+ * `(subject, root-from)` tuple matches. Computing this on demand
1851
+ * from envelope data is cheap and avoids depending on the IMAP
1852
+ * `THREAD` extension (which Stalwart doesn't advertise) or on
1853
+ * reconstructing `In-Reply-To` / `References` chains (which agents
1854
+ * sometimes forge or strip).
1855
+ *
1856
+ * # Normalization rules
1857
+ *
1858
+ * - Strip every leading `Re:` / `Fwd:` / `Re[2]:` chain. Some
1859
+ * clients chain prefixes (`Re: Re: Fwd: Re: …`), which would
1860
+ * otherwise produce a different thread id for every hop.
1861
+ * - Collapse internal whitespace to single spaces.
1862
+ * - Trim leading + trailing whitespace.
1863
+ * - Lower-case for case-insensitive matching.
1864
+ * - Reply-on-thread coordination markers (`[FINAL]`, `[DONE]`,
1865
+ * `[CLOSED]`, `[WRAP]`) are stripped — a closing message
1866
+ * belongs to the SAME thread as the conversation it closes.
1867
+ *
1868
+ * # Identity hash
1869
+ *
1870
+ * SHA-256 of `<normalizedSubject>\n<rootFromLower>`, base64url
1871
+ * truncated to 16 chars (~12 bytes of entropy = ~10^28 distinct
1872
+ * threads; collision-free for any realistic deployment).
1873
+ *
1874
+ * The root sender is included so two unrelated conversations
1875
+ * that share a generic subject ("hello", "follow up") aren't
1876
+ * collapsed into one thread. We use the FIRST sender's address
1877
+ * on the thread — agents reading a reply pass their own
1878
+ * envelope's `from` value, but the thread id stays stable
1879
+ * because we re-derive `rootFromAddr` from the cache when
1880
+ * looking up an existing thread (see thread-cache.ts).
1881
+ */
1882
+ declare function normalizeSubject(subject: string | undefined | null): string;
1883
+ declare function normalizeAddress(addr: string | undefined | null): string;
1884
+ interface ThreadIdInput {
1885
+ subject?: string | null;
1886
+ /** Optional. Kept as a field so call sites that previously
1887
+ * passed it keep working, but NOT used in the hash. The thread
1888
+ * id is intentionally subject-only so a reply from a different
1889
+ * sender (the replier, not the root) still maps to the same
1890
+ * thread without needing a cache lookup first. The dispatcher's
1891
+ * legacy `threadIdFromSubject` uses the same convention; this
1892
+ * function is its disk-safe + hashed equivalent. */
1893
+ rootFromAddr?: string | null;
1894
+ }
1895
+ /**
1896
+ * Subject-only stable thread id. Collisions between unrelated
1897
+ * conversations that genuinely share the same normalized subject
1898
+ * ("hello", "follow up") are accepted as the tradeoff for stable
1899
+ * threading across replies. In practice agents on different
1900
+ * threads use different participants, so the wake-budget +
1901
+ * thread-close logic disambiguates downstream.
1902
+ */
1903
+ declare function threadIdFor(input: ThreadIdInput): string;
1904
+
1905
+ /**
1906
+ * Per-thread message cache — Layer 1 of the wake-context system.
1907
+ *
1908
+ * # What it stores
1909
+ *
1910
+ * Each thread (keyed by the stable `threadIdFor` hash) gets a JSON
1911
+ * file containing the last K message envelopes that were seen on
1912
+ * the thread:
1913
+ *
1914
+ * { threadId, subject, rootFromAddr, lastUpdated, messages: [
1915
+ * { uid, from, subject, preview, date }
1916
+ * ]}
1917
+ *
1918
+ * # What it does NOT store
1919
+ *
1920
+ * - Full message bodies. Storing the body would multiply the cache
1921
+ * size 20x and most agents don't need it on rehydration — they
1922
+ * need to see "who said what about what" at a glance, not the
1923
+ * exact prose. The dedicated `read_email(uid)` MCP tool serves
1924
+ * the body when the agent actually wants it.
1925
+ *
1926
+ * # Lifecycle
1927
+ *
1928
+ * - Built passively: the dispatcher calls `pushMessage(t, env)` on
1929
+ * every SSE new-mail event for the thread, even when no agent
1930
+ * actually wakes. Selective-wake skips, circuit-breaker mutes,
1931
+ * `[FINAL]` markers — none of them prevent the cache from being
1932
+ * populated. The cache is always up to date.
1933
+ *
1934
+ * - Read on every spawn: `readCache(t)` is called before the
1935
+ * dispatcher fires a worker, and the result is rendered into
1936
+ * the wake prompt.
1937
+ *
1938
+ * - Pruned on close: `cleanupThread(t)` is called when the
1939
+ * dispatcher detects a thread-close marker. Removes the file
1940
+ * immediately; 7-day grace via empty-tombstone is not needed
1941
+ * because the agent's own memory file handles "did this
1942
+ * thread actually close" semantics.
1943
+ *
1944
+ * - LRU-bounded: a directory-level LRU (default 5000 threads, ~25 MB)
1945
+ * runs at the head of every write to keep disk usage flat.
1946
+ */
1947
+ interface CachedMessage {
1948
+ uid: number;
1949
+ /** Display name OR raw address — whichever was on the envelope. */
1950
+ from: string;
1951
+ /** Sender's bare email (post-normalization). Same value used to
1952
+ * derive the thread root, so agents can spot "did I write this?"
1953
+ * with one equality check. */
1954
+ fromAddr: string;
1955
+ subject: string;
1956
+ /** Up to ~240 chars of plain-text body. */
1957
+ preview: string;
1958
+ /** ISO string. */
1959
+ date: string;
1960
+ }
1961
+ interface ThreadCacheEntry {
1962
+ threadId: string;
1963
+ /** First-seen normalized subject — kept on the entry so the wake
1964
+ * prompt can show it without re-normalizing. */
1965
+ subject: string;
1966
+ /** First-seen root sender. Used to keep `threadIdFor` stable on
1967
+ * subsequent replies (which carry the replier's `from`, not
1968
+ * the original). */
1969
+ rootFromAddr: string;
1970
+ /** ms timestamp of most recent write. Drives LRU eviction. */
1971
+ lastUpdated: number;
1972
+ /** Newest-first. We cap at K — drop oldest on push. */
1973
+ messages: CachedMessage[];
1974
+ }
1975
+ interface ThreadCacheOptions {
1976
+ /** Override the on-disk root. Mainly for tests. */
1977
+ cacheDir?: string;
1978
+ /** Newest-N messages kept per thread. */
1979
+ k?: number;
1980
+ /** Max threads on disk before LRU eviction kicks in. */
1981
+ lruCap?: number;
1982
+ }
1983
+ declare class ThreadCache {
1984
+ private readonly dir;
1985
+ private readonly k;
1986
+ private readonly lruCap;
1987
+ constructor(opts?: ThreadCacheOptions);
1988
+ private pathFor;
1989
+ read(threadId: string): ThreadCacheEntry | null;
1990
+ /**
1991
+ * Append a message to the thread's cache, pruning to the K
1992
+ * newest entries. Creates the cache entry on first write.
1993
+ *
1994
+ * `rootFromAddr` is the sender of the ROOT message on the
1995
+ * thread; on a brand-new thread this is just `env.fromAddr`,
1996
+ * on a reply it's read off the existing cache entry (callers
1997
+ * should pass the existing entry's rootFromAddr when known).
1998
+ */
1999
+ pushMessage(threadId: string, env: CachedMessage, meta: {
2000
+ subject: string;
2001
+ rootFromAddr: string;
2002
+ }): ThreadCacheEntry;
2003
+ /** Permanently remove a thread's cache (called on [FINAL] / [DONE] / [CLOSED] / [WRAP]). */
2004
+ delete(threadId: string): void;
2005
+ /**
2006
+ * Render the cache as a compact text block for the wake prompt.
2007
+ * One line per message, newest first. Empty string when the
2008
+ * cache is empty — caller decides whether to suppress the
2009
+ * header in that case.
2010
+ */
2011
+ renderForPrompt(entry: ThreadCacheEntry | null): string;
2012
+ private writeAtomic;
2013
+ /**
2014
+ * Best-effort LRU eviction. Runs at most every 256 writes (we
2015
+ * don't track a precise counter — `Math.random()` sampling keeps
2016
+ * the write path cheap). When the directory has more files than
2017
+ * `lruCap`, sort by mtime ascending and delete the oldest 10%.
2018
+ */
2019
+ private maybeEvict;
2020
+ }
2021
+
2022
+ /**
2023
+ * Per-agent thread memory — Layer 2 of the wake-context system.
2024
+ *
2025
+ * # What it stores
2026
+ *
2027
+ * Each `(agent, thread)` tuple gets a tiny markdown file
2028
+ * the AGENT writes at the end of its wake. The dispatcher
2029
+ * doesn't write this — it's the agent's own narrative about
2030
+ * what THEY committed to, what's open, and the last action
2031
+ * they took on the thread.
2032
+ *
2033
+ * # Why it's separate from the ThreadCache
2034
+ *
2035
+ * The cache is FACTS (whoever sent what when). The memory is
2036
+ * JUDGMENT (what does each agent intend to do about it). Two
2037
+ * agents on the same thread share the cache verbatim but each
2038
+ * has their own memory file; what Vesper thinks she committed
2039
+ * to is none of Orion's business.
2040
+ *
2041
+ * # Disk layout
2042
+ *
2043
+ * ~/.agenticmail/agent-memory/<agentId>/<threadId>.md
2044
+ *
2045
+ * The path is hierarchical (per-agent dir) so cleanup on agent
2046
+ * deletion is `rm -rf <agentDir>` and concurrent writers don't
2047
+ * step on each other across agents.
2048
+ *
2049
+ * # Format
2050
+ *
2051
+ * Tiny YAML frontmatter for structured fields the dispatcher
2052
+ * cares about (`updated_at`, `lastUid`); free-form markdown
2053
+ * body for the agent's prose. The agent passes these as
2054
+ * separate fields on the MCP tool and we render the file
2055
+ * deterministically — no Markdown-in-YAML parsing nightmare.
2056
+ */
2057
+ interface AgentMemoryFields {
2058
+ /** One-paragraph narrative of where the thread stands. */
2059
+ summary?: string;
2060
+ /** Things THIS agent has committed to doing. */
2061
+ commitments?: string[];
2062
+ /** Things THIS agent is waiting on / open questions. */
2063
+ openQuestions?: string[];
2064
+ /** Last action this agent took on the thread (e.g. "replied UID 41 asking for the raw counts"). */
2065
+ lastAction?: string;
2066
+ /** Last message UID this agent has digested. Used as a cursor
2067
+ * to detect "memory is older than the cache" on the dispatcher
2068
+ * side. */
2069
+ lastUid?: number;
2070
+ }
2071
+ interface AgentMemoryRead extends AgentMemoryFields {
2072
+ /** ISO timestamp of the most recent write. */
2073
+ updatedAt?: string;
2074
+ /** Raw file contents — useful for the wake prompt; rendered
2075
+ * verbatim into the "Your own memory" block. */
2076
+ raw: string;
2077
+ }
2078
+ interface AgentMemoryOptions {
2079
+ memoryDir?: string;
2080
+ }
2081
+ declare class AgentMemoryStore {
2082
+ private readonly dir;
2083
+ constructor(opts?: AgentMemoryOptions);
2084
+ private dirFor;
2085
+ private pathFor;
2086
+ read(agentId: string, threadId: string): AgentMemoryRead | null;
2087
+ write(agentId: string, threadId: string, fields: AgentMemoryFields): void;
2088
+ delete(agentId: string, threadId: string): void;
2089
+ /** Render an agent's memory for injection into a wake prompt.
2090
+ * Returns the raw markdown if present; empty string when there's
2091
+ * no prior memory (the caller decides whether to suppress the
2092
+ * whole "Your own memory" block). */
2093
+ renderForPrompt(memory: AgentMemoryRead | null): string;
2094
+ }
2095
+
2096
+ export { AGENT_ROLES, AccountManager, type AddressInfo, type Agent, AgentDeletionService, type AgentMemoryFields, type AgentMemoryOptions, type AgentMemoryRead, AgentMemoryStore, type AgentRole, AgenticMailClient, type AgenticMailClientOptions, type AgenticMailConfig, type ArchiveAndDeleteOptions, type ArchivedEmail, type Attachment, type AttachmentAdvisory, type CachedMessage, CloudflareClient, type CreateAgentOptions, DEFAULT_AGENT_NAME, DEFAULT_AGENT_ROLE, DNSConfigurator, type Database, type DeletionReport, type DeletionSummary, DependencyChecker, DependencyInstaller, type DependencyStatus, type DnsRecord, type DnsSetupResult, type DomainInfo, DomainManager, type DomainModeConfig, type DomainPurchaseResult, DomainPurchaser, type DomainSearchResult, type DomainSetupResult, type EmailEnvelope, type EmailRouteAction, type EmailRouteClass, type EmailRouteClassification, type EmailRouteInput, EmailSearchIndex, type FolderInfo, type GatewayConfig, GatewayManager, type GatewayManagerOptions, type GatewayMode, type GatewayStatus, type InboundEmail, type InboxEvent, type InboxExpungeEvent, type InboxFlagsEvent, type InboxNewEvent, InboxWatcher, type InboxWatcherOptions, type InstallProgress, type LinkAdvisory, type LocalSmtpConfig, MailReceiver, type MailReceiverOptions, MailSender, type MailSenderOptions, type MailboxInfo, type OutboundCategory, type OutboundScanInput, type OutboundScanResult, type OutboundWarning, type ParsedAttachment, type ParsedEmail, type ParsedSms, type PurchasedDomain, RELAY_PRESETS, RelayBridge, type RelayBridgeOptions, type RelayConfig, RelayGateway, type RelayProvider, type RelaySearchResult, SPAM_THRESHOLD, type SanitizeDetection, type SanitizeResult, type SearchCriteria, type SearchableEmail, type SecurityAdvisory, type SendMailOptions, type SendResult, type SendResultWithRaw, ServiceManager, type ServiceStatus, type SetupConfig, SetupManager, type SetupResult, type Severity, type SmsConfig, SmsManager, type SmsMessage, SmsPoller, type SpamCategory, type SpamResult, type SpamRuleMatch, StalwartAdmin, type StalwartAdminOptions, type StalwartPrincipal, ThreadCache, type ThreadCacheEntry, type ThreadCacheOptions, type ThreadIdInput, type TunnelConfig, TunnelManager, WARNING_THRESHOLD, type WatcherOptions, buildInboundSecurityAdvisory, classifyEmailRoute, closeDatabase, createTestDatabase, debug, debugWarn, ensureDataDir, extractVerificationCode, flushTelemetry, getDatabase, isInternalEmail, isValidPhoneNumber, normalizeAddress, normalizePhoneNumber, normalizeSubject, parseEmail, parseGoogleVoiceSms, recordToolCall, resolveConfig, sanitizeEmail, saveConfig, scanOutboundEmail, scoreEmail, setTelemetryVersion, startRelayBridge, threadIdFor };