@agenticmail/core 0.9.8 → 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.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
@@ -3097,7 +3096,47 @@ var DomainManager = class {
3097
3096
 
3098
3097
  // src/gateway/manager.ts
3099
3098
  import { join as join4 } from "path";
3099
+
3100
+ // src/crypto/secrets.ts
3100
3101
  import { createCipheriv, createDecipheriv, randomBytes as randomBytes2, createHash, scryptSync } from "crypto";
3102
+ function deriveKey(key, salt) {
3103
+ return scryptSync(key, salt, 32, { N: 16384, r: 8, p: 1 });
3104
+ }
3105
+ function encryptSecret(plaintext, key) {
3106
+ const salt = randomBytes2(16);
3107
+ const derivedKey = deriveKey(key, salt);
3108
+ const iv = randomBytes2(12);
3109
+ const cipher = createCipheriv("aes-256-gcm", derivedKey, iv);
3110
+ const encrypted = Buffer.concat([cipher.update(plaintext, "utf8"), cipher.final()]);
3111
+ const authTag = cipher.getAuthTag();
3112
+ return `enc2:${salt.toString("hex")}:${iv.toString("hex")}:${authTag.toString("hex")}:${encrypted.toString("hex")}`;
3113
+ }
3114
+ function decryptSecret(value, key) {
3115
+ if (value.startsWith("enc2:")) {
3116
+ const parts = value.split(":");
3117
+ if (parts.length !== 5) return value;
3118
+ const [, saltHex, ivHex, authTagHex, ciphertextHex] = parts;
3119
+ const derivedKey = deriveKey(key, Buffer.from(saltHex, "hex"));
3120
+ const decipher = createDecipheriv("aes-256-gcm", derivedKey, Buffer.from(ivHex, "hex"));
3121
+ decipher.setAuthTag(Buffer.from(authTagHex, "hex"));
3122
+ return Buffer.concat([decipher.update(Buffer.from(ciphertextHex, "hex")), decipher.final()]).toString("utf8");
3123
+ }
3124
+ if (value.startsWith("enc:")) {
3125
+ const parts = value.split(":");
3126
+ if (parts.length !== 4) return value;
3127
+ const [, ivHex, authTagHex, ciphertextHex] = parts;
3128
+ const keyHash = createHash("sha256").update(key).digest();
3129
+ const decipher = createDecipheriv("aes-256-gcm", keyHash, Buffer.from(ivHex, "hex"));
3130
+ decipher.setAuthTag(Buffer.from(authTagHex, "hex"));
3131
+ return Buffer.concat([decipher.update(Buffer.from(ciphertextHex, "hex")), decipher.final()]).toString("utf8");
3132
+ }
3133
+ return value;
3134
+ }
3135
+ function isEncryptedSecret(value) {
3136
+ return typeof value === "string" && (value.startsWith("enc2:") || value.startsWith("enc:"));
3137
+ }
3138
+
3139
+ // src/gateway/manager.ts
3101
3140
  import nodemailer3 from "nodemailer";
3102
3141
 
3103
3142
  // src/debug.ts
@@ -4374,6 +4413,130 @@ var TunnelManager = class {
4374
4413
  import MailComposer3 from "nodemailer/lib/mail-composer/index.js";
4375
4414
 
4376
4415
  // src/sms/manager.ts
4416
+ function asString(value) {
4417
+ return typeof value === "string" ? value.trim() : "";
4418
+ }
4419
+ function defaultApiUrl(config) {
4420
+ const url = (config.apiUrl || "https://api.46elks.com/a1").replace(/\/+$/, "");
4421
+ if (!/^https:\/\//i.test(url)) {
4422
+ throw new Error("46elks apiUrl must use https:// \u2014 refusing to send credentials over a non-TLS connection");
4423
+ }
4424
+ return url;
4425
+ }
4426
+ function basicAuth(username, password) {
4427
+ return Buffer.from(`${username}:${password}`, "utf8").toString("base64");
4428
+ }
4429
+ function redactSmsConfig(config) {
4430
+ return {
4431
+ ...config,
4432
+ forwardingPassword: config.forwardingPassword ? "***" : void 0,
4433
+ password: config.password ? "***" : void 0,
4434
+ webhookSecret: config.webhookSecret ? "***" : void 0
4435
+ };
4436
+ }
4437
+ var GoogleVoiceSmsProvider = class {
4438
+ id = "google_voice";
4439
+ async sendSms(config, input) {
4440
+ const to = normalizePhoneNumber(input.to);
4441
+ const from = normalizePhoneNumber(config.phoneNumber);
4442
+ if (!to) throw new Error("Invalid recipient phone number");
4443
+ if (!from) throw new Error("Invalid configured Google Voice phone number");
4444
+ return {
4445
+ provider: this.id,
4446
+ status: "pending",
4447
+ from,
4448
+ to,
4449
+ body: input.body,
4450
+ raw: {
4451
+ delivery: "manual_google_voice_web",
4452
+ url: "https://voice.google.com"
4453
+ }
4454
+ };
4455
+ }
4456
+ parseInboundSms() {
4457
+ return null;
4458
+ }
4459
+ };
4460
+ var FortySixElksSmsProvider = class {
4461
+ id = "46elks";
4462
+ async sendSms(config, input) {
4463
+ const username = asString(config.username);
4464
+ const password = asString(config.password);
4465
+ if (!username || !password) {
4466
+ throw new Error("46elks username and password are required");
4467
+ }
4468
+ const to = normalizePhoneNumber(input.to);
4469
+ const from = normalizePhoneNumber(config.phoneNumber);
4470
+ if (!to) throw new Error("Invalid recipient phone number");
4471
+ if (!from) throw new Error("Invalid configured 46elks phone number");
4472
+ const form = new URLSearchParams();
4473
+ form.set("to", to);
4474
+ form.set("from", from);
4475
+ form.set("message", input.body);
4476
+ if (input.dryRun) form.set("dryrun", "yes");
4477
+ const response = await fetch(`${defaultApiUrl(config)}/sms`, {
4478
+ method: "POST",
4479
+ headers: {
4480
+ "Authorization": `Basic ${basicAuth(username, password)}`,
4481
+ "Content-Type": "application/x-www-form-urlencoded"
4482
+ },
4483
+ body: form,
4484
+ signal: AbortSignal.timeout(15e3)
4485
+ });
4486
+ const text = await response.text();
4487
+ let raw = text;
4488
+ try {
4489
+ raw = JSON.parse(text);
4490
+ } catch {
4491
+ }
4492
+ if (!response.ok) {
4493
+ const message = typeof raw === "object" && raw && ("message" in raw || "error" in raw) ? String(raw.message ?? raw.error) : text.slice(0, 200);
4494
+ throw new Error(`46elks SMS failed (${response.status}): ${message}`);
4495
+ }
4496
+ const providerId = typeof raw === "object" && raw && "id" in raw ? String(raw.id) : void 0;
4497
+ const providerStatus = typeof raw === "object" && raw && "status" in raw ? String(raw.status) : "sent";
4498
+ return {
4499
+ provider: this.id,
4500
+ id: providerId,
4501
+ status: providerStatus,
4502
+ from,
4503
+ to,
4504
+ body: input.body,
4505
+ raw
4506
+ };
4507
+ }
4508
+ parseInboundSms(payload) {
4509
+ const direction = asString(payload.direction).toLowerCase();
4510
+ if (direction && direction !== "incoming") return null;
4511
+ const from = normalizePhoneNumber(asString(payload.from));
4512
+ const to = normalizePhoneNumber(asString(payload.to));
4513
+ const body = asString(payload.message);
4514
+ if (!from || !to || !body) return null;
4515
+ return {
4516
+ provider: this.id,
4517
+ id: asString(payload.id) || void 0,
4518
+ from,
4519
+ to,
4520
+ body,
4521
+ timestamp: asString(payload.created) || (/* @__PURE__ */ new Date()).toISOString(),
4522
+ raw: payload
4523
+ };
4524
+ }
4525
+ };
4526
+ var PROVIDERS = {
4527
+ google_voice: new GoogleVoiceSmsProvider(),
4528
+ "46elks": new FortySixElksSmsProvider()
4529
+ };
4530
+ function getSmsProvider(provider) {
4531
+ return PROVIDERS[provider];
4532
+ }
4533
+ function mapProviderSmsStatus(status) {
4534
+ const normalized = status.toLowerCase();
4535
+ if (normalized === "delivered") return "delivered";
4536
+ if (normalized === "failed" || normalized === "error") return "failed";
4537
+ if (normalized === "created" || normalized === "queued" || normalized === "sent") return "sent";
4538
+ return "sent";
4539
+ }
4377
4540
  function normalizePhoneNumber(raw) {
4378
4541
  const cleaned = raw.replace(/[^+\d]/g, "");
4379
4542
  if (!cleaned) return null;
@@ -4480,12 +4643,48 @@ function extractVerificationCode(smsBody) {
4480
4643
  }
4481
4644
  return null;
4482
4645
  }
4646
+ var SMS_SECRET_FIELDS = ["password", "webhookSecret", "forwardingPassword"];
4483
4647
  var SmsManager = class {
4484
- constructor(db2) {
4648
+ /**
4649
+ * Optional master key used to encrypt SMS credentials at rest (same
4650
+ * AES-256-GCM scheme GatewayManager uses for relay/domain secrets).
4651
+ * When absent (e.g. tests, or a deployment with no master key) configs
4652
+ * are stored as-is and reads tolerate plaintext — so upgrades and
4653
+ * downgrades both stay safe.
4654
+ */
4655
+ constructor(db2, encryptionKey) {
4485
4656
  this.db = db2;
4657
+ this.encryptionKey = encryptionKey;
4486
4658
  this.ensureTable();
4487
4659
  }
4488
4660
  initialized = false;
4661
+ /** Encrypt the credential fields of an SMS config before persisting. */
4662
+ encryptConfig(config) {
4663
+ if (!this.encryptionKey) return config;
4664
+ const out = { ...config };
4665
+ for (const field of SMS_SECRET_FIELDS) {
4666
+ const value = out[field];
4667
+ if (typeof value === "string" && value && !isEncryptedSecret(value)) {
4668
+ out[field] = encryptSecret(value, this.encryptionKey);
4669
+ }
4670
+ }
4671
+ return out;
4672
+ }
4673
+ /** Decrypt the credential fields of an SMS config after loading. */
4674
+ decryptConfig(config) {
4675
+ if (!this.encryptionKey) return config;
4676
+ const out = { ...config };
4677
+ for (const field of SMS_SECRET_FIELDS) {
4678
+ const value = out[field];
4679
+ if (typeof value === "string" && isEncryptedSecret(value)) {
4680
+ try {
4681
+ out[field] = decryptSecret(value, this.encryptionKey);
4682
+ } catch {
4683
+ }
4684
+ }
4685
+ }
4686
+ return out;
4687
+ }
4489
4688
  ensureTable() {
4490
4689
  if (this.initialized) return;
4491
4690
  try {
@@ -4518,18 +4717,19 @@ var SmsManager = class {
4518
4717
  this.initialized = true;
4519
4718
  }
4520
4719
  }
4521
- /** Get SMS config from agent metadata */
4720
+ /** Get SMS config from agent metadata (credential fields decrypted). */
4522
4721
  getSmsConfig(agentId) {
4523
4722
  const row = this.db.prepare("SELECT metadata FROM agents WHERE id = ?").get(agentId);
4524
4723
  if (!row) return null;
4525
4724
  try {
4526
4725
  const meta = JSON.parse(row.metadata || "{}");
4527
- return meta.sms && meta.sms.enabled !== void 0 ? meta.sms : null;
4726
+ if (!meta.sms || meta.sms.enabled === void 0) return null;
4727
+ return this.decryptConfig(meta.sms);
4528
4728
  } catch {
4529
4729
  return null;
4530
4730
  }
4531
4731
  }
4532
- /** Save SMS config to agent metadata */
4732
+ /** Save SMS config to agent metadata (credential fields encrypted). */
4533
4733
  saveSmsConfig(agentId, config) {
4534
4734
  const row = this.db.prepare("SELECT metadata FROM agents WHERE id = ?").get(agentId);
4535
4735
  if (!row) throw new Error(`Agent ${agentId} not found`);
@@ -4539,7 +4739,7 @@ var SmsManager = class {
4539
4739
  } catch {
4540
4740
  meta = {};
4541
4741
  }
4542
- meta.sms = config;
4742
+ meta.sms = this.encryptConfig(config);
4543
4743
  this.db.prepare("UPDATE agents SET metadata = ?, updated_at = datetime('now') WHERE id = ?").run(JSON.stringify(meta), agentId);
4544
4744
  }
4545
4745
  /**
@@ -4582,27 +4782,50 @@ var SmsManager = class {
4582
4782
  delete meta.sms;
4583
4783
  this.db.prepare("UPDATE agents SET metadata = ?, updated_at = datetime('now') WHERE id = ?").run(JSON.stringify(meta), agentId);
4584
4784
  }
4585
- /** Record an inbound SMS (parsed from email) */
4586
- recordInbound(agentId, parsed) {
4785
+ /** Find the agent whose SMS config owns a phone number. */
4786
+ findAgentBySmsNumber(phoneNumber, provider) {
4787
+ const normalized = normalizePhoneNumber(phoneNumber);
4788
+ if (!normalized) return null;
4789
+ const rows = this.db.prepare("SELECT id, metadata FROM agents").all();
4790
+ for (const row of rows) {
4791
+ try {
4792
+ const meta = JSON.parse(row.metadata || "{}");
4793
+ const cfg = meta.sms;
4794
+ if (!cfg?.enabled) continue;
4795
+ if (provider && cfg.provider !== provider) continue;
4796
+ if (normalizePhoneNumber(cfg.phoneNumber) === normalized) {
4797
+ return { agentId: row.id, config: this.decryptConfig(cfg) };
4798
+ }
4799
+ } catch {
4800
+ }
4801
+ }
4802
+ return null;
4803
+ }
4804
+ /** Record an inbound SMS (parsed from email or provider webhook) */
4805
+ recordInbound(agentId, parsed, metadata) {
4587
4806
  const id = `sms_in_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
4588
4807
  const createdAt = parsed.timestamp || (/* @__PURE__ */ new Date()).toISOString();
4589
4808
  this.db.prepare(
4590
- "INSERT INTO sms_messages (id, agent_id, direction, phone_number, body, status, created_at) VALUES (?, ?, ?, ?, ?, ?, ?)"
4591
- ).run(id, agentId, "inbound", parsed.from, parsed.body, "received", createdAt);
4592
- return { id, agentId, direction: "inbound", phoneNumber: parsed.from, body: parsed.body, status: "received", createdAt };
4809
+ "INSERT INTO sms_messages (id, agent_id, direction, phone_number, body, status, created_at, metadata) VALUES (?, ?, ?, ?, ?, ?, ?, ?)"
4810
+ ).run(id, agentId, "inbound", parsed.from, parsed.body, "received", createdAt, JSON.stringify(metadata ?? {}));
4811
+ return { id, agentId, direction: "inbound", phoneNumber: parsed.from, body: parsed.body, status: "received", createdAt, metadata };
4593
4812
  }
4594
4813
  /** Record an outbound SMS attempt */
4595
- recordOutbound(agentId, phoneNumber, body, status = "pending") {
4814
+ recordOutbound(agentId, phoneNumber, body, status = "pending", metadata) {
4596
4815
  const id = `sms_out_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
4597
4816
  const now = (/* @__PURE__ */ new Date()).toISOString();
4598
4817
  const normalized = normalizePhoneNumber(phoneNumber) || phoneNumber;
4599
4818
  this.db.prepare(
4600
- "INSERT INTO sms_messages (id, agent_id, direction, phone_number, body, status, created_at) VALUES (?, ?, ?, ?, ?, ?, ?)"
4601
- ).run(id, agentId, "outbound", normalized, body, status, now);
4602
- return { id, agentId, direction: "outbound", phoneNumber: normalized, body, status, createdAt: now };
4603
- }
4604
- /** Update SMS status */
4605
- updateStatus(id, status) {
4819
+ "INSERT INTO sms_messages (id, agent_id, direction, phone_number, body, status, created_at, metadata) VALUES (?, ?, ?, ?, ?, ?, ?, ?)"
4820
+ ).run(id, agentId, "outbound", normalized, body, status, now, JSON.stringify(metadata ?? {}));
4821
+ return { id, agentId, direction: "outbound", phoneNumber: normalized, body, status, createdAt: now, metadata };
4822
+ }
4823
+ /** Update SMS status and optional provider metadata */
4824
+ updateStatus(id, status, metadata) {
4825
+ if (metadata) {
4826
+ this.db.prepare("UPDATE sms_messages SET status = ?, metadata = ? WHERE id = ?").run(status, JSON.stringify(metadata), id);
4827
+ return;
4828
+ }
4606
4829
  this.db.prepare("UPDATE sms_messages SET status = ? WHERE id = ?").run(status, id);
4607
4830
  }
4608
4831
  /** List SMS messages for an agent */
@@ -4802,39 +5025,6 @@ var SmsPoller = class {
4802
5025
  };
4803
5026
 
4804
5027
  // src/gateway/manager.ts
4805
- function deriveKey(key, salt) {
4806
- return scryptSync(key, salt, 32, { N: 16384, r: 8, p: 1 });
4807
- }
4808
- function encryptSecret(plaintext, key) {
4809
- const salt = randomBytes2(16);
4810
- const derivedKey = deriveKey(key, salt);
4811
- const iv = randomBytes2(12);
4812
- const cipher = createCipheriv("aes-256-gcm", derivedKey, iv);
4813
- const encrypted = Buffer.concat([cipher.update(plaintext, "utf8"), cipher.final()]);
4814
- const authTag = cipher.getAuthTag();
4815
- return `enc2:${salt.toString("hex")}:${iv.toString("hex")}:${authTag.toString("hex")}:${encrypted.toString("hex")}`;
4816
- }
4817
- function decryptSecret(value, key) {
4818
- if (value.startsWith("enc2:")) {
4819
- const parts = value.split(":");
4820
- if (parts.length !== 5) return value;
4821
- const [, saltHex, ivHex, authTagHex, ciphertextHex] = parts;
4822
- const derivedKey = deriveKey(key, Buffer.from(saltHex, "hex"));
4823
- const decipher = createDecipheriv("aes-256-gcm", derivedKey, Buffer.from(ivHex, "hex"));
4824
- decipher.setAuthTag(Buffer.from(authTagHex, "hex"));
4825
- return Buffer.concat([decipher.update(Buffer.from(ciphertextHex, "hex")), decipher.final()]).toString("utf8");
4826
- }
4827
- if (value.startsWith("enc:")) {
4828
- const parts = value.split(":");
4829
- if (parts.length !== 4) return value;
4830
- const [, ivHex, authTagHex, ciphertextHex] = parts;
4831
- const keyHash = createHash("sha256").update(key).digest();
4832
- const decipher = createDecipheriv("aes-256-gcm", keyHash, Buffer.from(ivHex, "hex"));
4833
- decipher.setAuthTag(Buffer.from(authTagHex, "hex"));
4834
- return Buffer.concat([decipher.update(Buffer.from(ciphertextHex, "hex")), decipher.final()]).toString("utf8");
4835
- }
4836
- return value;
4837
- }
4838
5028
  var GatewayManager = class {
4839
5029
  constructor(options) {
4840
5030
  this.options = options;
@@ -6256,6 +6446,69 @@ function hostSessionStoragePath() {
6256
6446
  return storagePath();
6257
6447
  }
6258
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
+
6259
6512
  // src/util/safe-url.ts
6260
6513
  var UnsafeApiUrlError = class extends Error {
6261
6514
  constructor(raw, reason) {
@@ -7947,6 +8200,7 @@ export {
7947
8200
  AgentDeletionService,
7948
8201
  AgentMemoryStore,
7949
8202
  AgenticMailClient,
8203
+ BRIDGE_OPERATOR_LIVE_WINDOW_MS,
7950
8204
  CloudflareClient,
7951
8205
  DEFAULT_AGENT_NAME,
7952
8206
  DEFAULT_AGENT_ROLE,
@@ -7977,10 +8231,14 @@ export {
7977
8231
  UnsafeApiUrlError,
7978
8232
  WARNING_THRESHOLD,
7979
8233
  assertWithinBase,
8234
+ bridgeWakeErrorMessage,
8235
+ bridgeWakeLastSeenAgeMs,
7980
8236
  buildApiUrl,
7981
8237
  buildInboundSecurityAdvisory,
7982
8238
  classifyEmailRoute,
8239
+ classifyResumeError,
7983
8240
  closeDatabase,
8241
+ composeBridgeWakePrompt,
7984
8242
  createTestDatabase,
7985
8243
  debug,
7986
8244
  debugWarn,
@@ -7990,11 +8248,13 @@ export {
7990
8248
  forgetHostSession,
7991
8249
  getDatabase,
7992
8250
  getOperatorEmail,
8251
+ getSmsProvider,
7993
8252
  hostSessionStoragePath,
7994
8253
  isInternalEmail,
7995
8254
  isSessionFresh,
7996
8255
  isValidPhoneNumber,
7997
8256
  loadHostSession,
8257
+ mapProviderSmsStatus,
7998
8258
  normalizeAddress,
7999
8259
  normalizePhoneNumber,
8000
8260
  normalizeSubject,
@@ -8004,6 +8264,7 @@ export {
8004
8264
  recordToolCall,
8005
8265
  redactObject,
8006
8266
  redactSecret,
8267
+ redactSmsConfig,
8007
8268
  resolveConfig,
8008
8269
  safeJoin,
8009
8270
  sanitizeEmail,
@@ -8013,6 +8274,7 @@ export {
8013
8274
  scoreEmail,
8014
8275
  setOperatorEmail,
8015
8276
  setTelemetryVersion,
8277
+ shouldSkipBridgeWakeForLiveOperator,
8016
8278
  startRelayBridge,
8017
8279
  threadIdFor,
8018
8280
  tryJoin,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agenticmail/core",
3
- "version": "0.9.8",
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",