@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/README.md +10 -1
- package/dist/index.cjs +321 -25
- package/dist/index.d.cts +251 -1
- package/dist/index.d.ts +251 -1
- package/dist/index.js +331 -24
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1087,14 +1087,14 @@ var StalwartAdmin = class {
|
|
|
1087
1087
|
if (!isValidDomain(domain)) {
|
|
1088
1088
|
throw new Error(`Invalid domain format: "${domain}"`);
|
|
1089
1089
|
}
|
|
1090
|
-
const { readFileSync:
|
|
1091
|
-
const { homedir:
|
|
1092
|
-
const { join:
|
|
1093
|
-
const configPath =
|
|
1090
|
+
const { readFileSync: readFileSync7, writeFileSync: writeFileSync8 } = await import("fs");
|
|
1091
|
+
const { homedir: homedir11 } = await import("os");
|
|
1092
|
+
const { join: join12 } = await import("path");
|
|
1093
|
+
const configPath = join12(homedir11(), ".agenticmail", "stalwart.toml");
|
|
1094
1094
|
try {
|
|
1095
|
-
let config =
|
|
1095
|
+
let config = readFileSync7(configPath, "utf-8");
|
|
1096
1096
|
config = config.replace(/^hostname\s*=\s*"[^"]*"/m, `hostname = "${escapeTomlString(domain)}"`);
|
|
1097
|
-
|
|
1097
|
+
writeFileSync8(configPath, config);
|
|
1098
1098
|
console.log(`[Stalwart] Updated hostname to "${domain}" in stalwart.toml`);
|
|
1099
1099
|
} catch (err) {
|
|
1100
1100
|
throw new Error(`Failed to set config server.hostname=${domain}`);
|
|
@@ -1103,15 +1103,15 @@ var StalwartAdmin = class {
|
|
|
1103
1103
|
// --- DKIM ---
|
|
1104
1104
|
/** Path to the host-side stalwart.toml (mounted read-only into container) */
|
|
1105
1105
|
get configPath() {
|
|
1106
|
-
const { homedir:
|
|
1107
|
-
const { join:
|
|
1108
|
-
return
|
|
1106
|
+
const { homedir: homedir11 } = __require("os");
|
|
1107
|
+
const { join: join12 } = __require("path");
|
|
1108
|
+
return join12(homedir11(), ".agenticmail", "stalwart.toml");
|
|
1109
1109
|
}
|
|
1110
1110
|
/** Path to host-side DKIM key directory */
|
|
1111
1111
|
get dkimDir() {
|
|
1112
|
-
const { homedir:
|
|
1113
|
-
const { join:
|
|
1114
|
-
return
|
|
1112
|
+
const { homedir: homedir11 } = __require("os");
|
|
1113
|
+
const { join: join12 } = __require("path");
|
|
1114
|
+
return join12(homedir11(), ".agenticmail");
|
|
1115
1115
|
}
|
|
1116
1116
|
/**
|
|
1117
1117
|
* Create/reuse a DKIM signing key for a domain.
|
|
@@ -1212,12 +1212,12 @@ var StalwartAdmin = class {
|
|
|
1212
1212
|
* This bypasses the need for a PTR record on the sending IP.
|
|
1213
1213
|
*/
|
|
1214
1214
|
async configureOutboundRelay(config) {
|
|
1215
|
-
const { readFileSync:
|
|
1216
|
-
const { homedir:
|
|
1217
|
-
const { join:
|
|
1215
|
+
const { readFileSync: readFileSync7, writeFileSync: writeFileSync8 } = await import("fs");
|
|
1216
|
+
const { homedir: homedir11 } = await import("os");
|
|
1217
|
+
const { join: join12 } = await import("path");
|
|
1218
1218
|
const routeName = config.routeName ?? "gmail";
|
|
1219
|
-
const tomlPath =
|
|
1220
|
-
let toml =
|
|
1219
|
+
const tomlPath = join12(homedir11(), ".agenticmail", "stalwart.toml");
|
|
1220
|
+
let toml = readFileSync7(tomlPath, "utf-8");
|
|
1221
1221
|
toml = toml.replace(/\n\[queue\.route\.gmail\][\s\S]*?(?=\n\[|$)/, "");
|
|
1222
1222
|
toml = toml.replace(/\n\[queue\.strategy\][\s\S]*?(?=\n\[|$)/, "");
|
|
1223
1223
|
const safeRouteName = routeName.replace(/[^a-zA-Z0-9_-]/g, "");
|
|
@@ -1237,7 +1237,7 @@ auth.secret = "${escapeTomlString(config.password)}"
|
|
|
1237
1237
|
route = [ { if = "is_local_domain('', rcpt_domain)", then = "'local'" },
|
|
1238
1238
|
{ else = "'${safeRouteName}'" } ]
|
|
1239
1239
|
`;
|
|
1240
|
-
|
|
1240
|
+
writeFileSync8(tomlPath, toml, "utf-8");
|
|
1241
1241
|
await this.restartContainer();
|
|
1242
1242
|
}
|
|
1243
1243
|
};
|
|
@@ -2846,6 +2846,18 @@ CREATE INDEX IF NOT EXISTS idx_pending_notification ON pending_outbound(notifica
|
|
|
2846
2846
|
-- Column is optional; NULL means "no schema, accept anything" (the
|
|
2847
2847
|
-- v0.8.x behaviour, fully back-compat).
|
|
2848
2848
|
ALTER TABLE agent_tasks ADD COLUMN output_schema TEXT;
|
|
2849
|
+
`,
|
|
2850
|
+
"015_draft_attachments.sql": `
|
|
2851
|
+
-- Persist attachments alongside their draft.
|
|
2852
|
+
--
|
|
2853
|
+
-- Stored as a JSON array on the row: each entry is
|
|
2854
|
+
-- { filename, contentType, content (base64), size }. The web UI
|
|
2855
|
+
-- cap is 20 MB total per draft, which SQLite handles fine without
|
|
2856
|
+
-- bloating other queries \u2014 the column is only fetched on the
|
|
2857
|
+
-- per-draft GET (not on the list endpoint) so the Drafts sidebar
|
|
2858
|
+
-- stays snappy. NULL means "no attachments", fully back-compat
|
|
2859
|
+
-- with rows from before this migration.
|
|
2860
|
+
ALTER TABLE drafts ADD COLUMN attachments TEXT;
|
|
2849
2861
|
`
|
|
2850
2862
|
};
|
|
2851
2863
|
function runMigrations(database) {
|
|
@@ -5093,12 +5105,12 @@ var GatewayManager = class {
|
|
|
5093
5105
|
zone = await this.cfClient.createZone(domain);
|
|
5094
5106
|
}
|
|
5095
5107
|
const existingRecords = await this.cfClient.listDnsRecords(zone.id);
|
|
5096
|
-
const { homedir:
|
|
5097
|
-
const backupDir = join4(
|
|
5108
|
+
const { homedir: homedir11 } = await import("os");
|
|
5109
|
+
const backupDir = join4(homedir11(), ".agenticmail");
|
|
5098
5110
|
const backupPath = join4(backupDir, `dns-backup-${domain}-${Date.now()}.json`);
|
|
5099
|
-
const { writeFileSync:
|
|
5100
|
-
|
|
5101
|
-
|
|
5111
|
+
const { writeFileSync: writeFileSync8, mkdirSync: mkdirSync9 } = await import("fs");
|
|
5112
|
+
mkdirSync9(backupDir, { recursive: true });
|
|
5113
|
+
writeFileSync8(backupPath, JSON.stringify({
|
|
5102
5114
|
domain,
|
|
5103
5115
|
zoneId: zone.id,
|
|
5104
5116
|
backedUpAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -7280,10 +7292,301 @@ secret = "${password}"
|
|
|
7280
7292
|
return existsSync7(configPath);
|
|
7281
7293
|
}
|
|
7282
7294
|
};
|
|
7295
|
+
|
|
7296
|
+
// src/threading/thread-id.ts
|
|
7297
|
+
import { createHash as createHash2 } from "crypto";
|
|
7298
|
+
function stripReplyPrefixes(subject) {
|
|
7299
|
+
let s = subject;
|
|
7300
|
+
for (; ; ) {
|
|
7301
|
+
const next = s.replace(/^\s*(?:re|fwd?|fw)\s*(?:\[\d+\])?\s*:\s*/i, "");
|
|
7302
|
+
if (next === s) break;
|
|
7303
|
+
s = next;
|
|
7304
|
+
}
|
|
7305
|
+
return s;
|
|
7306
|
+
}
|
|
7307
|
+
function stripCoordinationMarkers(subject) {
|
|
7308
|
+
return subject.replace(/\[\s*(?:final|done|closed|wrap)\s*\]/gi, " ");
|
|
7309
|
+
}
|
|
7310
|
+
function normalizeSubject(subject) {
|
|
7311
|
+
if (!subject) return "(no subject)";
|
|
7312
|
+
let s = stripReplyPrefixes(subject);
|
|
7313
|
+
s = stripCoordinationMarkers(s);
|
|
7314
|
+
s = s.replace(/\s+/g, " ").trim().toLowerCase();
|
|
7315
|
+
return s || "(no subject)";
|
|
7316
|
+
}
|
|
7317
|
+
function normalizeAddress(addr) {
|
|
7318
|
+
if (!addr) return "(unknown)";
|
|
7319
|
+
const m = addr.match(/<([^>]+)>/);
|
|
7320
|
+
const raw = m ? m[1] : addr;
|
|
7321
|
+
return raw.trim().toLowerCase();
|
|
7322
|
+
}
|
|
7323
|
+
function threadIdFor(input) {
|
|
7324
|
+
const subject = normalizeSubject(input.subject);
|
|
7325
|
+
return createHash2("sha256").update(subject).digest("base64url").slice(0, 16);
|
|
7326
|
+
}
|
|
7327
|
+
|
|
7328
|
+
// src/threading/thread-cache.ts
|
|
7329
|
+
import {
|
|
7330
|
+
existsSync as existsSync8,
|
|
7331
|
+
mkdirSync as mkdirSync7,
|
|
7332
|
+
readFileSync as readFileSync5,
|
|
7333
|
+
writeFileSync as writeFileSync6,
|
|
7334
|
+
readdirSync,
|
|
7335
|
+
statSync,
|
|
7336
|
+
rmSync,
|
|
7337
|
+
renameSync
|
|
7338
|
+
} from "fs";
|
|
7339
|
+
import { homedir as homedir9 } from "os";
|
|
7340
|
+
import { join as join10 } from "path";
|
|
7341
|
+
var CACHE_DIR_DEFAULT = join10(homedir9(), ".agenticmail", "thread-cache");
|
|
7342
|
+
var DEFAULT_K_MESSAGES = 10;
|
|
7343
|
+
var DEFAULT_LRU_CAP = 5e3;
|
|
7344
|
+
var PREVIEW_MAX_CHARS = 240;
|
|
7345
|
+
var ThreadCache = class {
|
|
7346
|
+
dir;
|
|
7347
|
+
k;
|
|
7348
|
+
lruCap;
|
|
7349
|
+
constructor(opts = {}) {
|
|
7350
|
+
this.dir = opts.cacheDir ?? CACHE_DIR_DEFAULT;
|
|
7351
|
+
this.k = opts.k ?? DEFAULT_K_MESSAGES;
|
|
7352
|
+
this.lruCap = opts.lruCap ?? DEFAULT_LRU_CAP;
|
|
7353
|
+
try {
|
|
7354
|
+
mkdirSync7(this.dir, { recursive: true });
|
|
7355
|
+
} catch {
|
|
7356
|
+
}
|
|
7357
|
+
}
|
|
7358
|
+
pathFor(threadId) {
|
|
7359
|
+
return join10(this.dir, `${threadId}.json`);
|
|
7360
|
+
}
|
|
7361
|
+
read(threadId) {
|
|
7362
|
+
const p = this.pathFor(threadId);
|
|
7363
|
+
if (!existsSync8(p)) return null;
|
|
7364
|
+
try {
|
|
7365
|
+
const raw = readFileSync5(p, "utf-8");
|
|
7366
|
+
return JSON.parse(raw);
|
|
7367
|
+
} catch {
|
|
7368
|
+
try {
|
|
7369
|
+
rmSync(p, { force: true });
|
|
7370
|
+
} catch {
|
|
7371
|
+
}
|
|
7372
|
+
return null;
|
|
7373
|
+
}
|
|
7374
|
+
}
|
|
7375
|
+
/**
|
|
7376
|
+
* Append a message to the thread's cache, pruning to the K
|
|
7377
|
+
* newest entries. Creates the cache entry on first write.
|
|
7378
|
+
*
|
|
7379
|
+
* `rootFromAddr` is the sender of the ROOT message on the
|
|
7380
|
+
* thread; on a brand-new thread this is just `env.fromAddr`,
|
|
7381
|
+
* on a reply it's read off the existing cache entry (callers
|
|
7382
|
+
* should pass the existing entry's rootFromAddr when known).
|
|
7383
|
+
*/
|
|
7384
|
+
pushMessage(threadId, env, meta) {
|
|
7385
|
+
const existing = this.read(threadId);
|
|
7386
|
+
const entry = existing ? {
|
|
7387
|
+
...existing,
|
|
7388
|
+
// We re-affirm subject + rootFromAddr to existing values —
|
|
7389
|
+
// the first-seen values win. Reply messages carry the
|
|
7390
|
+
// replier's `from`, not the original sender's, so we'd
|
|
7391
|
+
// corrupt the thread root if we overwrote here.
|
|
7392
|
+
subject: existing.subject,
|
|
7393
|
+
rootFromAddr: existing.rootFromAddr,
|
|
7394
|
+
lastUpdated: Date.now(),
|
|
7395
|
+
messages: dedupAndCap([env, ...existing.messages], this.k)
|
|
7396
|
+
} : {
|
|
7397
|
+
threadId,
|
|
7398
|
+
subject: meta.subject,
|
|
7399
|
+
rootFromAddr: meta.rootFromAddr,
|
|
7400
|
+
lastUpdated: Date.now(),
|
|
7401
|
+
messages: [env]
|
|
7402
|
+
};
|
|
7403
|
+
this.writeAtomic(threadId, entry);
|
|
7404
|
+
this.maybeEvict();
|
|
7405
|
+
return entry;
|
|
7406
|
+
}
|
|
7407
|
+
/** Permanently remove a thread's cache (called on [FINAL] / [DONE] / [CLOSED] / [WRAP]). */
|
|
7408
|
+
delete(threadId) {
|
|
7409
|
+
try {
|
|
7410
|
+
rmSync(this.pathFor(threadId), { force: true });
|
|
7411
|
+
} catch {
|
|
7412
|
+
}
|
|
7413
|
+
}
|
|
7414
|
+
/**
|
|
7415
|
+
* Render the cache as a compact text block for the wake prompt.
|
|
7416
|
+
* One line per message, newest first. Empty string when the
|
|
7417
|
+
* cache is empty — caller decides whether to suppress the
|
|
7418
|
+
* header in that case.
|
|
7419
|
+
*/
|
|
7420
|
+
renderForPrompt(entry) {
|
|
7421
|
+
if (!entry || entry.messages.length === 0) return "";
|
|
7422
|
+
return entry.messages.map((m) => {
|
|
7423
|
+
const preview = m.preview.replace(/\s+/g, " ").slice(0, PREVIEW_MAX_CHARS);
|
|
7424
|
+
return `- UID ${m.uid} \xB7 ${m.from} \xB7 ${m.date} \xB7 "${m.subject}" \xB7 ${preview}`;
|
|
7425
|
+
}).join("\n");
|
|
7426
|
+
}
|
|
7427
|
+
writeAtomic(threadId, entry) {
|
|
7428
|
+
const p = this.pathFor(threadId);
|
|
7429
|
+
const tmp = `${p}.tmp`;
|
|
7430
|
+
writeFileSync6(tmp, JSON.stringify(entry), "utf-8");
|
|
7431
|
+
renameSync(tmp, p);
|
|
7432
|
+
}
|
|
7433
|
+
/**
|
|
7434
|
+
* Best-effort LRU eviction. Runs at most every 256 writes (we
|
|
7435
|
+
* don't track a precise counter — `Math.random()` sampling keeps
|
|
7436
|
+
* the write path cheap). When the directory has more files than
|
|
7437
|
+
* `lruCap`, sort by mtime ascending and delete the oldest 10%.
|
|
7438
|
+
*/
|
|
7439
|
+
maybeEvict() {
|
|
7440
|
+
if (Math.random() > 1 / 256) return;
|
|
7441
|
+
let files;
|
|
7442
|
+
try {
|
|
7443
|
+
files = readdirSync(this.dir).filter((f) => f.endsWith(".json"));
|
|
7444
|
+
} catch {
|
|
7445
|
+
return;
|
|
7446
|
+
}
|
|
7447
|
+
if (files.length <= this.lruCap) return;
|
|
7448
|
+
const stats = files.map((f) => {
|
|
7449
|
+
const p = join10(this.dir, f);
|
|
7450
|
+
try {
|
|
7451
|
+
return { p, mtime: statSync(p).mtimeMs };
|
|
7452
|
+
} catch {
|
|
7453
|
+
return { p, mtime: 0 };
|
|
7454
|
+
}
|
|
7455
|
+
});
|
|
7456
|
+
stats.sort((a, b) => a.mtime - b.mtime);
|
|
7457
|
+
const dropCount = Math.max(1, Math.floor(this.lruCap * 0.1));
|
|
7458
|
+
for (let i = 0; i < dropCount; i++) {
|
|
7459
|
+
try {
|
|
7460
|
+
rmSync(stats[i].p, { force: true });
|
|
7461
|
+
} catch {
|
|
7462
|
+
}
|
|
7463
|
+
}
|
|
7464
|
+
}
|
|
7465
|
+
};
|
|
7466
|
+
function dedupAndCap(messages, k) {
|
|
7467
|
+
const seen = /* @__PURE__ */ new Set();
|
|
7468
|
+
const out = [];
|
|
7469
|
+
for (const m of messages) {
|
|
7470
|
+
if (seen.has(m.uid)) continue;
|
|
7471
|
+
seen.add(m.uid);
|
|
7472
|
+
out.push(m);
|
|
7473
|
+
if (out.length >= k) break;
|
|
7474
|
+
}
|
|
7475
|
+
return out;
|
|
7476
|
+
}
|
|
7477
|
+
|
|
7478
|
+
// src/threading/agent-memory.ts
|
|
7479
|
+
import {
|
|
7480
|
+
existsSync as existsSync9,
|
|
7481
|
+
mkdirSync as mkdirSync8,
|
|
7482
|
+
readFileSync as readFileSync6,
|
|
7483
|
+
writeFileSync as writeFileSync7,
|
|
7484
|
+
rmSync as rmSync2,
|
|
7485
|
+
renameSync as renameSync2
|
|
7486
|
+
} from "fs";
|
|
7487
|
+
import { homedir as homedir10 } from "os";
|
|
7488
|
+
import { join as join11 } from "path";
|
|
7489
|
+
var MEMORY_DIR_DEFAULT = join11(homedir10(), ".agenticmail", "agent-memory");
|
|
7490
|
+
var AgentMemoryStore = class {
|
|
7491
|
+
dir;
|
|
7492
|
+
constructor(opts = {}) {
|
|
7493
|
+
this.dir = opts.memoryDir ?? MEMORY_DIR_DEFAULT;
|
|
7494
|
+
try {
|
|
7495
|
+
mkdirSync8(this.dir, { recursive: true });
|
|
7496
|
+
} catch {
|
|
7497
|
+
}
|
|
7498
|
+
}
|
|
7499
|
+
dirFor(agentId) {
|
|
7500
|
+
return join11(this.dir, sanitizeId(agentId));
|
|
7501
|
+
}
|
|
7502
|
+
pathFor(agentId, threadId) {
|
|
7503
|
+
return join11(this.dirFor(agentId), `${sanitizeId(threadId)}.md`);
|
|
7504
|
+
}
|
|
7505
|
+
read(agentId, threadId) {
|
|
7506
|
+
const p = this.pathFor(agentId, threadId);
|
|
7507
|
+
if (!existsSync9(p)) return null;
|
|
7508
|
+
try {
|
|
7509
|
+
const raw = readFileSync6(p, "utf-8");
|
|
7510
|
+
const parsed = parse(raw);
|
|
7511
|
+
return { ...parsed, raw };
|
|
7512
|
+
} catch {
|
|
7513
|
+
return null;
|
|
7514
|
+
}
|
|
7515
|
+
}
|
|
7516
|
+
write(agentId, threadId, fields) {
|
|
7517
|
+
const agentDir = this.dirFor(agentId);
|
|
7518
|
+
try {
|
|
7519
|
+
mkdirSync8(agentDir, { recursive: true });
|
|
7520
|
+
} catch {
|
|
7521
|
+
}
|
|
7522
|
+
const body = render({ ...fields, updatedAt: (/* @__PURE__ */ new Date()).toISOString() });
|
|
7523
|
+
const p = this.pathFor(agentId, threadId);
|
|
7524
|
+
const tmp = `${p}.tmp`;
|
|
7525
|
+
writeFileSync7(tmp, body, "utf-8");
|
|
7526
|
+
renameSync2(tmp, p);
|
|
7527
|
+
}
|
|
7528
|
+
delete(agentId, threadId) {
|
|
7529
|
+
try {
|
|
7530
|
+
rmSync2(this.pathFor(agentId, threadId), { force: true });
|
|
7531
|
+
} catch {
|
|
7532
|
+
}
|
|
7533
|
+
}
|
|
7534
|
+
/** Render an agent's memory for injection into a wake prompt.
|
|
7535
|
+
* Returns the raw markdown if present; empty string when there's
|
|
7536
|
+
* no prior memory (the caller decides whether to suppress the
|
|
7537
|
+
* whole "Your own memory" block). */
|
|
7538
|
+
renderForPrompt(memory) {
|
|
7539
|
+
if (!memory) return "";
|
|
7540
|
+
return memory.raw;
|
|
7541
|
+
}
|
|
7542
|
+
};
|
|
7543
|
+
function sanitizeId(id) {
|
|
7544
|
+
return id.replace(/[^a-zA-Z0-9._-]/g, "_");
|
|
7545
|
+
}
|
|
7546
|
+
function render(fields) {
|
|
7547
|
+
const fm = ["---"];
|
|
7548
|
+
fm.push(`updated_at: ${fields.updatedAt}`);
|
|
7549
|
+
if (typeof fields.lastUid === "number") fm.push(`last_uid: ${fields.lastUid}`);
|
|
7550
|
+
fm.push("---", "");
|
|
7551
|
+
const sections = [];
|
|
7552
|
+
if (fields.summary && fields.summary.trim()) {
|
|
7553
|
+
sections.push(fields.summary.trim());
|
|
7554
|
+
}
|
|
7555
|
+
if (fields.commitments && fields.commitments.length > 0) {
|
|
7556
|
+
sections.push(`### Commitments
|
|
7557
|
+
${fields.commitments.map((c) => `- ${c}`).join("\n")}`);
|
|
7558
|
+
}
|
|
7559
|
+
if (fields.openQuestions && fields.openQuestions.length > 0) {
|
|
7560
|
+
sections.push(`### Open
|
|
7561
|
+
${fields.openQuestions.map((q) => `- ${q}`).join("\n")}`);
|
|
7562
|
+
}
|
|
7563
|
+
if (fields.lastAction && fields.lastAction.trim()) {
|
|
7564
|
+
sections.push(`### Last action
|
|
7565
|
+
${fields.lastAction.trim()}`);
|
|
7566
|
+
}
|
|
7567
|
+
return fm.join("\n") + sections.join("\n\n") + "\n";
|
|
7568
|
+
}
|
|
7569
|
+
function parse(raw) {
|
|
7570
|
+
const out = {};
|
|
7571
|
+
const m = raw.match(/^---\n([\s\S]*?)\n---\n/);
|
|
7572
|
+
if (m) {
|
|
7573
|
+
for (const line of m[1].split("\n")) {
|
|
7574
|
+
const kv = line.match(/^(\w+):\s*(.*)$/);
|
|
7575
|
+
if (!kv) continue;
|
|
7576
|
+
if (kv[1] === "updated_at") out.updatedAt = kv[2].trim();
|
|
7577
|
+
else if (kv[1] === "last_uid") {
|
|
7578
|
+
const n = parseInt(kv[2], 10);
|
|
7579
|
+
if (!isNaN(n)) out.lastUid = n;
|
|
7580
|
+
}
|
|
7581
|
+
}
|
|
7582
|
+
}
|
|
7583
|
+
return out;
|
|
7584
|
+
}
|
|
7283
7585
|
export {
|
|
7284
7586
|
AGENT_ROLES,
|
|
7285
7587
|
AccountManager,
|
|
7286
7588
|
AgentDeletionService,
|
|
7589
|
+
AgentMemoryStore,
|
|
7287
7590
|
AgenticMailClient,
|
|
7288
7591
|
CloudflareClient,
|
|
7289
7592
|
DEFAULT_AGENT_NAME,
|
|
@@ -7307,6 +7610,7 @@ export {
|
|
|
7307
7610
|
SmsManager,
|
|
7308
7611
|
SmsPoller,
|
|
7309
7612
|
StalwartAdmin,
|
|
7613
|
+
ThreadCache,
|
|
7310
7614
|
TunnelManager,
|
|
7311
7615
|
WARNING_THRESHOLD,
|
|
7312
7616
|
buildInboundSecurityAdvisory,
|
|
@@ -7321,7 +7625,9 @@ export {
|
|
|
7321
7625
|
getDatabase,
|
|
7322
7626
|
isInternalEmail,
|
|
7323
7627
|
isValidPhoneNumber,
|
|
7628
|
+
normalizeAddress,
|
|
7324
7629
|
normalizePhoneNumber,
|
|
7630
|
+
normalizeSubject,
|
|
7325
7631
|
parseEmail,
|
|
7326
7632
|
parseGoogleVoiceSms,
|
|
7327
7633
|
recordToolCall,
|
|
@@ -7331,5 +7637,6 @@ export {
|
|
|
7331
7637
|
scanOutboundEmail,
|
|
7332
7638
|
scoreEmail,
|
|
7333
7639
|
setTelemetryVersion,
|
|
7334
|
-
startRelayBridge
|
|
7640
|
+
startRelayBridge,
|
|
7641
|
+
threadIdFor
|
|
7335
7642
|
};
|