@agenticmail/core 0.9.9 → 0.9.10

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.cjs CHANGED
@@ -713,6 +713,7 @@ __export(index_exports, {
713
713
  AgentDeletionService: () => AgentDeletionService,
714
714
  AgentMemoryStore: () => AgentMemoryStore,
715
715
  AgenticMailClient: () => AgenticMailClient,
716
+ BRIDGE_OPERATOR_LIVE_WINDOW_MS: () => BRIDGE_OPERATOR_LIVE_WINDOW_MS,
716
717
  CloudflareClient: () => CloudflareClient,
717
718
  DEFAULT_AGENT_NAME: () => DEFAULT_AGENT_NAME,
718
719
  DEFAULT_AGENT_ROLE: () => DEFAULT_AGENT_ROLE,
@@ -743,10 +744,14 @@ __export(index_exports, {
743
744
  UnsafeApiUrlError: () => UnsafeApiUrlError,
744
745
  WARNING_THRESHOLD: () => WARNING_THRESHOLD,
745
746
  assertWithinBase: () => assertWithinBase,
747
+ bridgeWakeErrorMessage: () => bridgeWakeErrorMessage,
748
+ bridgeWakeLastSeenAgeMs: () => bridgeWakeLastSeenAgeMs,
746
749
  buildApiUrl: () => buildApiUrl,
747
750
  buildInboundSecurityAdvisory: () => buildInboundSecurityAdvisory,
748
751
  classifyEmailRoute: () => classifyEmailRoute,
752
+ classifyResumeError: () => classifyResumeError,
749
753
  closeDatabase: () => closeDatabase,
754
+ composeBridgeWakePrompt: () => composeBridgeWakePrompt,
750
755
  createTestDatabase: () => createTestDatabase,
751
756
  debug: () => debug,
752
757
  debugWarn: () => debugWarn,
@@ -782,6 +787,7 @@ __export(index_exports, {
782
787
  scoreEmail: () => scoreEmail,
783
788
  setOperatorEmail: () => setOperatorEmail,
784
789
  setTelemetryVersion: () => setTelemetryVersion,
790
+ shouldSkipBridgeWakeForLiveOperator: () => shouldSkipBridgeWakeForLiveOperator,
785
791
  startRelayBridge: () => startRelayBridge,
786
792
  threadIdFor: () => threadIdFor,
787
793
  tryJoin: () => tryJoin,
@@ -805,8 +811,7 @@ var MailSender = class {
805
811
  pass: options.password
806
812
  },
807
813
  tls: {
808
- rejectUnauthorized: false
809
- // Local dev — no TLS
814
+ rejectUnauthorized: options.tlsRejectUnauthorized ?? true
810
815
  },
811
816
  connectionTimeout: 1e4,
812
817
  // 10s to establish TCP connection
@@ -7233,6 +7238,69 @@ function hostSessionStoragePath() {
7233
7238
  return storagePath();
7234
7239
  }
7235
7240
 
7241
+ // src/host-bridge.ts
7242
+ var BRIDGE_OPERATOR_LIVE_WINDOW_MS = 3e4;
7243
+ var DEFAULT_EXPIRED_MARKERS = [
7244
+ "session not found",
7245
+ "invalid session",
7246
+ "session expired",
7247
+ "no such session",
7248
+ "unknown session",
7249
+ "thread not found",
7250
+ "invalid thread",
7251
+ "thread expired",
7252
+ "no such thread",
7253
+ "unknown thread"
7254
+ ];
7255
+ var DEFAULT_SDK_MISSING_MARKERS = [
7256
+ "cannot find module",
7257
+ "could not be found",
7258
+ "command not found"
7259
+ ];
7260
+ function bridgeWakeErrorMessage(err) {
7261
+ return err?.message ?? String(err);
7262
+ }
7263
+ function classifyResumeError(err, options = {}) {
7264
+ const msg = bridgeWakeErrorMessage(err).toLowerCase();
7265
+ const expiredMarkers = options.expiredMarkers ?? DEFAULT_EXPIRED_MARKERS;
7266
+ const sdkMissingMarkers = options.sdkMissingMarkers ?? DEFAULT_SDK_MISSING_MARKERS;
7267
+ if (expiredMarkers.some((marker) => msg.includes(marker))) return "session-expired";
7268
+ if (sdkMissingMarkers.some((marker) => msg.includes(marker))) return "sdk-missing";
7269
+ return "other";
7270
+ }
7271
+ function bridgeWakeLastSeenAgeMs(session, nowMs = Date.now()) {
7272
+ if (!session) return null;
7273
+ return nowMs - session.lastSeenMs;
7274
+ }
7275
+ function shouldSkipBridgeWakeForLiveOperator(session, nowMs = Date.now(), liveWindowMs = BRIDGE_OPERATOR_LIVE_WINDOW_MS) {
7276
+ const ageMs = bridgeWakeLastSeenAgeMs(session, nowMs);
7277
+ return ageMs !== null && ageMs < liveWindowMs;
7278
+ }
7279
+ function composeBridgeWakePrompt(args) {
7280
+ const subject = args.subject ?? "(no subject)";
7281
+ const from = args.from ?? "unknown";
7282
+ const preview = (args.preview ?? "").slice(0, 600);
7283
+ return [
7284
+ `\u{1F380} Bridge mail arrived \u2014 headless wake.`,
7285
+ "",
7286
+ `You are being resumed against your last session because new mail landed in your bridge inbox (${args.bridgeName}@localhost) and you weren't actively at the keyboard.`,
7287
+ "",
7288
+ `Trigger:`,
7289
+ ` UID: ${args.uid}`,
7290
+ ` From: ${from}`,
7291
+ ` Subject: ${subject}`,
7292
+ ` Preview: ${preview}`,
7293
+ "",
7294
+ `Read it with mcp__agenticmail__read_email({ uid: ${args.uid} }) and decide:`,
7295
+ ` \xB7 Does it need a reply from YOU (the operator's session)? Reply via mcp__agenticmail__reply_email.`,
7296
+ ` \xB7 Does it need a teammate to act? Forward / re-route by replying with wake: ["<teammate>"].`,
7297
+ ` \xB7 Is it [NEEDS OPERATOR] / [BLOCKED]? Then it's actually for the human \u2014 mark it unread, and the operator will see it on their next keystroke.`,
7298
+ ` \xB7 Is it FYI noise? mark_read and exit.`,
7299
+ "",
7300
+ `Keep this turn SHORT. You're being resumed to handle ONE piece of mail, not to continue the prior conversation.`
7301
+ ].join("\n");
7302
+ }
7303
+
7236
7304
  // src/util/safe-url.ts
7237
7305
  var UnsafeApiUrlError = class extends Error {
7238
7306
  constructor(raw, reason) {
@@ -8910,6 +8978,7 @@ function parse(raw) {
8910
8978
  AgentDeletionService,
8911
8979
  AgentMemoryStore,
8912
8980
  AgenticMailClient,
8981
+ BRIDGE_OPERATOR_LIVE_WINDOW_MS,
8913
8982
  CloudflareClient,
8914
8983
  DEFAULT_AGENT_NAME,
8915
8984
  DEFAULT_AGENT_ROLE,
@@ -8940,10 +9009,14 @@ function parse(raw) {
8940
9009
  UnsafeApiUrlError,
8941
9010
  WARNING_THRESHOLD,
8942
9011
  assertWithinBase,
9012
+ bridgeWakeErrorMessage,
9013
+ bridgeWakeLastSeenAgeMs,
8943
9014
  buildApiUrl,
8944
9015
  buildInboundSecurityAdvisory,
8945
9016
  classifyEmailRoute,
9017
+ classifyResumeError,
8946
9018
  closeDatabase,
9019
+ composeBridgeWakePrompt,
8947
9020
  createTestDatabase,
8948
9021
  debug,
8949
9022
  debugWarn,
@@ -8979,6 +9052,7 @@ function parse(raw) {
8979
9052
  scoreEmail,
8980
9053
  setOperatorEmail,
8981
9054
  setTelemetryVersion,
9055
+ shouldSkipBridgeWakeForLiveOperator,
8982
9056
  startRelayBridge,
8983
9057
  threadIdFor,
8984
9058
  tryJoin,
package/dist/index.d.cts CHANGED
@@ -481,6 +481,7 @@ interface MailSenderOptions {
481
481
  password: string;
482
482
  authUser?: string;
483
483
  secure?: boolean;
484
+ tlsRejectUnauthorized?: boolean;
484
485
  }
485
486
  interface SendResultWithRaw extends SendResult {
486
487
  /** Raw RFC822 message bytes (for appending to Sent folder) */
@@ -1899,8 +1900,8 @@ declare function operatorPrefsStoragePath(): string;
1899
1900
  *
1900
1901
  * # What this is for
1901
1902
  *
1902
- * When a sub-agent replies into the host bridge inbox
1903
- * (`claudecode@localhost` / `codex@localhost`), the dispatcher
1903
+ * When a sub-agent replies into a host bridge inbox
1904
+ * (`claudecode@localhost` / `codex@localhost` / etc.), the dispatcher
1904
1905
  * historically had no way to react: bridges are skipped by
1905
1906
  * `shouldWatch` because they belong to the human operator's host CLI,
1906
1907
  * not to an automated worker. The mail would sit unread until the
@@ -1933,6 +1934,12 @@ declare function operatorPrefsStoragePath(): string;
1933
1934
  * "sessionId": "019a2b3c-…",
1934
1935
  * "workspace": "/Users/ope/Desktop/facebook-project",
1935
1936
  * "lastSeenMs": 1778905100000
1937
+ * },
1938
+ * "openclaw": {
1939
+ * "sessionId": "openclaw-session-key",
1940
+ * "workspace": "/Users/ope/Desktop/facebook-project",
1941
+ * "lastSeenMs": 1778905000000,
1942
+ * "resumeMode": "wake"
1936
1943
  * }
1937
1944
  * }
1938
1945
  * }
@@ -1963,14 +1970,24 @@ declare function operatorPrefsStoragePath(): string;
1963
1970
  * that crashes the next reader. Same shape as `dispatcher-state.ts`.
1964
1971
  */
1965
1972
  /** Canonical names for the host integrations that own bridge inboxes. */
1966
- type HostName = 'claudecode' | 'codex';
1973
+ type HostName = 'claudecode' | 'codex' | 'openclaw' | 'gemini' | 'hermes';
1974
+ /**
1975
+ * How a host can be woken from a persisted session record.
1976
+ *
1977
+ * - `resume`: the host can resume a durable prior conversation/thread.
1978
+ * - `wake`: the host can target a live or recently known session key, but does
1979
+ * not guarantee full headless resume semantics.
1980
+ * - `wake-only`: the host can receive a wake notification, but the dispatcher
1981
+ * must not treat it as a resumed worker turn.
1982
+ */
1983
+ type HostSessionResumeMode = 'resume' | 'wake' | 'wake-only';
1967
1984
  /**
1968
1985
  * A snapshot of one host CLI's last-known session. Persisted to disk
1969
1986
  * by the mail-hook on every fire; loaded by the dispatcher when
1970
1987
  * bridge mail arrives so a resume can be attempted.
1971
1988
  */
1972
1989
  interface HostSession {
1973
- /** Stable session_id from the host CLI (Claude Code or Codex). */
1990
+ /** Stable session_id/thread_id/session key from the host CLI/runtime. */
1974
1991
  sessionId: string;
1975
1992
  /** Wall-clock timestamp of the last hook fire on this session. */
1976
1993
  lastSeenMs: number;
@@ -1980,6 +1997,10 @@ interface HostSession {
1980
1997
  /** Optional: model name the host session was using, surfaced
1981
1998
  * in logs for diagnostic context. */
1982
1999
  model?: string;
2000
+ /** Optional: describes whether this host supports true resume or only wake. */
2001
+ resumeMode?: HostSessionResumeMode;
2002
+ /** Optional host-specific metadata. Must not contain secrets. */
2003
+ hostMetadata?: Record<string, unknown>;
1983
2004
  }
1984
2005
  /** Default freshness window — sessions older than this are skipped. */
1985
2006
  declare const DEFAULT_SESSION_MAX_AGE_MS: number;
@@ -2017,6 +2038,38 @@ declare function forgetHostSession(host: HostName): void;
2017
2038
  /** Exposed for tests + the `agenticmail status` diagnostic command. */
2018
2039
  declare function hostSessionStoragePath(): string;
2019
2040
 
2041
+ type BridgeWakeError = 'session-expired' | 'sdk-missing' | 'timeout' | 'other';
2042
+ interface BridgeWakeResult {
2043
+ ok: boolean;
2044
+ text?: string;
2045
+ error?: BridgeWakeError;
2046
+ errorMessage?: string;
2047
+ durationMs?: number;
2048
+ }
2049
+ interface BridgeWakePromptArgs {
2050
+ bridgeName: string;
2051
+ uid: number;
2052
+ subject?: string;
2053
+ from?: string;
2054
+ preview?: string;
2055
+ }
2056
+ interface ResumeErrorClassificationOptions {
2057
+ expiredMarkers?: readonly string[];
2058
+ sdkMissingMarkers?: readonly string[];
2059
+ }
2060
+ declare const BRIDGE_OPERATOR_LIVE_WINDOW_MS = 30000;
2061
+ declare function bridgeWakeErrorMessage(err: unknown): string;
2062
+ declare function classifyResumeError(err: unknown, options?: ResumeErrorClassificationOptions): BridgeWakeError;
2063
+ declare function bridgeWakeLastSeenAgeMs(session: Pick<HostSession, 'lastSeenMs'> | null | undefined, nowMs?: number): number | null;
2064
+ declare function shouldSkipBridgeWakeForLiveOperator(session: Pick<HostSession, 'lastSeenMs'> | null | undefined, nowMs?: number, liveWindowMs?: number): boolean;
2065
+ /**
2066
+ * Build the prompt a host session sees on bridge wake. Host adapters
2067
+ * keep their own SDK resume call, but share this operator-facing
2068
+ * instruction shape so Claude Code, Codex, OpenClaw and later hosts
2069
+ * do not drift semantically.
2070
+ */
2071
+ declare function composeBridgeWakePrompt(args: BridgeWakePromptArgs): string;
2072
+
2020
2073
  /**
2021
2074
  * SSRF-safe URL validation for the AgenticMail API base URL.
2022
2075
  *
@@ -2624,4 +2677,4 @@ declare class AgentMemoryStore {
2624
2677
  renderForPrompt(memory: AgentMemoryRead | null): string;
2625
2678
  }
2626
2679
 
2627
- 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, DEFAULT_SESSION_MAX_AGE_MS, 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 HostName, type HostSession, type InboundEmail, type InboundSmsEvent, 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, PathTraversalError, type PurchasedDomain, REDACTED, RELAY_PRESETS, RelayBridge, type RelayBridgeOptions, type RelayConfig, RelayGateway, type RelayProvider, type RelaySearchResult, SPAM_THRESHOLD, type SafeJoinOptions, type SanitizeDetection, type SanitizeResult, type SearchCriteria, type SearchableEmail, type SecurityAdvisory, type SendMailOptions, type SendResult, type SendResultWithRaw, type SendSmsInput, type SendSmsResult, ServiceManager, type ServiceStatus, type SetupConfig, SetupManager, type SetupResult, type Severity, type SmsConfig, SmsManager, type SmsMessage, SmsPoller, type SmsProvider, type SpamCategory, type SpamResult, type SpamRuleMatch, StalwartAdmin, type StalwartAdminOptions, type StalwartPrincipal, ThreadCache, type ThreadCacheEntry, type ThreadCacheOptions, type ThreadIdInput, type TunnelConfig, TunnelManager, UnsafeApiUrlError, WARNING_THRESHOLD, type WatcherOptions, assertWithinBase, buildApiUrl, buildInboundSecurityAdvisory, classifyEmailRoute, closeDatabase, createTestDatabase, debug, debugWarn, ensureDataDir, extractVerificationCode, flushTelemetry, forgetHostSession, getDatabase, getOperatorEmail, getSmsProvider, hostSessionStoragePath, isInternalEmail, isSessionFresh, isValidPhoneNumber, loadHostSession, mapProviderSmsStatus, normalizeAddress, normalizePhoneNumber, normalizeSubject, operatorPrefsStoragePath, parseEmail, parseGoogleVoiceSms, recordToolCall, redactObject, redactSecret, redactSmsConfig, resolveConfig, safeJoin, sanitizeEmail, saveConfig, saveHostSession, scanOutboundEmail, scoreEmail, setOperatorEmail, setTelemetryVersion, startRelayBridge, threadIdFor, tryJoin, validateApiUrl };
2680
+ 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, BRIDGE_OPERATOR_LIVE_WINDOW_MS, type BridgeWakeError, type BridgeWakePromptArgs, type BridgeWakeResult, type CachedMessage, CloudflareClient, type CreateAgentOptions, DEFAULT_AGENT_NAME, DEFAULT_AGENT_ROLE, DEFAULT_SESSION_MAX_AGE_MS, 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 HostName, type HostSession, type HostSessionResumeMode, type InboundEmail, type InboundSmsEvent, 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, PathTraversalError, type PurchasedDomain, REDACTED, RELAY_PRESETS, RelayBridge, type RelayBridgeOptions, type RelayConfig, RelayGateway, type RelayProvider, type RelaySearchResult, type ResumeErrorClassificationOptions, SPAM_THRESHOLD, type SafeJoinOptions, type SanitizeDetection, type SanitizeResult, type SearchCriteria, type SearchableEmail, type SecurityAdvisory, type SendMailOptions, type SendResult, type SendResultWithRaw, type SendSmsInput, type SendSmsResult, ServiceManager, type ServiceStatus, type SetupConfig, SetupManager, type SetupResult, type Severity, type SmsConfig, SmsManager, type SmsMessage, SmsPoller, type SmsProvider, type SpamCategory, type SpamResult, type SpamRuleMatch, StalwartAdmin, type StalwartAdminOptions, type StalwartPrincipal, ThreadCache, type ThreadCacheEntry, type ThreadCacheOptions, type ThreadIdInput, type TunnelConfig, TunnelManager, UnsafeApiUrlError, WARNING_THRESHOLD, type WatcherOptions, assertWithinBase, bridgeWakeErrorMessage, bridgeWakeLastSeenAgeMs, buildApiUrl, buildInboundSecurityAdvisory, classifyEmailRoute, classifyResumeError, closeDatabase, composeBridgeWakePrompt, createTestDatabase, debug, debugWarn, ensureDataDir, extractVerificationCode, flushTelemetry, forgetHostSession, getDatabase, getOperatorEmail, getSmsProvider, hostSessionStoragePath, isInternalEmail, isSessionFresh, isValidPhoneNumber, loadHostSession, mapProviderSmsStatus, normalizeAddress, normalizePhoneNumber, normalizeSubject, operatorPrefsStoragePath, parseEmail, parseGoogleVoiceSms, recordToolCall, redactObject, redactSecret, redactSmsConfig, resolveConfig, safeJoin, sanitizeEmail, saveConfig, saveHostSession, scanOutboundEmail, scoreEmail, setOperatorEmail, setTelemetryVersion, shouldSkipBridgeWakeForLiveOperator, startRelayBridge, threadIdFor, tryJoin, validateApiUrl };
package/dist/index.d.ts CHANGED
@@ -481,6 +481,7 @@ interface MailSenderOptions {
481
481
  password: string;
482
482
  authUser?: string;
483
483
  secure?: boolean;
484
+ tlsRejectUnauthorized?: boolean;
484
485
  }
485
486
  interface SendResultWithRaw extends SendResult {
486
487
  /** Raw RFC822 message bytes (for appending to Sent folder) */
@@ -1899,8 +1900,8 @@ declare function operatorPrefsStoragePath(): string;
1899
1900
  *
1900
1901
  * # What this is for
1901
1902
  *
1902
- * When a sub-agent replies into the host bridge inbox
1903
- * (`claudecode@localhost` / `codex@localhost`), the dispatcher
1903
+ * When a sub-agent replies into a host bridge inbox
1904
+ * (`claudecode@localhost` / `codex@localhost` / etc.), the dispatcher
1904
1905
  * historically had no way to react: bridges are skipped by
1905
1906
  * `shouldWatch` because they belong to the human operator's host CLI,
1906
1907
  * not to an automated worker. The mail would sit unread until the
@@ -1933,6 +1934,12 @@ declare function operatorPrefsStoragePath(): string;
1933
1934
  * "sessionId": "019a2b3c-…",
1934
1935
  * "workspace": "/Users/ope/Desktop/facebook-project",
1935
1936
  * "lastSeenMs": 1778905100000
1937
+ * },
1938
+ * "openclaw": {
1939
+ * "sessionId": "openclaw-session-key",
1940
+ * "workspace": "/Users/ope/Desktop/facebook-project",
1941
+ * "lastSeenMs": 1778905000000,
1942
+ * "resumeMode": "wake"
1936
1943
  * }
1937
1944
  * }
1938
1945
  * }
@@ -1963,14 +1970,24 @@ declare function operatorPrefsStoragePath(): string;
1963
1970
  * that crashes the next reader. Same shape as `dispatcher-state.ts`.
1964
1971
  */
1965
1972
  /** Canonical names for the host integrations that own bridge inboxes. */
1966
- type HostName = 'claudecode' | 'codex';
1973
+ type HostName = 'claudecode' | 'codex' | 'openclaw' | 'gemini' | 'hermes';
1974
+ /**
1975
+ * How a host can be woken from a persisted session record.
1976
+ *
1977
+ * - `resume`: the host can resume a durable prior conversation/thread.
1978
+ * - `wake`: the host can target a live or recently known session key, but does
1979
+ * not guarantee full headless resume semantics.
1980
+ * - `wake-only`: the host can receive a wake notification, but the dispatcher
1981
+ * must not treat it as a resumed worker turn.
1982
+ */
1983
+ type HostSessionResumeMode = 'resume' | 'wake' | 'wake-only';
1967
1984
  /**
1968
1985
  * A snapshot of one host CLI's last-known session. Persisted to disk
1969
1986
  * by the mail-hook on every fire; loaded by the dispatcher when
1970
1987
  * bridge mail arrives so a resume can be attempted.
1971
1988
  */
1972
1989
  interface HostSession {
1973
- /** Stable session_id from the host CLI (Claude Code or Codex). */
1990
+ /** Stable session_id/thread_id/session key from the host CLI/runtime. */
1974
1991
  sessionId: string;
1975
1992
  /** Wall-clock timestamp of the last hook fire on this session. */
1976
1993
  lastSeenMs: number;
@@ -1980,6 +1997,10 @@ interface HostSession {
1980
1997
  /** Optional: model name the host session was using, surfaced
1981
1998
  * in logs for diagnostic context. */
1982
1999
  model?: string;
2000
+ /** Optional: describes whether this host supports true resume or only wake. */
2001
+ resumeMode?: HostSessionResumeMode;
2002
+ /** Optional host-specific metadata. Must not contain secrets. */
2003
+ hostMetadata?: Record<string, unknown>;
1983
2004
  }
1984
2005
  /** Default freshness window — sessions older than this are skipped. */
1985
2006
  declare const DEFAULT_SESSION_MAX_AGE_MS: number;
@@ -2017,6 +2038,38 @@ declare function forgetHostSession(host: HostName): void;
2017
2038
  /** Exposed for tests + the `agenticmail status` diagnostic command. */
2018
2039
  declare function hostSessionStoragePath(): string;
2019
2040
 
2041
+ type BridgeWakeError = 'session-expired' | 'sdk-missing' | 'timeout' | 'other';
2042
+ interface BridgeWakeResult {
2043
+ ok: boolean;
2044
+ text?: string;
2045
+ error?: BridgeWakeError;
2046
+ errorMessage?: string;
2047
+ durationMs?: number;
2048
+ }
2049
+ interface BridgeWakePromptArgs {
2050
+ bridgeName: string;
2051
+ uid: number;
2052
+ subject?: string;
2053
+ from?: string;
2054
+ preview?: string;
2055
+ }
2056
+ interface ResumeErrorClassificationOptions {
2057
+ expiredMarkers?: readonly string[];
2058
+ sdkMissingMarkers?: readonly string[];
2059
+ }
2060
+ declare const BRIDGE_OPERATOR_LIVE_WINDOW_MS = 30000;
2061
+ declare function bridgeWakeErrorMessage(err: unknown): string;
2062
+ declare function classifyResumeError(err: unknown, options?: ResumeErrorClassificationOptions): BridgeWakeError;
2063
+ declare function bridgeWakeLastSeenAgeMs(session: Pick<HostSession, 'lastSeenMs'> | null | undefined, nowMs?: number): number | null;
2064
+ declare function shouldSkipBridgeWakeForLiveOperator(session: Pick<HostSession, 'lastSeenMs'> | null | undefined, nowMs?: number, liveWindowMs?: number): boolean;
2065
+ /**
2066
+ * Build the prompt a host session sees on bridge wake. Host adapters
2067
+ * keep their own SDK resume call, but share this operator-facing
2068
+ * instruction shape so Claude Code, Codex, OpenClaw and later hosts
2069
+ * do not drift semantically.
2070
+ */
2071
+ declare function composeBridgeWakePrompt(args: BridgeWakePromptArgs): string;
2072
+
2020
2073
  /**
2021
2074
  * SSRF-safe URL validation for the AgenticMail API base URL.
2022
2075
  *
@@ -2624,4 +2677,4 @@ declare class AgentMemoryStore {
2624
2677
  renderForPrompt(memory: AgentMemoryRead | null): string;
2625
2678
  }
2626
2679
 
2627
- 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, DEFAULT_SESSION_MAX_AGE_MS, 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 HostName, type HostSession, type InboundEmail, type InboundSmsEvent, 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, PathTraversalError, type PurchasedDomain, REDACTED, RELAY_PRESETS, RelayBridge, type RelayBridgeOptions, type RelayConfig, RelayGateway, type RelayProvider, type RelaySearchResult, SPAM_THRESHOLD, type SafeJoinOptions, type SanitizeDetection, type SanitizeResult, type SearchCriteria, type SearchableEmail, type SecurityAdvisory, type SendMailOptions, type SendResult, type SendResultWithRaw, type SendSmsInput, type SendSmsResult, ServiceManager, type ServiceStatus, type SetupConfig, SetupManager, type SetupResult, type Severity, type SmsConfig, SmsManager, type SmsMessage, SmsPoller, type SmsProvider, type SpamCategory, type SpamResult, type SpamRuleMatch, StalwartAdmin, type StalwartAdminOptions, type StalwartPrincipal, ThreadCache, type ThreadCacheEntry, type ThreadCacheOptions, type ThreadIdInput, type TunnelConfig, TunnelManager, UnsafeApiUrlError, WARNING_THRESHOLD, type WatcherOptions, assertWithinBase, buildApiUrl, buildInboundSecurityAdvisory, classifyEmailRoute, closeDatabase, createTestDatabase, debug, debugWarn, ensureDataDir, extractVerificationCode, flushTelemetry, forgetHostSession, getDatabase, getOperatorEmail, getSmsProvider, hostSessionStoragePath, isInternalEmail, isSessionFresh, isValidPhoneNumber, loadHostSession, mapProviderSmsStatus, normalizeAddress, normalizePhoneNumber, normalizeSubject, operatorPrefsStoragePath, parseEmail, parseGoogleVoiceSms, recordToolCall, redactObject, redactSecret, redactSmsConfig, resolveConfig, safeJoin, sanitizeEmail, saveConfig, saveHostSession, scanOutboundEmail, scoreEmail, setOperatorEmail, setTelemetryVersion, startRelayBridge, threadIdFor, tryJoin, validateApiUrl };
2680
+ 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, BRIDGE_OPERATOR_LIVE_WINDOW_MS, type BridgeWakeError, type BridgeWakePromptArgs, type BridgeWakeResult, type CachedMessage, CloudflareClient, type CreateAgentOptions, DEFAULT_AGENT_NAME, DEFAULT_AGENT_ROLE, DEFAULT_SESSION_MAX_AGE_MS, 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 HostName, type HostSession, type HostSessionResumeMode, type InboundEmail, type InboundSmsEvent, 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, PathTraversalError, type PurchasedDomain, REDACTED, RELAY_PRESETS, RelayBridge, type RelayBridgeOptions, type RelayConfig, RelayGateway, type RelayProvider, type RelaySearchResult, type ResumeErrorClassificationOptions, SPAM_THRESHOLD, type SafeJoinOptions, type SanitizeDetection, type SanitizeResult, type SearchCriteria, type SearchableEmail, type SecurityAdvisory, type SendMailOptions, type SendResult, type SendResultWithRaw, type SendSmsInput, type SendSmsResult, ServiceManager, type ServiceStatus, type SetupConfig, SetupManager, type SetupResult, type Severity, type SmsConfig, SmsManager, type SmsMessage, SmsPoller, type SmsProvider, type SpamCategory, type SpamResult, type SpamRuleMatch, StalwartAdmin, type StalwartAdminOptions, type StalwartPrincipal, ThreadCache, type ThreadCacheEntry, type ThreadCacheOptions, type ThreadIdInput, type TunnelConfig, TunnelManager, UnsafeApiUrlError, WARNING_THRESHOLD, type WatcherOptions, assertWithinBase, bridgeWakeErrorMessage, bridgeWakeLastSeenAgeMs, buildApiUrl, buildInboundSecurityAdvisory, classifyEmailRoute, classifyResumeError, closeDatabase, composeBridgeWakePrompt, createTestDatabase, debug, debugWarn, ensureDataDir, extractVerificationCode, flushTelemetry, forgetHostSession, getDatabase, getOperatorEmail, getSmsProvider, hostSessionStoragePath, isInternalEmail, isSessionFresh, isValidPhoneNumber, loadHostSession, mapProviderSmsStatus, normalizeAddress, normalizePhoneNumber, normalizeSubject, operatorPrefsStoragePath, parseEmail, parseGoogleVoiceSms, recordToolCall, redactObject, redactSecret, redactSmsConfig, resolveConfig, safeJoin, sanitizeEmail, saveConfig, saveHostSession, scanOutboundEmail, scoreEmail, setOperatorEmail, setTelemetryVersion, shouldSkipBridgeWakeForLiveOperator, startRelayBridge, threadIdFor, tryJoin, validateApiUrl };
package/dist/index.js CHANGED
@@ -24,8 +24,7 @@ var MailSender = class {
24
24
  pass: options.password
25
25
  },
26
26
  tls: {
27
- rejectUnauthorized: false
28
- // Local dev — no TLS
27
+ rejectUnauthorized: options.tlsRejectUnauthorized ?? true
29
28
  },
30
29
  connectionTimeout: 1e4,
31
30
  // 10s to establish TCP connection
@@ -6447,6 +6446,69 @@ function hostSessionStoragePath() {
6447
6446
  return storagePath();
6448
6447
  }
6449
6448
 
6449
+ // src/host-bridge.ts
6450
+ var BRIDGE_OPERATOR_LIVE_WINDOW_MS = 3e4;
6451
+ var DEFAULT_EXPIRED_MARKERS = [
6452
+ "session not found",
6453
+ "invalid session",
6454
+ "session expired",
6455
+ "no such session",
6456
+ "unknown session",
6457
+ "thread not found",
6458
+ "invalid thread",
6459
+ "thread expired",
6460
+ "no such thread",
6461
+ "unknown thread"
6462
+ ];
6463
+ var DEFAULT_SDK_MISSING_MARKERS = [
6464
+ "cannot find module",
6465
+ "could not be found",
6466
+ "command not found"
6467
+ ];
6468
+ function bridgeWakeErrorMessage(err) {
6469
+ return err?.message ?? String(err);
6470
+ }
6471
+ function classifyResumeError(err, options = {}) {
6472
+ const msg = bridgeWakeErrorMessage(err).toLowerCase();
6473
+ const expiredMarkers = options.expiredMarkers ?? DEFAULT_EXPIRED_MARKERS;
6474
+ const sdkMissingMarkers = options.sdkMissingMarkers ?? DEFAULT_SDK_MISSING_MARKERS;
6475
+ if (expiredMarkers.some((marker) => msg.includes(marker))) return "session-expired";
6476
+ if (sdkMissingMarkers.some((marker) => msg.includes(marker))) return "sdk-missing";
6477
+ return "other";
6478
+ }
6479
+ function bridgeWakeLastSeenAgeMs(session, nowMs = Date.now()) {
6480
+ if (!session) return null;
6481
+ return nowMs - session.lastSeenMs;
6482
+ }
6483
+ function shouldSkipBridgeWakeForLiveOperator(session, nowMs = Date.now(), liveWindowMs = BRIDGE_OPERATOR_LIVE_WINDOW_MS) {
6484
+ const ageMs = bridgeWakeLastSeenAgeMs(session, nowMs);
6485
+ return ageMs !== null && ageMs < liveWindowMs;
6486
+ }
6487
+ function composeBridgeWakePrompt(args) {
6488
+ const subject = args.subject ?? "(no subject)";
6489
+ const from = args.from ?? "unknown";
6490
+ const preview = (args.preview ?? "").slice(0, 600);
6491
+ return [
6492
+ `\u{1F380} Bridge mail arrived \u2014 headless wake.`,
6493
+ "",
6494
+ `You are being resumed against your last session because new mail landed in your bridge inbox (${args.bridgeName}@localhost) and you weren't actively at the keyboard.`,
6495
+ "",
6496
+ `Trigger:`,
6497
+ ` UID: ${args.uid}`,
6498
+ ` From: ${from}`,
6499
+ ` Subject: ${subject}`,
6500
+ ` Preview: ${preview}`,
6501
+ "",
6502
+ `Read it with mcp__agenticmail__read_email({ uid: ${args.uid} }) and decide:`,
6503
+ ` \xB7 Does it need a reply from YOU (the operator's session)? Reply via mcp__agenticmail__reply_email.`,
6504
+ ` \xB7 Does it need a teammate to act? Forward / re-route by replying with wake: ["<teammate>"].`,
6505
+ ` \xB7 Is it [NEEDS OPERATOR] / [BLOCKED]? Then it's actually for the human \u2014 mark it unread, and the operator will see it on their next keystroke.`,
6506
+ ` \xB7 Is it FYI noise? mark_read and exit.`,
6507
+ "",
6508
+ `Keep this turn SHORT. You're being resumed to handle ONE piece of mail, not to continue the prior conversation.`
6509
+ ].join("\n");
6510
+ }
6511
+
6450
6512
  // src/util/safe-url.ts
6451
6513
  var UnsafeApiUrlError = class extends Error {
6452
6514
  constructor(raw, reason) {
@@ -8138,6 +8200,7 @@ export {
8138
8200
  AgentDeletionService,
8139
8201
  AgentMemoryStore,
8140
8202
  AgenticMailClient,
8203
+ BRIDGE_OPERATOR_LIVE_WINDOW_MS,
8141
8204
  CloudflareClient,
8142
8205
  DEFAULT_AGENT_NAME,
8143
8206
  DEFAULT_AGENT_ROLE,
@@ -8168,10 +8231,14 @@ export {
8168
8231
  UnsafeApiUrlError,
8169
8232
  WARNING_THRESHOLD,
8170
8233
  assertWithinBase,
8234
+ bridgeWakeErrorMessage,
8235
+ bridgeWakeLastSeenAgeMs,
8171
8236
  buildApiUrl,
8172
8237
  buildInboundSecurityAdvisory,
8173
8238
  classifyEmailRoute,
8239
+ classifyResumeError,
8174
8240
  closeDatabase,
8241
+ composeBridgeWakePrompt,
8175
8242
  createTestDatabase,
8176
8243
  debug,
8177
8244
  debugWarn,
@@ -8207,6 +8274,7 @@ export {
8207
8274
  scoreEmail,
8208
8275
  setOperatorEmail,
8209
8276
  setTelemetryVersion,
8277
+ shouldSkipBridgeWakeForLiveOperator,
8210
8278
  startRelayBridge,
8211
8279
  threadIdFor,
8212
8280
  tryJoin,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agenticmail/core",
3
- "version": "0.9.9",
3
+ "version": "0.9.10",
4
4
  "description": "Core SDK for AgenticMail — email, SMS, and phone number access for AI agents",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",